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>
3.0 KiB
3.0 KiB
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:对应资产的主键 IDcreated_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 到原有查询逻辑