# 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 的复用是硬约束，不得以“先快速修一行”替代。
