diff --git a/openspec/changes/add-realname-time-history/design.md b/openspec/changes/add-realname-time-history/design.md index 8832640b..e776e7ef 100644 --- a/openspec/changes/add-realname-time-history/design.md +++ b/openspec/changes/add-realname-time-history/design.md @@ -15,7 +15,7 @@ **Non-Goals:** - 不迁移 `real_name_status`,不删除 `first_realname_at`。 -- 不为联通、电信做二次实名(无实名时间无法区分新旧)。 +- 不为上游不返回 `archiveTime` 的卡做二次实名(无实名时间无法区分新旧)。 - 不重算已判定的沉默期套餐。 - 不改实名逆转阈值、停复机规则。 @@ -49,7 +49,7 @@ ### 6. 二次实名执行 -导入接口同步解析与校验(≤1000 行),逐卡独立事务执行清除;清除复用 `ApplyCardObservation` 的人工纠偏通道(Source=manual_override,置未实名)以保持实名变化事件、审计与停复机评估一致,并在同事务写 `tb_iot_card_realname` 的 pending 字段与历史。运营商是否支持按卡所属运营商 `carrier_type = CMCC` 判定(常量白名单,后续确认其他运营商返回时间后再放开);同时要求当前实名时间非空作为基准。 +导入接口同步解析与校验(≤1000 行),逐卡独立事务执行清除;清除复用 `ApplyCardObservation` 的人工纠偏通道(Source=manual_override,置未实名)以保持实名变化事件、审计与停复机评估一致,并在同事务写 `tb_iot_card_realname` 的 pending 字段与历史。是否支持只看该卡 `upstream_realname_at` 是否非空(上游返回过 `archiveTime`),不按运营商写死;基准取 `upstream_realname_at`。 ### 7. 权限与接口 @@ -58,7 +58,6 @@ ## Risks / Trade-offs -- [仅抽样一个移动账户返回 `archiveTime`] → 白名单按运营商类型 CMCC;导入时额外要求卡当前实名时间来源为 upstream 或有上游最近返回时间,否则跳过并提示,避免对不返回时间的账户误清。 - [清除后实名管控账户会停机] → 用户已确认按普通未实名处理;测试环境网关只读,不会真实下发。 - [上游 `archiveTime` 时区或格式变化] → 解析失败视为空,不影响状态,日志告警。 - [迁移补录数据量大] → 分批插入、可重复执行。 @@ -70,8 +69,3 @@ 2. 部署 api / worker:开始解析 `archiveTime` 并写当前记录与历史;沉默期与展示切换读取源。 3. 管理端前端上线实名记录与二次实名页面,分配权限 `iot_card:realname_reset`。 4. 回滚:回滚 api / worker 镜像即可恢复旧读取(`first_realname_at` 一直保留且仍按旧规则写入);新表可保留。 - -## Open Questions - -- 其他移动账户是否都返回 `archiveTime`、联通 / 电信能否提供实名时间,需与网关确认;确认后扩充二次实名支持白名单。 -- 上游对已实名过的卡是否仍能正常生成实名链接,需上线后用一张测试卡验证。 diff --git a/openspec/changes/add-realname-time-history/proposal.md b/openspec/changes/add-realname-time-history/proposal.md index 2fa01e26..a0d49b12 100644 --- a/openspec/changes/add-realname-time-history/proposal.md +++ b/openspec/changes/add-realname-time-history/proposal.md @@ -4,11 +4,11 @@ ## What Changes -- 解析网关实名响应的 `archiveTime`(移动返回;联通、电信不返回)。 +- 解析网关实名响应的 `archiveTime`,返回了就以它为准;没返回的一律按"无实名时间"的规则处理。 - 新增「卡当前实名记录」表(每卡一行):当前实名时间及来源(上游返回 / 首次读到 / 迁移沿用)、上游最近返回时间、首次读到已实名时间、二次实名状态与基准。 - 新增「卡实名时间历史」表:只在当前实名时间变化时追加一条,不是每次读到都写。 - 实名时间口径:有上游时间用上游时间;没有则用首次读到已实名的时间;已实名的老卡沿用库里现存实名时间(迁移补录);已实名但库里无时间的卡留空,等下次读到再填。 -- 新增二次实名:管理端导入 ICCID / 虚拟号 / IMEI(设备则其全部绑定卡)形成批次,仅支持返回实名时间的运营商;系统清除卡实名状态与当前实名时间并记下清除前实名时间;之后上游返回的实名时间不晚于清除前时间时不覆盖,晚于时才算二次实名完成。C 端按普通未实名展示,不提示"二次实名"。 +- 新增二次实名:管理端导入 ICCID / 虚拟号 / IMEI(设备则其全部绑定卡)形成批次,仅支持上游返回过 `archiveTime` 的卡(以 `archiveTime` 为准,不按运营商区分);系统清除卡实名状态与当前实名时间并记下清除前的上游实名时间;之后上游返回的实名时间不晚于清除前时间时不覆盖,晚于时才算二次实名完成。C 端按普通未实名展示,不提示"二次实名"。 - 等待二次实名的卡按普通未实名卡处理:实名管控账户下按现有实名停机逻辑执行(关闭轮询的卡仍任何入口都不停复机),不新增停复机路径。 - 沉默期判定与资产实名时间展示改用当前实名时间;已判定过的套餐不重算。 - 管理端资产详情展示当前实名时间及来源、实名时间历史;二次实名批次导入与查询。 diff --git a/openspec/changes/add-realname-time-history/specs/card-realname-reset/spec.md b/openspec/changes/add-realname-time-history/specs/card-realname-reset/spec.md index 40c8af4c..34f58a86 100644 --- a/openspec/changes/add-realname-time-history/specs/card-realname-reset/spec.md +++ b/openspec/changes/add-realname-time-history/specs/card-realname-reset/spec.md @@ -2,7 +2,7 @@ ### Requirement: 二次实名批次导入 -有 `iot_card:realname_reset` 权限的运营 SHALL 可导入二次实名名单(xlsx / csv,一列:ICCID、虚拟号或 IMEI,单批最多 1000 行),填写原因后提交形成一个二次实名批次。系统逐行解析:ICCID 直接定位卡;虚拟号或 IMEI 定位设备,取设备当前全部绑定卡。以下行 MUST 跳过并记录原因,不影响其他行:资产不存在、卡未实名、卡所属运营商不返回实名时间(不支持二次实名)、该卡从未读到过上游实名时间、卡已处于等待二次实名、卡当前实名时间为空(无法确定新旧基准)。批次记录导入文件、总条数、成功数、跳过数及每行结果、操作人与时间。 +有 `iot_card:realname_reset` 权限的运营 SHALL 可导入二次实名名单(xlsx / csv,一列:ICCID、虚拟号或 IMEI,单批最多 1000 行),填写原因后提交形成一个二次实名批次。系统逐行解析:ICCID 直接定位卡;虚拟号或 IMEI 定位设备,取设备当前全部绑定卡。以下行 MUST 跳过并记录原因,不影响其他行:资产不存在、卡未实名、该卡上游从未返回过实名时间 `archiveTime`(无法区分新旧实名,不支持二次实名)、卡已处于等待二次实名。是否支持只以上游是否返回过 `archiveTime` 为准,不按运营商写死。批次记录导入文件、总条数、成功数、跳过数及每行结果、操作人与时间。 #### Scenario: 导入设备 @@ -11,8 +11,8 @@ #### Scenario: 导入不支持的运营商 -- **WHEN** 名单中有一张电信已实名卡 -- **THEN** 该行跳过,原因"该运营商不返回实名时间,不支持二次实名",其他行照常处理 +- **WHEN** 名单中有一张上游从未返回过 `archiveTime` 的已实名卡 +- **THEN** 该行跳过,原因"上游未返回实名时间,不支持二次实名",其他行照常处理 ### Requirement: 二次实名清除 @@ -25,7 +25,7 @@ ### Requirement: 二次实名新旧判定 -卡处于等待二次实名时,读到"已实名"观测 SHALL 按上游实名时间判定:上游实名时间晚于基准时间 → 二次实名完成,卡置为已实名,当前实名时间取上游时间(来源 upstream),清除等待状态,写"二次实名完成"历史;上游实名时间不晚于基准时间、或未返回上游实名时间 → MUST NOT 改实名状态、MUST NOT 写当前实名时间与历史,仅更新最近检查时间。卡处于等待二次实名时读到未实名观测按现有逻辑处理(保持未实名)。 +卡处于等待二次实名时,读到"已实名"观测 SHALL 以上游 `archiveTime` 为准判定:上游实名时间晚于基准时间(清除前的上游实名时间) → 二次实名完成,卡置为已实名,当前实名时间取上游时间(来源 upstream),清除等待状态,写"二次实名完成"历史;上游实名时间不晚于基准时间、或未返回上游实名时间 → MUST NOT 改实名状态、MUST NOT 写当前实名时间与历史,仅更新最近检查时间。卡处于等待二次实名时读到未实名观测按现有逻辑处理(保持未实名)。 #### Scenario: 读到旧实名 diff --git a/openspec/changes/add-realname-time-history/specs/card-realname-time/spec.md b/openspec/changes/add-realname-time-history/specs/card-realname-time/spec.md index a9d76b4c..0eeef948 100644 --- a/openspec/changes/add-realname-time-history/specs/card-realname-time/spec.md +++ b/openspec/changes/add-realname-time-history/specs/card-realname-time/spec.md @@ -32,7 +32,7 @@ 卡读到"已实名"观测(轮询、手动刷新、运营商回调、业务观测)并被采纳为已实名时,系统 SHALL 按以下顺序确定实名时间: -1. 观测带上游实名时间 → 取上游实名时间,来源 `upstream`; +1. 观测带上游实名时间 `archiveTime` → 一律以它为准,来源 `upstream`; 2. 否则当前记录已有实名时间 → 保持不变; 3. 否则 → 取本次观测时间(首次读到已实名),来源 `first_observed`。 diff --git a/openspec/changes/add-realname-time-history/tasks.md b/openspec/changes/add-realname-time-history/tasks.md index cd34ebbb..18f2ec6d 100644 --- a/openspec/changes/add-realname-time-history/tasks.md +++ b/openspec/changes/add-realname-time-history/tasks.md @@ -24,11 +24,11 @@ ## 5. 二次实名 -- [ ] 5.1 导入接口:解析 ICCID / 虚拟号 / IMEI(设备取全部绑定卡),逐行校验(不存在、未实名、运营商不支持、已在等待、无基准时间),形成批次与行结果 +- [ ] 5.1 导入接口:解析 ICCID / 虚拟号 / IMEI(设备取全部绑定卡),逐行校验(不存在、未实名、上游从未返回 `archiveTime`、已在等待),形成批次与行结果 - [ ] 5.2 清除执行:每卡独立事务,经人工纠偏通道置未实名,写 pending 基准与清除历史;不改 `first_realname_at`、不回退套餐 - [ ] 5.3 撤销接口:等待中的卡恢复已实名与基准时间,写撤销历史 - [ ] 5.4 批次列表、详情接口;路由注册、文档生成器 DTO 与权限 -- [ ] 5.5 测试:读到旧实名不覆盖、新实名完成、撤销、电信卡被跳过、设备展开为全部卡;确认 C 端接口无二次实名字段 +- [ ] 5.5 测试:读到旧实名不覆盖、新实名完成、撤销、上游无 `archiveTime` 的卡被跳过、设备展开为全部卡;确认 C 端接口无二次实名字段 ## 6. 验证与部署