feat: 资产标识符标准化、资产历史订单查询及导入虚拟号强制验证
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>
This commit is contained in:
2026-04-07 17:39:36 +08:00
parent 7e489a19fb
commit 80c6f6c756
69 changed files with 3039 additions and 851 deletions

View File

@@ -0,0 +1,53 @@
## Why
当前系统的 B 端资产操作接口大量使用数据库主键 ID`/assets/:asset_type/:id/``/devices/:id/`)作为路径参数,导致接口语义模糊、前端被迫持有无业务含义的 ID、换货后旧 ID 失效等问题。与此同时IoT 卡和设备的虚拟号VirtualNo没有跨表全局唯一约束ICCID/VirtualNo 在多个子系统中的使用也不一致。现阶段系统尚处于开发测试期,是建立资产标识符规范的最佳窗口。
## What Changes
- **[BREAKING]** B 端资产操作接口路径参数统一改为资产标识符ICCID 或 VirtualNo废弃基于数据库主键 ID 的路由
- **[BREAKING]** 订单创建接口B 端 + C 端)从传入 `iot_card_id`/`device_id` 主键改为传入 `identifier` 标识符
- **[NEW]** 新增全局资产标识符注册表 `tb_asset_identifier`,在数据库层保证 ICCID/VirtualNo 跨 `tb_iot_card``tb_device` 两表全局唯一,防止并发写入冲突
- **[NEW]** 资产解析服务Resolve主路径改走注册表单次查询即可定位资产替代原有的跨表 OR 查询
- **[FIX]** 修复:已绑定设备的 IoT 卡(`is_standalone=false`)可被单独购买套餐的 Bug改为直接报错并引导用户至设备页面购买
- 订单查询列表新增按资产标识符过滤条件
- 订单响应新增资产标识符字段,前端无需二次查询
- 所有 B 端 `:id` 路由同步更新,前端需切换调用方式
## Capabilities
### New Capabilities
- `asset-identifier-registry`:全局资产标识符注册表(`tb_asset_identifier`)的模型定义、写入/查询接口,以及 Resolve 服务的注册表主路径改造
- `asset-identifier-routes`B 端统一标识符路由规范——将 `/assets/:asset_type/:id/*``/devices/:id/*``/iot-cards/:id/*` 全线迁移为 `/assets/:identifier/*``/devices/:virtual_no/*`
### Modified Capabilities
- `asset-resolve`:主查询路径改走 `tb_asset_identifier`IMEI/SN/MSISDN 降级为 fallback 模糊查询
- `order-management`CreateAdminOrderRequest 改用 `identifier`OrderResponse 新增 `asset_identifier` 字段OrderListRequest 新增 `identifier` 过滤条件
- `client-order-purchase`CreateOrderRequest 改用 `identifier`,系统内部解析资产类型
- `package-purchase-validation`:新增 `is_standalone` 校验——若卡已绑定设备则拒绝单卡购买套餐
## Impact
**受影响的代码**
- `internal/model/`:新增 `AssetIdentifier` 模型;`Order` 模型新增 `asset_identifier` 快照字段
- `internal/store/postgres/`:新增 `asset_identifier_store.go``order_store.go` 新增标识符过滤
- `internal/service/asset/``Resolve` 方法改走注册表
- `internal/service/purchase_validation/`:新增 `is_standalone` 校验
- `internal/handler/admin/``asset.go``device.go``iot_card.go``order.go` 全面更新路径参数和请求/响应 DTO
- `internal/handler/app/``client_order.go` 更新请求 DTO
- `internal/routes/``asset.go``device.go``iot_card.go``order.go` 路由重注册
- `internal/model/dto/``asset_dto.go``order_dto.go``client_order_dto.go` 字段变更
- `migrations/`:新增迁移文件创建 `tb_asset_identifier`
**受影响的 APIBREAKING**
- `GET /api/admin/assets/:asset_type/:id/*`7 个端点)
- `POST /api/admin/assets/device/:device_id/stop|start`
- `DELETE /api/admin/devices/:id`
- `GET|POST|DELETE /api/admin/devices/:id/cards*`
- `PATCH /api/admin/devices/:id/deactivate`
- `PATCH /api/admin/iot-cards/:id/deactivate`
- `POST /api/admin/orders`(请求体变更)
- `POST /api/c/v1/orders/create`(请求体变更)
**前端影响**:所有使用 `asset_id` 做路径参数的页面必须切换为使用 `identifier`ICCID 或 VirtualNo