feat: 卡实名时间以上游 archiveTime 为准并记录实名时间历史,新增二次实名批次
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled

This commit is contained in:
邹进
2026-11-08 19:12:46 +09:00
parent 94f86ab217
commit 4967f4df43
32 changed files with 1885 additions and 47 deletions

View File

@@ -1,33 +1,33 @@
## 1. 数据模型与迁移
- [ ] 1.1 新增迁移:`tb_iot_card_realname`(card_id 唯一)、`tb_iot_card_realname_history`(索引 card_id,id desc)、`tb_iot_card_realname_reset_batch`,含 down 迁移
- [ ] 1.2 新增 model 与常量(时间来源、变化类型、二次实名状态、权限码 `iot_card:realname_reset`),scan 结构显式 gorm column 标签
- [ ] 1.3 迁移补录:已实名且 `first_realname_at` 非空的卡按 id 分批插入当前记录(migrated)与迁移历史,可重复执行;补测试验证幂等
- [x] 1.1 新增迁移:`tb_iot_card_realname`(card_id 唯一)、`tb_iot_card_realname_history`(索引 card_id,id desc)、`tb_iot_card_realname_reset_batch`,含 down 迁移
- [x] 1.2 新增 model 与常量(时间来源、变化类型、二次实名状态、权限码 `iot_card:realname_reset`),scan 结构显式 gorm column 标签
- [x] 1.3 迁移补录:已实名且 `first_realname_at` 非空的卡按 id 分批插入当前记录(migrated)与迁移历史,可重复执行;补测试验证幂等
## 2. 上游实名时间解析
- [ ] 2.1 `RealnameStatusResp` 增加 `archiveTime`,解析为北京时间 `*time.Time`,null/空/格式错视为空;表驱动测试覆盖移动、联通、电信三种响应
- [ ] 2.2 `RealnameObservation` 增加 `UpstreamRealnameAt`,轮询、观测序列、手动刷新入口透传
- [x] 2.1 `RealnameStatusResp` 增加 `archiveTime`,解析为北京时间 `*time.Time`,null/空/格式错视为空;表驱动测试覆盖移动、联通、电信三种响应
- [x] 2.2 `RealnameObservation` 增加 `UpstreamRealnameAt`,轮询、观测序列、手动刷新入口透传
## 3. 当前实名时间与历史
- [ ] 3.1 领域纯函数 `DecideRealnameTime`:上游优先 → 保持 → 首次读到;相同不写;未实名不写;二次实名新旧判定;表驱动测试覆盖 spec 全部场景
- [ ] 3.2 `ApplyCardObservation` 事务内锁定 / 创建当前实名记录,按决定更新并追加历史;二次实名 pending 时拦截旧实名观测
- [x] 3.1 领域纯函数 `DecideRealnameTime`:上游优先 → 保持 → 首次读到;相同不写;未实名不写;二次实名新旧判定;表驱动测试覆盖 spec 全部场景
- [x] 3.2 `ApplyCardObservation` 事务内锁定 / 创建当前实名记录,按决定更新并追加历史;二次实名 pending 时拦截旧实名观测
- [ ] 3.3 集成测试:重复轮询不写历史、上游更正 migrated 时间、逆转后再实名不写历史
## 4. 沉默期与展示切换
- [ ] 4.1 `packagesilence/resolve.go` 卡实名时间改取当前实名时间;已实名无时间时写当前记录(first_observed)并写历史,不再补写 `first_realname_at`
- [ ] 4.2 资产详情、卡列表、导出"实名时间"改取当前实名时间;设备聚合口径不变
- [ ] 4.3 新增 `GET /api/admin/iot-cards/:id/realname-history` 分页查询,返回来源与变化类型中文名
- [x] 4.1 `packagesilence/resolve.go` 卡实名时间改取当前实名时间;已实名无时间时写当前记录(first_observed)并写历史(`first_realname_at` 照旧补写,供其他旧读取方兼容)
- [x] 4.2 资产详情、卡列表、导出"实名时间"改取当前实名时间;设备聚合口径不变
- [x] 4.3 新增 `GET /api/admin/iot-cards/:id/realname-history` 分页查询,返回来源与变化类型中文名
- [ ] 4.4 回归测试:沉默期期内 / 截止后实名、已判定不重算
## 5. 二次实名
- [ ] 5.1 导入接口:解析 ICCID / 虚拟号 / IMEI(设备取全部绑定卡),逐行校验(不存在、未实名、上游从未返回 `archiveTime`、已在等待),形成批次与行结果
- [ ] 5.2 清除执行:每卡独立事务,经人工纠偏通道置未实名,写 pending 基准与清除历史;不改 `first_realname_at`、不回退套餐
- [ ] 5.3 撤销接口:等待中的卡恢复已实名与基准时间,写撤销历史
- [ ] 5.4 批次列表、详情接口;路由注册、文档生成器 DTO 与权限
- [x] 5.1 导入接口:解析 ICCID / 虚拟号 / IMEI(设备取全部绑定卡),逐行校验(不存在、未实名、上游从未返回 `archiveTime`、已在等待),形成批次与行结果
- [x] 5.2 清除执行:每卡独立事务,经人工纠偏通道置未实名,写 pending 基准与清除历史;不改 `first_realname_at`、不回退套餐
- [x] 5.3 撤销接口:等待中的卡恢复已实名与基准时间,写撤销历史
- [x] 5.4 批次列表、详情接口;路由注册、文档生成器 DTO 与权限
- [ ] 5.5 测试:读到旧实名不覆盖、新实名完成、撤销、上游无 `archiveTime` 的卡被跳过、设备展开为全部卡;确认 C 端接口无二次实名字段
## 6. 验证与部署