All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m21s
主要变更: - 新增 AssetIdentifier 模型及 Store,统一管理资产标识符(ICCID/IMEI/SN 等) - 新增迁移:asset_identifier 表、order 表新增 asset_identifier 字段、iot_card.virtual_no NOT NULL 约束 - 资产 Handler/Service/Route 全面重构,支持标识符路由查询与解析 - 新增资产历史订单查询接口,支持跨设备/卡/钱包维度的订单聚合 - 设备与物联卡导入任务强制校验虚拟号,缺失时直接拒绝 - Excel 工具函数优化,前端导入指引文档同步更新 - 归档三个 OpenSpec 提案:asset-identifier-standardization、asset-historical-orders、import-mandatory-virtual-no - 更新 OpenAPI 文档及相关 DTO Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
53 lines
3.0 KiB
Markdown
53 lines
3.0 KiB
Markdown
# Capability: 全局资产标识符注册表
|
||
|
||
## Purpose
|
||
|
||
维护一张全局唯一的资产标识符注册表(`tb_asset_identifier`),在数据库层保证 IoT 卡的 ICCID/VirtualNo 与设备的 VirtualNo 跨两张表不重复,消除并发写入竞态风险,同时提供高性能的标识符→资产 ID 精确查找能力。
|
||
|
||
## Requirements
|
||
|
||
### Requirement: 全局资产标识符注册表
|
||
|
||
系统 SHALL 维护一张全局唯一的资产标识符注册表(`tb_asset_identifier`),在数据库层保证 IoT 卡的 ICCID/VirtualNo 与设备的 VirtualNo 跨两张表不重复,消除并发写入竞态风险。
|
||
|
||
注册表仅存储**全局唯一标识符**(ICCID、VirtualNo),不存储 IMEI/SN/MSISDN 等非唯一标识符。
|
||
|
||
**表字段**:
|
||
- `id`:自增主键
|
||
- `identifier`:标识符值(VARCHAR(100),UNIQUE 约束)
|
||
- `asset_type`:资产类型(`iot_card` 或 `device`)
|
||
- `asset_id`:对应资产的主键 ID
|
||
- `created_at`:写入时间
|
||
|
||
#### Scenario: 创建设备时注册 VirtualNo
|
||
- **WHEN** 创建新设备(通过导入或 API),VirtualNo 非空
|
||
- **THEN** 系统在同一事务内向 `tb_asset_identifier` 写入一条记录(identifier=VirtualNo, asset_type=device, asset_id=新设备ID)
|
||
- **THEN** 若 VirtualNo 已在注册表中存在,事务回滚,返回错误"虚拟号已被占用"
|
||
|
||
#### Scenario: 创建 IoT 卡时注册 ICCID
|
||
- **WHEN** 创建新 IoT 卡(通过导入),ICCID 非空
|
||
- **THEN** 系统在同一事务内向注册表写入(identifier=ICCID, asset_type=iot_card, asset_id=新卡ID)
|
||
- **THEN** 若 ICCID 已在注册表中存在,事务回滚,返回错误"ICCID 已被占用"
|
||
|
||
#### Scenario: 创建 IoT 卡时注册 VirtualNo(如有)
|
||
- **WHEN** 创建新 IoT 卡时 VirtualNo 非空
|
||
- **THEN** 系统额外向注册表写入(identifier=VirtualNo, asset_type=iot_card, asset_id=新卡ID)
|
||
- **THEN** 若 VirtualNo 已被其他设备或卡占用,事务回滚,返回错误"虚拟号已被占用"
|
||
|
||
#### Scenario: 并发写入同一标识符
|
||
- **WHEN** 两个并发请求同时尝试注册相同的 VirtualNo(如 "CARD-001")
|
||
- **THEN** 数据库 UNIQUE 约束保证只有一个写入成功;另一个收到唯一约束冲突错误,事务回滚,返回错误"虚拟号已被占用"
|
||
|
||
#### Scenario: 软删除资产时清理注册表
|
||
- **WHEN** 软删除设备或 IoT 卡
|
||
- **THEN** 系统在同一事务内删除 `tb_asset_identifier` 中对应的记录(所有 asset_id 匹配的行),允许标识符被后续资产复用
|
||
|
||
#### Scenario: 通过标识符精确查找资产
|
||
- **WHEN** 系统需要根据 identifier(ICCID 或 VirtualNo)定位资产
|
||
- **THEN** 系统查询 `SELECT * FROM tb_asset_identifier WHERE identifier = ?`,一次查询得到 asset_type 和 asset_id
|
||
- **THEN** 再按 asset_type 查对应表(`tb_device` 或 `tb_iot_card`)取完整记录
|
||
|
||
#### Scenario: 查询不存在的标识符
|
||
- **WHEN** 查询注册表中不存在的 identifier
|
||
- **THEN** 返回空结果(not found),调用方可 fallback 到原有查询逻辑
|