# PRD:UR#73 按运营商实名能力控制复机 Status: ready-for-agent --- ## Problem Statement 当前停复机代码同时存在“行业卡无需实名”和“所有卡都必须实名”的互相冲突分支。卡业务类别被错误当成实名能力,导致相同运营商能力下出现不同复机结果,也使手动复机、设备复机、自动复机和开放接口无法共享同一规则。 七月迭代的数据同步公共方案将卡实名、流量和网络状态收口到统一 DDD 边界。UR#73 不能继续在旧 Service 中补条件,否则会形成第二套卡状态规则和状态写入口。 ## Solution 复机是否要求实名只由卡所属运营商的 `realname_link_type` 决定:`none` 不要求实名,`template` 和 `gateway` 要求卡已实名。`card_category` 仅保留分类展示用途,不再参与任何复机资格判断。 该规则进入数据同步公共 Asset/Card 领域模型,由统一复机 Application UseCase 供后台单卡、设备、自动任务和 OpenAPI 调用。Gateway 成功后的状态收敛、领域事件、Outbox、Audit Event 和同步触发全部复用公共 DDD,不在旧 Service 直接更新卡状态。 ## User Stories 1. 作为运营人员,我希望不支持实名能力的运营商卡即使本地仍为未实名,也可在满足其他条件时复机。 2. 作为运营人员,我希望需要实名的运营商卡在未实名时被明确拒绝。 3. 作为运营人员,我希望普通卡和行业卡遵循相同的运营商能力规则,卡类别不再造成例外。 4. 作为设备操作人员,我希望设备下每张卡独立按其运营商能力判断,不因设备中其他卡的状态被错误放行或拦截。 5. 作为 OpenAPI 调用方,我希望复机与后台入口使用同一资格规则。 6. 作为维护人员,我希望所有入口只调用一个复机用例,避免轮询、Service 和 Handler 各自维护规则。 7. 作为审计人员,我希望成功、拒绝和失败都进入统一审计链路并能关联后续同步观测。 ## Implementation Decisions ### 业务规则 - `realname_link_type=none` 表示运营商不要求实名;复机资格不检查 `real_name_status`。 - `realname_link_type=template` 或 `gateway` 表示运营商要求实名;仅 `real_name_status=1` 可继续复机。 - `card_category=normal/industry` 只用于分类和展示,不参与手动复机、设备复机、自动复机、套餐触发复机或 OpenAPI 复机判断。周期轮询资格不由 UR#73 调整。 - `realname_policy` 只描述实名与购买顺序,不替代运营商能力;本需求不得用它决定复机是否要求实名。 - 保留所有既有非实名约束,包括风险停机、已销户、机卡分离状态限定、停复机保护期、有效套餐、流量耗尽、网络状态和数据权限。 - 多卡设备逐卡判断。满足条件的卡正常处理,不满足条件的卡跳过或拒绝;不得因其中一张卡无需实名而放行全部卡。 - 未知 `realname_link_type`、运营商记录缺失或配置读取失败属于配置/内部错误,不得静默按 `none` 放行,也不得调用 Gateway。 - 未实名拒绝继续使用统一禁止访问业务错误,并向用户返回中文原因“卡未实名,无法操作”。 ### DDD 与公共数据同步边界 - 本需求依赖七月“数据同步触发与轮询优化”公共能力先落地;公共能力尚未完成时,不允许先在旧 StopResume Service 中打临时补丁。 - “运营商是否要求实名”和“卡当前是否具备复机资格”属于 Asset/Card 领域规则。领域方法只接收已加载的运营商能力和卡状态,不直接查询 GORM、Redis、Fiber 或 Gateway。 - 后台单卡复机、后台设备复机、自动复机、套餐激活后的复机和 OpenAPI 复机统一调用 Application 复机用例;各入口不得自行判断 `card_category`、`real_name_status` 或 `realname_link_type`。 - Application 用例负责加载并锁定必要状态、调用领域资格规则、执行 Gateway 端口、记录业务结果并发布可靠事件。 - 旧 Service 如需过渡,只能作为无业务判断的内部门面转发到新用例;不得继续直接写实名、网络状态、停机原因或复机时间。 - `ApplyCardObservation` 是上游观测写入卡状态的唯一入口。复机 Gateway 返回成功不授权旧 Service 直接修改本地网络状态。 - 复机成功边界发布网络状态同步触发,按公共方案创建立即、3 分钟、5 分钟观测序列;观测结果通过 `ApplyCardObservation` 应用,达到开机预期后剩余任务直接完成。 - 状态变化产生统一领域事件和 Outbox,触发其他业务联动;不得由多个入口重复执行相同副作用。UR#94 本期保持现有轮询调度和重排策略。 - Audit Event 和 Integration Log 使用公共审计设施;不得继续扩展旧资产审计为第二套长期写入系统。 ### 接口与前端 - 后台继续复用 `POST /api/admin/assets/{identifier}/start`;卡和设备均走统一复机用例。 - OpenAPI 继续复用现有卡复机接口;机卡分离限定仍先于一般复机规则生效。 - 本需求不新增 API,不因规则修正改变成功响应外层结构。 - 前端不读取 `card_category` 预判,不在本地复制运营商能力规则。 - 提交期间禁用重复操作;失败原地展示后端中文业务原因;成功后重新拉取资产详情。 - 详情刷新读取本地状态快照;异步观测尚未收敛时沿用公共同步状态展示,不伪造 Gateway 已确认结果。 ### 发布与兼容 - 删除或改造所有以 `card_category=industry` 跳过实名的生产分支和注释。 - 删除手动单卡、设备批量及自动复机中直接判断“必须已实名”的重复分支,统一转调领域规则。 - 发布前核查 `realname_link_type!=none` 但资产 `realname_policy=none` 的冲突数据;修正结果遵循公共数据同步方案,不在运行时静默选边。 - API、Worker 和公共卡状态能力在同一维护窗口切换,不保留新旧复机规则双写或双判定。 ## Testing Decisions - 领域单元测试覆盖 `none/template/gateway` 与实名 0/1、普通/行业卡的完整矩阵,并证明卡类别不影响结果。 - Application 测试使用可控 Gateway Adapter,验证不合格时绝不调用 Gateway,合格时只调用一次,并正确发布审计和同步触发。 - HTTP 集成测试覆盖后台卡复机、设备复机和 OpenAPI 入口,穿过真实认证、权限和统一错误处理;PostgreSQL、Redis 使用开发配置,Gateway 使用测试 Adapter,禁止对真实业务卡执行停复机。 - 验证设备内混合运营商能力、混合实名状态时逐卡处理,不发生整设备误放行。 - 回归风险停机、销户、保护期、无有效套餐、流量耗尽、非机卡分离 OpenAPI 卡等既有限制。 - 验证 Gateway 成功后旧 Service 没有直接写状态,公共同步序列生成一次,观测确认后由 `ApplyCardObservation` 更新状态并提前完成剩余任务。 - 验证重复请求、重复事件和重复观测不造成重复 Gateway 调用、重复领域事件或重复审计。 - 人工验收使用明确隔离的联调卡,对三个运营商能力值分别验证;核对 PostgreSQL 状态、Outbox/Audit Event、Integration Log 和同步任务关联链路。 ## Out of Scope - 不修改 `card_category` 的存储和展示。 - 不新增实名能力字段,继续使用运营商 `realname_link_type`。 - 不重新设计风险停机、销户、机卡分离、保护期、套餐或流量规则。 - 不新增显式同步按钮或同步 API。 - 不修改周期轮询的卡资格、卡类别过滤、配置、频率、队列或重排策略;UR#94 只把轮询观测的状态应用收口到公共用例。 - 不在 UR#73 单独实现公共观测、Outbox、审计或 0/3/5 调度的另一份副本。 - 不通过本需求处理运营商回调、事件同步和全部数据同步入口;回调与事件由 UR#94 交付,周期轮询策略本轮保持现状并留待后续设计。 ## Further Notes - 推荐实施顺序:数据同步公共 DDD → UR#73 接入统一复机用例 → 前后端联调。 - 当前代码仍存在行业卡豁免、强制实名和直接写网络状态等多套实现;它们是迁移输入,不是目标架构。 - 本 Spec 对数据同步公共 DDD 的复用是硬约束,不得以“先快速修一行”替代。