基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# 箱号内部ID 真隔离重构方案
|
||||
|
||||
## Context(为什么改)
|
||||
|
||||
**用户问题**:从方案库往报价页面重复插入"同一种箱号和图号"时,多个箱号实例会共用同一张元件表,元件行混在一起无法区分(串数据)。需求是"完全隔离不要串数据"。
|
||||
|
||||
**根因(已逐行核对代码确认)**:
|
||||
|
||||
1. [主窗口.菜单与库.cs:4066](file:///c:/Users/099978/Documents/SharpDevelop%20Projects/成套报价软件/成套报价软件/主窗口.菜单与库.cs#L4066) `renamedRow["内部ID"] = actualXhName` — 内部ID 直接赋值为重命名后的箱号显示名,毫无隔离作用
|
||||
2. [主窗口.菜单与库.cs:4073](file:///c:/Users/099978/Documents/SharpDevelop%20Projects/成套报价软件/成套报价软件/主窗口.菜单与库.cs#L4073) `LookupTableByIndex("元件", targetTuhao, actualXhName, dstConn)` 命中已有元件表时复用,元件追加到旧表 → **串数据入口**
|
||||
3. [扒图\Pdf\Form3.cs:4171](file:///c:/Users/099978/Documents/SharpDevelop%20Projects/成套报价软件/成套报价软件/扒图/Pdf/Form3.cs#L4171) `cmd.Parameters.AddWithValue("@nid", xianghao)` — 扒图代码同样把内部ID 写成箱号显示名(注释"主窗口同款约定")
|
||||
4. [扒图\Pdf\Form3.cs:4193](file:///c:/Users/099978/Documents/SharpDevelop%20Projects/成套报价软件/成套报价软件/扒图/Pdf/Form3.cs#L4193) 注释"双键(2026-09-21 残留根治)"显示之前已有人尝试用"双键删除"治标,但未根治
|
||||
|
||||
**目标**:内部ID 改用 GUID 真隔离,箱号显示名只用于显示,所有读写元件表/箱号表的代码统一用内部ID。新插入永不复用旧元件表。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **内部ID =** **`Guid.NewGuid().ToString("N")`**(32 位无连字符,跨库复制安全,全局唯一不撞)
|
||||
2. **箱号表 + 图号表**新增 `内部ID TEXT` 列(扒图代码已有;主窗口代码已有图号表的,箱号表需确认)
|
||||
3. **元件表索引的"箱号"字段**统一存内部ID(GUID)
|
||||
4. **方案库插入时永远创建新元件表**,永不复用(即使 GUID 撞了也只是空表,安全)
|
||||
5. **向后兼容**:老数据无"内部ID"列 → EnsureColumns 启动时补列;已有行内部ID 为空时**兜底为箱号显示名**(保证旧数据可用,新插入不再产生串数据)
|
||||
6. **扒图代码双键删除逻辑保留**:老数据按显示名登记、新数据按 GUID 登记,删除时两种键都查(防孤儿)
|
||||
|
||||
## 实施阶段
|
||||
|
||||
### 阶段 1:方案库→项目库路径(用户场景精确入口)
|
||||
|
||||
**文件**:`成套报价软件\主窗口.菜单与库.cs`
|
||||
|
||||
* **`FakXianghao_Insert_Click`** **L3888-L4184**(方案→报价:箱号右键插入)
|
||||
|
||||
* L4037-4056 箱号显示名重命名(保留,避免 UI 显示重名)
|
||||
|
||||
* L4066 改:`renamedRow["内部ID"] = Guid.NewGuid().ToString("N")` (新 GUID,**不再等于 actualXhName**)
|
||||
|
||||
* L4073-4089 改:**删除复用判断**,无条件 `CreateFakYuanjianTable` + 新登记表索引(箱号字段写新 GUID)
|
||||
|
||||
* L4085 改:元件表索引的"箱号"参数从 `actualXhName` 改为新 GUID
|
||||
|
||||
* **`FakTuhao_Insert_Click`** **L3442-L3660**(方案→报价:图号右键插入)
|
||||
|
||||
* L3596 改:图号内部ID = 新 GUID(替换 `actualTh`)
|
||||
|
||||
* L3621 改:`r["内部ID"] = Guid.NewGuid().ToString("N")` — 每条箱号行重新生成 GUID,**不复用方案库原内部ID**
|
||||
|
||||
* L3657 改:元件表索引"箱号"参数从 `xhName` 改为对应箱号的新 GUID
|
||||
|
||||
* **`FakRead内部ID`** **L5187**:保留 fallback 到"箱号"列(旧数据兜底);新数据走 GUID
|
||||
|
||||
* **`EnsureColumns`** **调用**:L4034、L3180、L1130 在所有箱号表 EnsureColumns 列表里追加 `"内部ID"`(幂等补列)
|
||||
|
||||
### 阶段 2:扒图路径(双窗口同步改)
|
||||
|
||||
**文件**:`成套报价软件\扒图\Pdf\Form3.cs` 和 `成套报价软件\扒图\Cad\Form3.cs`(结构对称,同样改法;行号以 Pdf 版为准)
|
||||
|
||||
* **`CreateXianghaoTable`** **L1368**:CREATE TABLE 已含"内部ID"列 ✓ 不动;**`EnsureXianghaoTableExists`** **L1110** 同步确认 EnsureColumns 含"内部ID"
|
||||
|
||||
* **`InsertXianghao`** **L4139-L4181**:
|
||||
|
||||
* L4171 改:`cmd.Parameters.AddWithValue("@nid", Guid.NewGuid().ToString("N"))` (新 GUID,删除"内部ID=创建时的箱号名"注释)
|
||||
|
||||
* 方法签名增加 `out string newInternalId` 或返回 GUID,让调用方 `PaXianghaoInsert` 拿到新 GUID
|
||||
|
||||
* **`PaXianghaoInsert`** **L1872-L1960**:
|
||||
|
||||
* L1912 `lastInsertedXianghao` 改为接收 InsertXianghao 返回的 GUID
|
||||
|
||||
* L1954 `_currentXianghao = lastInsertedXianghao`(现在存 GUID)
|
||||
|
||||
* L1955 `SelectRowByValue(..., "内部ID", GUID)` — 列名从"箱号"改"内部ID"
|
||||
|
||||
* L1957 `ReloadYuanjianByXianghao` 传 GUID
|
||||
|
||||
* **`InsertTuhao`** **L4046**(图号插入):L4068 `@nid` 改新 GUID
|
||||
|
||||
* **`GetXianghaoFrameHandle L2437`** **/** **`GetXianghaoFrameHandleByTextHandle L2395`** **/** **`XianghaoExists L2386`**:参数语义改为内部ID,WHERE 改 `内部ID=@nid`;`GetXianghaoFrameHandleByTextHandle` 保留按"文本Handle"查的能力(同文本实体去重)
|
||||
|
||||
* **`DeleteXianghao`** **L4183**:双键删除逻辑保留(防孤儿),新增优先按内部ID 删
|
||||
|
||||
* **`ReloadYuanjianByXianghao`** 和 **`ReloadXianghaoByTuhao`**:内部按内部ID 查元件表索引;`ReloadXianghaoByTuhao` 加载后用第一行"内部ID"列设 `_currentXianghao`
|
||||
|
||||
* **`advancedDataGridView4_SelectionChanged`** **L5433**:L5439 改读"内部ID"列设置 `_currentXianghao`
|
||||
|
||||
* **`RecalcAllFromDatabase`** **L4991**:遍历箱号行用内部ID 作键查元件表
|
||||
|
||||
* **`CollectFrameHandlesForXianghao`** **L5674**:WHERE 改 `内部ID=@nid`;L5705 `GetYuanjianTableByIndex` 传内部ID
|
||||
|
||||
### 阶段 3:主窗口读取端 + AI 接口
|
||||
|
||||
**文件**:`成套报价软件\主窗口.cs` 和 `成套报价软件\AI助手接口.cs`
|
||||
|
||||
* **`主窗口.cs LoadSelectionDetail`** **L859-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)幂等执行:
|
||||
|
||||
1. **图号表**:`EnsureColumns(conn, TuhaoTableName, {"内部ID"})`(已有)
|
||||
2. **所有现存箱号表**:遍历 `表索引 WHERE 类型='箱号'`,对每张实际表 `EnsureColumns(..., {"内部ID"})`
|
||||
3. **回填老数据**:对每张箱号表 `UPDATE "<表>" SET 内部ID=箱号 WHERE 内部ID IS NULL OR 内部ID=''`;图号表 `UPDATE "<图号表>" SET 内部ID=图号 WHERE 内部ID IS NULL OR 内部ID=''`
|
||||
4. **表索引回填**(可选):把元件索引的"箱号"字段从显示名升级为内部ID(不回填也不影响新逻辑,因新逻辑按 GUID 查;保留显示名作 fallback)
|
||||
5. **GUID 不强求回填老数据**:老数据用显示名做内部ID 不会撞(除非同名重复,但同名重复本身就是历史 BUG 数据,新插入不再产生)
|
||||
|
||||
## 验证方法
|
||||
|
||||
1. **复现测试**:方案库 A 图号下"X1 箱号"(含 5 条元件)插入到报价项目"图号 T1"两次
|
||||
|
||||
* 期望:两个箱号行(显示名 X1、X1\_2),各自独立元件表,每张 5 条元件,互不污染
|
||||
2. **数据库直查**:`SELECT 箱号, 内部ID FROM "xh_1"` — 两行的内部ID 应为不同的 32 位 GUID
|
||||
3. **扒图回归**:扒一个新箱号 → `_currentXianghao` 应为 GUID;再扒同名箱号(同一文本实体)→ 应触发"重复删除"路径而非新增
|
||||
4. **选型页**:选型明细按元件名称+规格匹配,"行ID"按 rowid 锁定单条物理行(已实现),不会因内部ID 变更而误并
|
||||
5. **删箱号**:删除箱号 → 元件表 DROP + 索引行 DELETE;无孤儿表残留
|
||||
6. **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 兼容性
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
1. **跨库复制的 GUID 唯一性**:方案库→项目库复制时**新生成 GUID**(不复用方案库原内部ID),避免方案库被多次插入到同一项目库时 GUID 撞车(虽然 GUID 撞概率极低,但新生成更安全)
|
||||
2. **扒图双键删除保留**:L4193 的双键删除逻辑(显示名 + 内部ID)保留,防老数据孤儿;新数据天然走内部ID 单键
|
||||
3. **AI 接口兼容**:保留"双键兼容"读取逻辑,旧数据库升级后立即生效,无需强制迁移
|
||||
4. **`_currentXianghao`** **语义变更**:从"箱号显示名"改为"内部ID GUID"。所有比较相等的地方(如 `if (xh == _currentXianghao)`)需审查是否仍按显示名比较;建议增加 `_currentXianghaoName` 字段存显示名,仅用于 UI 显示
|
||||
|
||||
Reference in New Issue
Block a user