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