All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 14m42s
- 设备资产解析外层 real_name_status:存在当前使用卡时沿用该卡状态,无任何当前卡时任一有效绑定卡已实名即视为已实名 - 手工绑定与设备导入:设备最大槽位为 1 且有效绑定唯一时,在同一事务内将该绑定初始化为当前使用卡;多卡设备不推断 - 同步 asset-device、personal-customer 主 Spec;归档变更并附存量核对清单(75 条单槽唯一待修复绑定,其中 16 条已实名)
78 lines
5.5 KiB
Markdown
78 lines
5.5 KiB
Markdown
## Context
|
||
|
||
- 设备资产解析在 `internal/service/asset` 内一次组装绑定卡、外层实名与套餐摘要;C 端资产信息 DTO 只暴露 `real_name_status` 一个外层实名信号(不含 `real_name_at`),逐卡实名由卡列表返回。
|
||
- `is_current` 目前只在运营商设备观测、显式切卡、解绑与删设备路径写入;导入与手工绑定创建时不写,DB 默认 false。
|
||
- 设备在运营商侧的识别标识为 IMEI 优先、空则用 SN,读取侧(资产信息实时状态)与观测侧(卡观测序列)口径一致。
|
||
- 生产只读核对:有有效绑定的设备 19031 台,其中 639 台无任何当前卡标识;这 639 台里 76 台存在已实名绑定卡(16 台为单槽单卡,其余为多卡设备)。
|
||
- 既有约束:自动化测试当前为 N/A(用户决策);生产写入由维护者按生产运行说明执行,Agent 只做只读诊断。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- 让「设备没有任何当前卡标识」这一分支不再把已实名设备展示为未实名。
|
||
- 让单槽单卡设备在绑定后即具备当前卡标识,消除「当前卡」展示为空的产生条件。
|
||
- 给存量同类数据一个可复核、可回滚的核对与修复口径。
|
||
|
||
**Non-Goals:**
|
||
|
||
- 不改变存在当前卡标识时的判定结果。
|
||
- 不改变管理端设备列表既有的「任一有效绑定卡已实名」筛选,以及设备激活状态判定。
|
||
- 不为多卡设备推断当前卡,不改变运营商观测/切卡对 `is_current` 的写入职责。
|
||
- 不处理运营商侧设备登记(如 Gateway 返回设备号无效)的修复。
|
||
- 不引入 Schema 变更、迁移、一次性常驻命令或自动化测试。
|
||
|
||
## Decisions
|
||
|
||
### 1. 回退判定放在读取侧,不改写业务事实
|
||
|
||
无当前卡时,外层实名按有效绑定卡的实名状态取或(任一已实名即已实名),实现位置为设备资产解析中的外层实名判定。
|
||
|
||
- 理由:读取侧回退只影响展示,不把「哪张卡在用」变成猜测事实;多卡设备无当前卡写侧无法推断,回填会污染 `is_current` 语义。
|
||
- 备选:在所有情况下都改为「任一有效绑定卡已实名」(否决,等于回退既有当前卡语义);在读取时按最大槽位推断当前卡(否决,会让 `is_current` 出现第二套判定来源)。
|
||
|
||
### 2. 保留「当前卡优先」分支,仅补分支缺口
|
||
|
||
存在 `is_current=true` 的有效绑定时沿用该卡实名状态;仅在一条当前卡标识都没有时进入回退。如此本 change 不改变有当前卡设备的既有返回。
|
||
|
||
### 3. 绑定创建时初始化单槽单卡的当前卡标识
|
||
|
||
判据:设备最大槽位为 1,且绑定创建后该设备的有效绑定唯一。此时设备只有唯一的可能当前卡,标记为当前使用卡是事实而非猜测。写入与绑定创建放在同一事务,保证绑定与当前卡标识一致可见;导入与手工绑定两条创建路径都要覆盖。
|
||
|
||
- 理由:多卡设备的当前卡必须来自运营商观测或显式切卡,绑定时不猜。
|
||
- 备选:绑定后异步触发一次设备观测(否决,依赖外部调用且失败时仍留空)。
|
||
|
||
### 4. 存量数据走受控人工修复,不写一次性代码
|
||
|
||
只读核对口径(识别待修复绑定):
|
||
|
||
```sql
|
||
SELECT d.id AS device_id, b.id AS binding_id, b.iot_card_id
|
||
FROM tb_device_sim_binding b
|
||
JOIN tb_device d ON d.id = b.device_id AND d.deleted_at IS NULL
|
||
WHERE b.bind_status = 1
|
||
AND d.max_sim_slots = 1
|
||
AND NOT EXISTS (SELECT 1 FROM tb_device_sim_binding x
|
||
WHERE x.device_id = b.device_id AND x.bind_status = 1 AND x.is_current)
|
||
AND (SELECT count(*) FROM tb_device_sim_binding y
|
||
WHERE y.device_id = b.device_id AND y.bind_status = 1) = 1;
|
||
```
|
||
|
||
受控修复:仅对上述结果集内的绑定置 `is_current = true`,同一设备只允许一条;执行前留存核对结果,执行后抽样比对设备卡列表与资产信息。
|
||
|
||
- 理由:生产写操作由维护者执行;一次性命令会长期留在仓库成为隐性维护面。
|
||
- 备选:新增数据修复命令或迁移(否决,超出必要范围且规则与绑定创建规则重复)。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- [无当前卡集合里包含「当前卡标识因观测缺失而丢失」的设备,回退可能把实际在用卡未实名的设备显示为已实名] → 以绑定卡本地事实为准,卡列表仍逐卡返回真实状态;上游设备登记类问题另行在运维侧处理,不在本 change。
|
||
- [绑定即置当前卡可能让设备观测的期望值提前满足] → 判据限定最大槽位为 1 且有效绑定唯一,此时可被切换的目标只有同一张卡;多卡设备不写入。
|
||
- [存量修复误伤多卡或重复当前卡] → 核对口径限定单槽且唯一有效绑定,修复语句限定 `bind_status = 1` 并保证同一设备至多一条 `is_current`;先只读核对,再小批执行。
|
||
- [C 端展示口径变化无自动化测试覆盖] → 按用户既有决策不新增自动化测试,改为对案例设备(19090)与抽样设备做发布后实接口核对。
|
||
|
||
## Migration Plan
|
||
|
||
1. 代码发布:无 Schema 变更,随常规二进制发布生效;展示口径回退只需回滚二进制,无数据残留。
|
||
2. 存量修复:先在只读环境按核对口径确认结果集,再由维护者按生产运行说明执行修复;不随发布自动执行。
|
||
3. 发布后核对:以案例设备 `90001595626` 与抽样多卡设备比对资产信息外层实名、卡列表当前卡标识。
|