112 lines
5.1 KiB
Markdown
112 lines
5.1 KiB
Markdown
## Context
|
||
|
||
当前设备导入链路分为两段:
|
||
|
||
1. `pkg/utils/excel.go` 负责按固定列读取设备导入 Excel。
|
||
2. `internal/task/device_import.go` 负责校验卡并创建 `tb_device_sim_binding` 记录。
|
||
|
||
现状问题在于解析层只保留“非空 ICCID 列表”,没有保留“这个 ICCID 原来来自第几个卡槽列”。任务层随后按数组顺序写入 `slot_position = i + 1`,使得源文件中的空槽位被压缩。这个问题不涉及新接口,也不需要调整数据库结构,但会跨越解析层与任务执行层,适合先通过设计文档把数据语义钉死。
|
||
|
||
约束条件:
|
||
|
||
- 必须遵守现有 `Handler → Service → Store → Model` 分层,不把槽位推断逻辑散落到多处。
|
||
- 不新增表结构,不引入新依赖。
|
||
- 设备导入仍走 Asynq 异步任务,保持当前事务边界和幂等行为。
|
||
- 文档、日志、错误消息保持中文。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- 保留 Excel `卡1~卡4` 的原始槽位信息,避免前置空槽被压缩。
|
||
- 让导入创建的 `slot_position` 与 Excel 列号严格一致。
|
||
- 对 `max_sim_slots` 之外的已填写 ICCID 给出明确失败规则。
|
||
- 让后续查询设备绑定关系、切卡和运维判断都基于正确槽位数据。
|
||
|
||
**Non-Goals:**
|
||
|
||
- 不新增或修改导入 API。
|
||
- 不修复历史上已经错误导入的数据。
|
||
- 不改变设备导入任务的权限、批次、实名策略等既有能力。
|
||
- 不引入自动化测试要求,本次仅定义实现任务和手工验收路径。
|
||
|
||
## Decisions
|
||
|
||
### 决策1:在解析结果中保留“槽位 + ICCID”映射,而不是仅保留 ICCID 列表
|
||
|
||
**选择**:
|
||
设备导入解析结构需要显式表达每张卡来自哪个槽位,例如保留 `slot_position` 与 `iccid` 的成对关系,或者使用等价的固定槽位结构。
|
||
|
||
**理由**:
|
||
|
||
- 只有解析层知道 ICCID 来自哪一列,到了任务层再推断已经丢失上下文。
|
||
- 把槽位语义前置到解析结果,可以让任务层只负责校验和落库,职责更清晰。
|
||
|
||
**备选方案**:
|
||
|
||
- 在任务层根据 ICCID 顺序反推槽位:不可行,前置空槽信息已丢失。
|
||
- 用占位空字符串继续传 `[]string`:可读性差,容易在后续处理中再次被压缩或忽略。
|
||
|
||
### 决策2:绑定记录的 `slot_position` 直接使用源列位,不再使用连续下标
|
||
|
||
**选择**:
|
||
创建设备卡绑定时,`slot_position` 必须直接使用解析出的槽位编号,而不是 `i + 1`。
|
||
|
||
**理由**:
|
||
|
||
- `tb_device_sim_binding.slot_position` 本来就是槽位语义字段,应该存真实槽位。
|
||
- 这样可以保持导入、查询、切卡、展示四个环节的语义一致。
|
||
|
||
**备选方案**:
|
||
|
||
- 继续写连续下标并在展示层修正:会污染底层数据,后续所有依赖槽位的逻辑都要补丁式兼容,不可接受。
|
||
|
||
### 决策3:填写超过 `max_sim_slots` 的 ICCID 时整行失败
|
||
|
||
**选择**:
|
||
如果 Excel 某行声明 `max_sim_slots=2`,却填写了 `卡3` 或 `卡4`,该行导入失败,并给出明确失败原因。
|
||
|
||
**理由**:
|
||
|
||
- 超出设备最大槽位的绑定关系本身就是无效数据,不能静默忽略。
|
||
- 失败比“部分导入 + 隐式丢弃”更容易让运营发现模板问题。
|
||
|
||
**备选方案**:
|
||
|
||
- 静默忽略越界槽位:会造成运营以为导入成功,但设备绑定不完整。
|
||
- 自动上调 `max_sim_slots`:篡改源数据含义,不符合设备真实硬件能力。
|
||
|
||
### 决策4:沿用现有事务边界,不扩大导入任务架构
|
||
|
||
**选择**:
|
||
仍在当前单行事务中完成设备创建、资产标识注册、卡绑定创建、卡快照更新和钱包创建,只替换槽位数据来源。
|
||
|
||
**理由**:
|
||
|
||
- 问题根因是数据语义错误,不是事务模型错误。
|
||
- 保持现有事务边界可以减少改动范围和回归风险。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- [风险] 解析结构变更会波及设备导入任务中遍历 ICCID 的逻辑
|
||
→ Mitigation:只在设备导入专用结构中调整,不影响卡导入等其他解析路径。
|
||
|
||
- [风险] 历史错误导入的数据仍然存在,修复后新旧数据表现不一致
|
||
→ Mitigation:在提案中明确本次仅修复“后续导入行为”,历史数据如需修复需单独立项。
|
||
|
||
- [风险] 运营模板可能继续填写越界槽位,修复后失败数会上升
|
||
→ Mitigation:在失败原因中明确指出“填写槽位超出最大SIM槽数”,并更新导入使用说明。
|
||
|
||
## Migration Plan
|
||
|
||
1. 先更新 `device-import` 规格,明确“列位即槽位”。
|
||
2. 调整设备导入解析结构与任务执行逻辑。
|
||
3. 使用手工构造的 Excel 样例做导入验收,覆盖空槽保留和越界失败两类场景。
|
||
4. 上线后仅影响新导入任务,无需数据迁移。
|
||
5. 如需回滚,恢复解析和绑定逻辑即可;本次不涉及表结构变更。
|
||
|
||
## Open Questions
|
||
|
||
- 历史错误导入的数据是否需要补录或脚本修正;如果需要,应拆成独立变更处理。
|
||
- 前端模板文案是否要把 `iccid_1 ~ iccid_4` 明确标注为“槽1 ~ 槽4”;这会影响使用体验,但不阻塞后端修复。
|