## 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”；这会影响使用体验，但不阻塞后端修复。
