9.6 KiB
箱号内部ID 真隔离重构方案
Context(为什么改)
用户问题:从方案库往报价页面重复插入"同一种箱号和图号"时,多个箱号实例会共用同一张元件表,元件行混在一起无法区分(串数据)。需求是"完全隔离不要串数据"。
根因(已逐行核对代码确认):
- 主窗口.菜单与库.cs:4066
renamedRow["内部ID"] = actualXhName— 内部ID 直接赋值为重命名后的箱号显示名,毫无隔离作用 - 主窗口.菜单与库.cs:4073
LookupTableByIndex("元件", targetTuhao, actualXhName, dstConn)命中已有元件表时复用,元件追加到旧表 → 串数据入口 - 扒图\Pdf\Form3.cs:4171
cmd.Parameters.AddWithValue("@nid", xianghao)— 扒图代码同样把内部ID 写成箱号显示名(注释"主窗口同款约定") - 扒图\Pdf\Form3.cs:4193 注释"双键(2026-09-21 残留根治)"显示之前已有人尝试用"双键删除"治标,但未根治
目标:内部ID 改用 GUID 真隔离,箱号显示名只用于显示,所有读写元件表/箱号表的代码统一用内部ID。新插入永不复用旧元件表。
设计原则
- 内部ID =
Guid.NewGuid().ToString("N")(32 位无连字符,跨库复制安全,全局唯一不撞) - 箱号表 + 图号表新增
内部ID TEXT列(扒图代码已有;主窗口代码已有图号表的,箱号表需确认) - 元件表索引的"箱号"字段统一存内部ID(GUID)
- 方案库插入时永远创建新元件表,永不复用(即使 GUID 撞了也只是空表,安全)
- 向后兼容:老数据无"内部ID"列 → EnsureColumns 启动时补列;已有行内部ID 为空时兜底为箱号显示名(保证旧数据可用,新插入不再产生串数据)
- 扒图代码双键删除逻辑保留:老数据按显示名登记、新数据按 GUID 登记,删除时两种键都查(防孤儿)
实施阶段
阶段 1:方案库→项目库路径(用户场景精确入口)
文件:成套报价软件\主窗口.菜单与库.cs
-
FakXianghao_Insert_ClickL3888-L4184(方案→报价:箱号右键插入)-
L4037-4056 箱号显示名重命名(保留,避免 UI 显示重名)
-
L4066 改:
renamedRow["内部ID"] = Guid.NewGuid().ToString("N")(新 GUID,不再等于 actualXhName) -
L4073-4089 改:删除复用判断,无条件
CreateFakYuanjianTable+ 新登记表索引(箱号字段写新 GUID) -
L4085 改:元件表索引的"箱号"参数从
actualXhName改为新 GUID
-
-
FakTuhao_Insert_ClickL3442-L3660(方案→报价:图号右键插入)-
L3596 改:图号内部ID = 新 GUID(替换
actualTh) -
L3621 改:
r["内部ID"] = Guid.NewGuid().ToString("N")— 每条箱号行重新生成 GUID,不复用方案库原内部ID -
L3657 改:元件表索引"箱号"参数从
xhName改为对应箱号的新 GUID
-
-
FakRead内部IDL5187:保留 fallback 到"箱号"列(旧数据兜底);新数据走 GUID -
EnsureColumns调用:L4034、L3180、L1130 在所有箱号表 EnsureColumns 列表里追加"内部ID"(幂等补列)
阶段 2:扒图路径(双窗口同步改)
文件:成套报价软件\扒图\Pdf\Form3.cs 和 成套报价软件\扒图\Cad\Form3.cs(结构对称,同样改法;行号以 Pdf 版为准)
-
CreateXianghaoTableL1368:CREATE TABLE 已含"内部ID"列 ✓ 不动;EnsureXianghaoTableExistsL1110 同步确认 EnsureColumns 含"内部ID" -
InsertXianghaoL4139-L4181:-
L4171 改:
cmd.Parameters.AddWithValue("@nid", Guid.NewGuid().ToString("N"))(新 GUID,删除"内部ID=创建时的箱号名"注释) -
方法签名增加
out string newInternalId或返回 GUID,让调用方PaXianghaoInsert拿到新 GUID
-
-
PaXianghaoInsertL1872-L1960:-
L1912
lastInsertedXianghao改为接收 InsertXianghao 返回的 GUID -
L1954
_currentXianghao = lastInsertedXianghao(现在存 GUID) -
L1955
SelectRowByValue(..., "内部ID", GUID)— 列名从"箱号"改"内部ID" -
L1957
ReloadYuanjianByXianghao传 GUID
-
-
InsertTuhaoL4046(图号插入):L4068@nid改新 GUID -
GetXianghaoFrameHandle L2437/GetXianghaoFrameHandleByTextHandle L2395/XianghaoExists L2386:参数语义改为内部ID,WHERE 改内部ID=@nid;GetXianghaoFrameHandleByTextHandle保留按"文本Handle"查的能力(同文本实体去重) -
DeleteXianghaoL4183:双键删除逻辑保留(防孤儿),新增优先按内部ID 删 -
ReloadYuanjianByXianghao和ReloadXianghaoByTuhao:内部按内部ID 查元件表索引;ReloadXianghaoByTuhao加载后用第一行"内部ID"列设_currentXianghao -
advancedDataGridView4_SelectionChangedL5433:L5439 改读"内部ID"列设置_currentXianghao -
RecalcAllFromDatabaseL4991:遍历箱号行用内部ID 作键查元件表 -
CollectFrameHandlesForXianghaoL5674:WHERE 改内部ID=@nid;L5705GetYuanjianTableByIndex传内部ID
阶段 3:主窗口读取端 + AI 接口
文件:成套报价软件\主窗口.cs 和 成套报价软件\AI助手接口.cs
-
主窗口.cs LoadSelectionDetailL859-L1036:-
L921 元件索引 SQL 天然按内部ID 查(阶段 1 改后元件索引"箱号"字段已为 GUID)✓
-
L993
取箱号图纸(conn, th, xh, ...)中 xh 现在是内部ID,需对应改取箱号图纸内部 SQL
-
-
主窗口.cs 取箱号图纸L1041 附近:WHERE 改内部ID=@nid,参数语义对应 -
AI助手接口.cs L252-320:保留"双键兼容"逻辑(旧数据兜底),优先内部ID;新数据全部走 GUID 后显示名分支逐步成为空集 -
主窗口.菜单与库.cs SelectRowByValue 调用L4165/L4167:已用"内部ID"列 ✓ 不动
数据库迁移策略
在 EnsureQuoteDbExists 入口(主窗口.菜单与库.cs L399、主窗口.cs L2787 附近的 .source)幂等执行:
- 图号表:
EnsureColumns(conn, TuhaoTableName, {"内部ID"})(已有) - 所有现存箱号表:遍历
表索引 WHERE 类型='箱号',对每张实际表EnsureColumns(..., {"内部ID"}) - 回填老数据:对每张箱号表
UPDATE "<表>" SET 内部ID=箱号 WHERE 内部ID IS NULL OR 内部ID='';图号表UPDATE "<图号表>" SET 内部ID=图号 WHERE 内部ID IS NULL OR 内部ID='' - 表索引回填(可选):把元件索引的"箱号"字段从显示名升级为内部ID(不回填也不影响新逻辑,因新逻辑按 GUID 查;保留显示名作 fallback)
- GUID 不强求回填老数据:老数据用显示名做内部ID 不会撞(除非同名重复,但同名重复本身就是历史 BUG 数据,新插入不再产生)
验证方法
-
复现测试:方案库 A 图号下"X1 箱号"(含 5 条元件)插入到报价项目"图号 T1"两次
- 期望:两个箱号行(显示名 X1、X1_2),各自独立元件表,每张 5 条元件,互不污染
-
数据库直查:
SELECT 箱号, 内部ID FROM "xh_1"— 两行的内部ID 应为不同的 32 位 GUID -
扒图回归:扒一个新箱号 →
_currentXianghao应为 GUID;再扒同名箱号(同一文本实体)→ 应触发"重复删除"路径而非新增 -
选型页:选型明细按元件名称+规格匹配,"行ID"按 rowid 锁定单条物理行(已实现),不会因内部ID 变更而误并
-
删箱号:删除箱号 → 元件表 DROP + 索引行 DELETE;无孤儿表残留
-
AI 接口:调用
/api/xuanxing确认活元件表列表正确排除孤儿
编译与合入策略
按项目记忆约定编译:taskkill //F //IM 成套报价软件.exe 后 csc.exe "@../tools/verify_build.rsp"(PowerShell 引号包响应文件),零输出为通过。
阶段 1+2+3 建议一次性合入,避免中间态 GUID/显示名混用导致联调错乱。
关键文件清单
-
成套报价软件\主窗口.菜单与库.cs— 阶段 1 核心 -
成套报价软件\扒图\Pdf\Form3.cs— 阶段 2 -
成套报价软件\扒图\Cad\Form3.cs— 阶段 2(与 Pdf 版对称) -
成套报价软件\主窗口.cs— 阶段 3 读取端 -
成套报价软件\AI助手接口.cs— 阶段 3 兼容性
风险与注意事项
- 跨库复制的 GUID 唯一性:方案库→项目库复制时新生成 GUID(不复用方案库原内部ID),避免方案库被多次插入到同一项目库时 GUID 撞车(虽然 GUID 撞概率极低,但新生成更安全)
- 扒图双键删除保留:L4193 的双键删除逻辑(显示名 + 内部ID)保留,防老数据孤儿;新数据天然走内部ID 单键
- AI 接口兼容:保留"双键兼容"读取逻辑,旧数据库升级后立即生效,无需强制迁移
_currentXianghao语义变更:从"箱号显示名"改为"内部ID GUID"。所有比较相等的地方(如if (xh == _currentXianghao))需审查是否仍按显示名比较;建议增加_currentXianghaoName字段存显示名,仅用于 UI 显示