Files
junhong_cmp_fiber/openspec/specs/asset-device/spec.md
break 54c4ec7ba4
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 14m42s
fix(设备实名): 无当前卡时按绑定卡实名回退并初始化单槽当前卡
- 设备资产解析外层 real_name_status:存在当前使用卡时沿用该卡状态,无任何当前卡时任一有效绑定卡已实名即视为已实名
- 手工绑定与设备导入:设备最大槽位为 1 且有效绑定唯一时,在同一事务内将该绑定初始化为当前使用卡;多卡设备不推断
- 同步 asset-device、personal-customer 主 Spec;归档变更并附存量核对清单(75 条单槽唯一待修复绑定,其中 16 条已实名)
2026-09-18 17:49:12 +08:00

178 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# asset-device 当前行为
## Purpose
描述卡、设备、资产状态与绑定关系的当前可观察行为。
## Requirements
### Requirement: 资产业务状态
系统 SHALL 将资产状态按 1=在库、2=已销售、3=已换货、4=已停用返回,并与运营商网络状态分离。
#### Scenario: 资产业务状态
- **GIVEN** 资产存在且具有业务与网络状态
- **WHEN** 查询资产详情
- **THEN** 响应分别返回业务状态与网络状态
### Requirement: 设备多卡关系
系统 SHALL 允许设备显式绑定和解绑卡,并在设备资产查询中展示当前卡关系。
#### Scenario: 设备多卡关系
- **GIVEN** 设备与卡均存在且操作人有权管理
- **WHEN** 执行绑定或解绑
- **THEN** 后续设备卡列表反映该关系变化
### Requirement: ICCID 按原始长度精确唯一
系统 SHALL 将未删除卡的 ICCID 唯一性按原始 ICCID 长度分别约束:原始长度为 19 的卡以 `iccid_19` 唯一;原始长度为 20 的卡以 `iccid_20` 唯一。20 位 ICCID 卡共享相同的前 19 位 SHALL 被视为有效数据。
#### Scenario: 20 位 ICCID 共享前缀
- **WHEN** 两张未删除卡具有不同的 20 位完整 ICCID但其前 19 位相同
- **THEN** 数据库接受两张卡,并通过各自的 `iccid_20` 保证完整 ICCID 唯一
#### Scenario: 19 位 ICCID 重复
- **WHEN** 两张未删除卡具有相同的 19 位完整 ICCID
- **THEN** 数据库拒绝第二张卡的重复值
### Requirement: 临期颜色等级仅用于展示
系统 SHALL 对剩余 8 至 15 个上海自然日的临期资产返回粉色等级、对剩余 4 至 7 天返回紫色等级、对剩余 0 至 3 天返回红色等级。颜色等级 MUST 不改变资产是否进入每日临期提醒扫描的条件。
#### Scenario: 红色资产仍每日提醒
- **GIVEN** 一项资产剩余 2 个上海自然日且存在有效通知接收人
- **WHEN** 查询临期资产列表并执行每日临期扫描
- **THEN** 列表返回红色等级,且扫描创建当天的套餐临期通知
### Requirement: 卡业务观测可靠事件标识
系统 SHALL 为卡与设备控制及其后续网络、流量观测生成不超过公共可靠事件存储上限的稳定事件标识,相同业务事实重试时 SHALL 保持同一标识。
#### Scenario: 停复机成功写入观测事件
- **WHEN** 卡或设备停复机的上游调用成功且本地事务记录业务结果
- **THEN** 系统在同一事务写入合法长度的业务观测可靠事件,不因事件标识超长回滚本地结果
#### Scenario: 网络或流量变化写入可靠事件
- **WHEN** 一次具有长观测标识的观测产生网络状态变化或流量正增量
- **THEN** 系统写入合法长度且可重复计算的可靠事件标识
#### Scenario: 非法可靠事件标识被边界拒绝
- **WHEN** 生产者向公共可靠事件存储提交超过字段上限的事件标识或父事件标识
- **THEN** 系统在持久化边界返回明确的参数错误而不是数据库字段错误
### Requirement: 资产与卡详情的优先轮询状态投影
资产解析接口(`GET /api/admin/assets/resolve/{identifier}`)的卡与设备两个分支 SHALL 返回该资产或卡的优先轮询状态投影,至少包含:是否处于优先轮询中、最近触发场景、最近轮询结果、最近轮询时间与失败原因。本能力 MUST NOT 为投影新增独立端点,投影 MUST 落在该既有解析端点内。投影口径 MUST 与该卡在优先轮询事实表中的未完成活动项一致存在待执行或执行中的项时「是否处于优先轮询中」为真最近触发场景取最近触发类型最近轮询结果取最近一次执行结果执行成功、轮询失败、已关闭、已过期或处理中最近轮询时间取最近一次执行或触发时间失败原因取最近一次可安全展示的失败原因。无任何优先轮询事实时字段以否或空值返回MUST NOT 以普通轮询结果填充。
投影 MUST 只读:读取 MUST NOT 创建、合并、推进或出队任何优先轮询项MUST NOT 触发上游调用。投影 MUST 受既有资产数据权限约束,越权资产 MUST 按不存在处理MUST NOT 通过该字段泄露范围外资产。
#### Scenario: 资产存在未完成优先项
- **GIVEN** 某卡的资产存在待执行的优先轮询项
- **WHEN** 查询该资产详情
- **THEN** 响应返回处于优先轮询中为真、最近触发场景与最近轮询时间
#### Scenario: 优先项失败展示原因
- **GIVEN** 某卡的优先轮询项已因尝试次数达到上限进入失败终态
- **WHEN** 查询该卡详情
- **THEN** 响应返回最近轮询结果为轮询失败及可安全展示的失败原因
#### Scenario: 无优先轮询事实
- **WHEN** 查询一个从未进入优先轮询、也无历史优先项的资产详情
- **THEN** 各投影字段以否或空值返回,不使用普通轮询结果填充
#### Scenario: 读取不改变队列
- **GIVEN** 某卡处于优先轮询执行中
- **WHEN** 连续查询该资产详情
- **THEN** 优先轮询项状态、触发次数与尝试次数均不因读取而改变
#### Scenario: 越权资产
- **WHEN** 调用者查询其数据范围外资产的详情
- **THEN** 响应与资产不存在不可区分,不返回任何优先轮询状态
### Requirement: 单槽单卡设备绑定时初始化当前卡标识
系统 SHALL 在设备最大槽位为 1 且该设备绑定的有效卡唯一时,将该绑定标记为当前使用卡;绑定成功后的设备卡列表 SHALL 把该卡作为当前卡对外返回。对多卡设备,系统 MUST NOT 在绑定时推断当前使用卡,其当前卡标识仍由运营商设备观测或显式切卡结果决定。系统 MUST 保证同一设备同一时刻至多一条有效绑定被标记为当前使用卡;解绑 SHALL 清除该绑定的当前卡标识。
#### Scenario: 单槽设备绑定唯一卡
- **GIVEN** 设备最大槽位为 1 且当前没有有效绑定卡
- **WHEN** 管理员为该设备绑定一张有效卡
- **THEN** 该绑定被标记为当前使用卡
- **AND** 后续设备卡列表中该卡返回为当前卡
#### Scenario: 多卡设备绑定不推断当前卡
- **GIVEN** 设备最大槽位大于 1
- **WHEN** 管理员为该设备绑定一张卡
- **THEN** 绑定结果不改变该设备既有的当前使用卡标识
#### Scenario: 解绑清除当前卡标识
- **GIVEN** 某有效绑定被标记为当前使用卡
- **WHEN** 该绑定被解绑
- **THEN** 该设备不再存在被标记为当前使用卡的有效绑定
### Requirement: 存量当前卡标识的核对与受控修复
系统 SHALL 提供只读核对口径,用于按「设备最大槽位为 1、有效绑定唯一、当前无任何当前卡标识」识别需要修复的存量绑定。受控修复 SHALL 仅将符合该口径的绑定标记为当前使用卡MUST NOT 修改其他绑定、MUST NOT 使同一设备出现多条当前使用卡。修复后设备卡列表 SHALL 反映该当前卡标识,设备实名判定 SHALL 按绑定卡事实返回。
#### Scenario: 只读核对识别待修复设备
- **GIVEN** 设备最大槽位为 1、存在唯一有效绑定且没有任何当前卡标识
- **WHEN** 维护者按核对口径查询
- **THEN** 该设备出现在待修复结果中,且查询本身不产生任何写入
#### Scenario: 受控修复后当前卡可见
- **GIVEN** 已按核对口径确认的设备
- **WHEN** 维护者执行受控修复
- **THEN** 该设备的唯一有效绑定被标记为当前使用卡
- **AND** 该设备的卡列表与资产信息按最新绑定事实返回
#### Scenario: 多卡设备不在修复范围
- **GIVEN** 设备最大槽位大于 1 或有效绑定多于一条
- **WHEN** 维护者按核对口径查询
- **THEN** 该设备不出现在待修复结果中
## 可达操作索引
本节只用于入口导航,不是行为 Requirement业务义务以上述 Requirements 为准。
### IoT卡管理
`GET /api/admin/iot-cards/{iccid}/realname-link`(获取实名认证链接);`PUT /api/admin/iot-cards/{iccid}/speed-tier`(设置卡固定限速档位);`POST /api/admin/iot-cards/batch-update-realname-policy`(批量更新卡实名认证策略);`POST /api/admin/iot-cards/import`批量导入IoT卡ICCID+MSISDN`GET /api/admin/iot-cards/import-tasks`(导入任务列表);`GET /api/admin/iot-cards/import-tasks/{id}`(导入任务详情);`PATCH /api/admin/iot-cards/series-binding`(批量设置卡的套餐系列绑定);`GET /api/admin/iot-cards/standalone`(单卡列表(未绑定设备));`POST /api/admin/iot-cards/standalone/allocate`(批量分配单卡);`POST /api/admin/iot-cards/standalone/recall`(批量回收单卡)。
### 设备管理
`GET /api/admin/devices`(设备列表);`DELETE /api/admin/devices/{virtual_no}`(删除设备);`GET /api/admin/devices/{virtual_no}/cards`(获取设备绑定的卡列表);`POST /api/admin/devices/{virtual_no}/cards`(绑定卡到设备);`DELETE /api/admin/devices/{virtual_no}/cards/{iccid}`(解绑设备上的卡);`POST /api/admin/devices/allocate`(批量分配设备);`POST /api/admin/devices/batch-update-realname-policy`(批量更新设备实名认证策略);`GET /api/admin/devices/by-identifier/{identifier}/gateway-slots`(查询卡槽信息);`POST /api/admin/devices/by-identifier/{identifier}/reboot`(重启设备);`POST /api/admin/devices/by-identifier/{identifier}/reset`(恢复出厂);`POST /api/admin/devices/by-identifier/{identifier}/switch-card`(切卡);`POST /api/admin/devices/by-identifier/{identifier}/switch-mode`(设置切卡模式);`PUT /api/admin/devices/by-identifier/{identifier}/wifi`(设置 WiFi`POST /api/admin/devices/import`(批量导入设备);`POST /api/admin/devices/import/allocations`创建CSV设备批量分配或回收任务`GET /api/admin/devices/import/tasks`(导入任务列表);`GET /api/admin/devices/import/tasks/{id}`(导入任务详情);`POST /api/admin/devices/recall`(批量回收设备);`PATCH /api/admin/devices/series-binding`(批量设置设备的套餐系列绑定)。
### 资产管理
`GET /api/admin/assets/{identifier}/current-package`(当前生效套餐);`PATCH /api/admin/assets/{identifier}/deactivate`(停用资产);`GET /api/admin/assets/{identifier}/operation-logs`(查询平台旧资产操作日志);`GET /api/admin/assets/{identifier}/orders`(资产历史订单);`GET /api/admin/assets/{identifier}/packages`(资产套餐列表);`PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/expires-at`(修改资产套餐过期时间);`PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/used-data`(修改资产套餐已用量);`PATCH /api/admin/assets/{identifier}/polling-status`(更新资产轮询状态);`PATCH /api/admin/assets/{identifier}/realname-mode`(更新资产实名认证策略);`PATCH /api/admin/assets/{identifier}/realname-status`(手动更新卡实名状态);`GET /api/admin/assets/{identifier}/realtime-status`(资产实时状态);`POST /api/admin/assets/{identifier}/refresh`(刷新资产状态);`POST /api/admin/assets/{identifier}/start`(复机);`POST /api/admin/assets/{identifier}/stop`(停机);`GET /api/admin/assets/{identifier}/wallet`(资产钱包概况);`GET /api/admin/assets/{identifier}/wallet/transactions`(资产钱包流水列表);`GET /api/admin/assets/resolve/{identifier}`(解析资产);`GET /api/admin/expiring-assets`(查询临期资产列表);`POST /api/admin/expiring-assets/reminder-scan`(手动触发套餐临期提醒扫描)。
### 资产分配记录
`GET /api/admin/asset-allocation-records`(分配记录列表);`GET /api/admin/asset-allocation-records/{id}`(分配记录详情)。
### 企业卡授权
`POST /api/admin/enterprises/{id}/allocate-cards`(授权卡给企业);`GET /api/admin/enterprises/{id}/cards`(企业卡列表);`POST /api/admin/enterprises/{id}/recall-cards`(回收卡授权)。
### 企业设备授权
`POST /api/admin/enterprises/{id}/allocate-devices`(授权设备给企业);`GET /api/admin/enterprises/{id}/devices`(企业设备列表);`POST /api/admin/enterprises/{id}/recall-devices`(撤销设备授权)。