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>
6.4 KiB
6.4 KiB
1. 数据库迁移:全局标识符注册表
- 1.1 创建迁移文件:
tb_asset_identifier表(identifier UNIQUE、asset_type、asset_id、created_at),建立idx_asset_identifier_asset(asset_type, asset_id)索引 - 1.2 执行迁移,验证表结构和唯一约束正确
2. 数据层:AssetIdentifier 模型与 Store
- 2.1 创建
internal/model/asset_identifier.go:定义AssetIdentifier结构体(含 GORM 标签、TableName) - 2.2 创建
internal/store/postgres/asset_identifier_store.go:实现Register(ctx, identifier, assetType, assetID)(写入注册表)、FindByIdentifier(ctx, identifier)(精确查询)、DeleteByAsset(ctx, assetType, assetID)(删除资产时清理) - 2.3 在
internal/bootstrap/stores.go注入AssetIdentifierStore
3. 数据层:Order 模型新增 asset_identifier 字段
- 3.1 创建迁移文件:
tb_order表增加asset_identifier VARCHAR(100)字段 - 3.2 更新
internal/model/order.go:新增AssetIdentifier string字段(含 GORM 标签和注释) - 3.3 更新
internal/store/postgres/order_store.go:列表查询新增identifier过滤条件(精确匹配asset_identifier)
4. 服务层:Resolve 改走注册表
- 4.1 更新
internal/service/asset/service.go:Resolve方法优先查AssetIdentifierStore.FindByIdentifier(),命中则按 asset_type 查对应表;未命中 fallback 原有逻辑 - 4.2 在
AssetService中注入AssetIdentifierStore依赖
5. 服务层:purchase_validation 增加 is_standalone 校验
- 5.1 更新
internal/service/purchase_validation/service.go:在ValidateCardPurchase()中增加if !card.IsStandalone { return error "该卡已绑定设备,请前往设备页面购买套餐" }校验(在套餐校验之前执行)
6. 服务层:资产创建/删除时同步注册表
- 6.1 更新设备导入任务
internal/task/device_import.go:创建设备成功后在同一事务内调用AssetIdentifierStore.Register()注册 VirtualNo;若冲突则拒绝该行 - 6.2 更新 IoT 卡导入任务
internal/task/iot_card_import.go:创建卡成功后注册 ICCID(必填)和 VirtualNo(如有)到注册表;冲突则拒绝该行 - 6.3 更新设备/IoT 卡的软删除逻辑:删除时调用
AssetIdentifierStore.DeleteByAsset()清理注册表
7. 服务层:订单创建改用 identifier
- 7.1 更新
internal/service/order/service.go(B 端):Create()方法接收identifier字符串,内部调用AssetService.Resolve()解析资产,不再接受iot_card_id/device_id;写入订单时填入asset_identifier快照字段 - 7.2 更新
internal/service/client_order/service.go(C 端):同上,接收identifier,解析后写入asset_identifier快照
8. DTO 层:更新请求/响应结构
- 8.1 更新
internal/model/dto/order_dto.go:CreateAdminOrderRequest去掉IotCardID/DeviceID/OrderType,新增Identifier string;OrderResponse新增AssetIdentifier string和AssetType string;OrderListRequest新增Identifier string - 8.2 更新
internal/model/dto/client_order_dto.go(CreateOrderRequest):同上,去掉IotCardID/DeviceID/OrderType,新增Identifier string - 8.3 更新
internal/model/dto/asset_dto.go:AssetTypeIDRequest废弃,AssetResolveRequest确认Identifier字段已有;新增AssetIdentifierRequest(路径参数,供操作接口使用) - 8.4 确认
AssetResolveResponse新增Identifier字段(回传请求中使用的标识符)
9. Handler 层:B 端资产操作接口更新
- 9.1 更新
internal/handler/admin/asset.go:RealtimeStatus/Refresh/Packages/CurrentPackage/UpdatePollingStatus/StopDevice/StartDevice/StopCard/StartCard均改为从路径参数:identifier读取标识符,调用 Resolve 解析后操作;合并 Stop/Start 卡和设备为统一的Stop/Starthandler(内部按资产类型分支) - 9.2 更新
internal/handler/admin/asset.go:新增Deactivatehandler(合并 IoT 卡和设备停用,原/iot-cards/:id/deactivate和/devices/:id/deactivate) - 9.3 更新
internal/handler/admin/device.go:Delete/ListCards/BindCard/UnbindCard改为从路径参数:virtual_no读取设备标识;UnbindCard第二段路径参数改为:iccid
10. Handler 层:B 端订单 Handler 更新
- 10.1 更新
internal/handler/admin/order.go:Createhandler 解析CreateAdminOrderRequest.Identifier字段,移除IotCardID/DeviceID解析逻辑;Listhandler 支持identifierquery 参数传入 Store 过滤
11. Handler 层:C 端订单 Handler 更新
- 11.1 更新
internal/handler/app/client_order.go:CreateOrderhandler 解析CreateOrderRequest.Identifier,移除旧字段解析
12. 路由层:重注册所有受影响路由
- 12.1 更新
internal/routes/asset.go:将所有:asset_type/:id路由改为:identifier;合并卡/设备的 stop/start/deactivate 路由 - 12.2 更新
internal/routes/device.go:/:id改为/:virtual_no;解绑路由/:id/cards/:cardId改为/:virtual_no/cards/:iccid - 12.3 更新
internal/routes/iot_card.go:移除/:id/deactivate(已并入 asset.go 的统一路由) - 12.4 更新
internal/routes/order.go:无路由变更,仅确认 Handler 已更新
13. 文档生成器更新
- 13.1 更新
cmd/api/docs.go和cmd/gendocs/main.go:确保新 Handler 方法已注册(Deactivate 等新合并 handler 需加入) - 13.2 执行
go run cmd/gendocs/main.go重新生成 OpenAPI 文档,验证新路由和 DTO 正确体现
14. 验证
- 14.1 构建验证:
go build ./...无编译错误 - 14.2 LSP 诊断:对所有修改文件运行 lsp_diagnostics,无错误/警告
- 14.3 接口验证(使用 PostgreSQL MCP 和 curl):
- 通过 ICCID 调用
GET /api/admin/assets/:identifier/packages返回正确套餐列表 - 通过 VirtualNo 调用
POST /api/admin/assets/:identifier/stop执行停机 - 使用绑定设备的卡 identifier 创建订单,验证返回"该卡已绑定设备"错误
- 使用独立卡 identifier 创建订单成功,OrderResponse 包含
asset_identifier字段 - 并发写入同一 VirtualNo 到注册表,验证只有一条成功
- 通过 ICCID 调用
- 14.4 订单列表验证:
GET /api/admin/orders?identifier=CARD-001返回该资产的订单