Files
junhong_cmp_fiber/openspec/changes/refactor-series-binding-to-series-id/tasks.md
huang 37f43d2e2d 重构: 将卡/设备的套餐系列绑定从分配ID改为系列ID
- 数据库: 重命名 series_allocation_id → series_id
- Model: IotCard 和 Device 字段重命名
- DTO: 所有请求/响应字段统一为 series_id
- Store: 方法重命名,新增 GetByShopAndSeries 查询
- Service: 业务逻辑优化,系列验证和权限验证分离
- 测试: 更新所有测试用例,新增 shop_series_allocation_store_test.go
- 文档: 更新 API 文档说明参数变更

BREAKING CHANGE: API 参数从 series_allocation_id 改为 series_id
2026-02-02 12:09:53 +08:00

192 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks: refactor-series-binding-to-series-id
## 1. 数据库迁移
- [x] 1.1 创建数据库迁移文件 `migrations/000XXX_refactor_series_binding_to_series_id.up.sql`,重命名 `tb_iot_card.series_allocation_id``series_id``tb_device.series_allocation_id``series_id`,更新字段注释
- [x] 1.2 创建回滚迁移文件 `migrations/000XXX_refactor_series_binding_to_series_id.down.sql`
- [x] 1.3 验证索引是否存在:检查 `tb_shop_series_allocation` 是否有 `(shop_id, series_id)` 复合索引,如不存在则添加
- [x] 1.4 执行迁移:运行 `migrate up`,验证字段重命名成功,无错误
## 2. Model 层修改
- [x] 2.1 修改 `internal/model/iot_card.go`:将 `SeriesAllocationID` 字段重命名为 `SeriesID`,更新 gorm 标签和注释
- [x] 2.2 修改 `internal/model/device.go`:将 `SeriesAllocationID` 字段重命名为 `SeriesID`,更新 gorm 标签和注释
- [x] 2.3 验证编译:运行 `go build ./internal/model/...`,确认无编译错误
## 3. DTO 层修改
- [x] 3.1 修改 `internal/model/dto/iot_card_dto.go`:更新 `ListStandaloneIotCardRequest` 的查询参数 `SeriesAllocationID``SeriesID`
- [x] 3.2 修改 `internal/model/dto/iot_card_dto.go`:更新 `StandaloneIotCardResponse` 的响应字段 `SeriesAllocationID``SeriesID`
- [x] 3.3 修改 `internal/model/dto/iot_card_dto.go`:更新 `BatchSetCardSeriesBindngRequest` 的请求字段 `SeriesAllocationID``SeriesID`,更新 description 为 "套餐系列ID0表示清除关联"
- [x] 3.4 修改 `internal/model/dto/device_dto.go`:更新 `ListDeviceRequest` 的查询参数 `SeriesAllocationID``SeriesID`
- [x] 3.5 修改 `internal/model/dto/device_dto.go`:更新 `DeviceResponse` 的响应字段 `SeriesAllocationID``SeriesID`
- [x] 3.6 修改 `internal/model/dto/device_dto.go`:更新 `BatchSetDeviceSeriesBindngRequest` 的请求字段 `SeriesAllocationID``SeriesID`,更新 description 为 "套餐系列ID0表示清除关联"
- [x] 3.7 验证编译:运行 `go build ./internal/model/dto/...`,确认无编译错误
## 4. Store 层修改
- [x] 4.1 修改 `internal/store/postgres/iot_card_store.go`:更新 `ListStandalone` 方法,将过滤条件 `series_allocation_id` 改为 `series_id`
- [x] 4.2 修改 `internal/store/postgres/iot_card_store.go`:更新 `Count` 方法,将过滤条件 `series_allocation_id` 改为 `series_id`
- [x] 4.3 修改 `internal/store/postgres/iot_card_store.go`:重命名方法 `BatchUpdateSeriesAllocation``BatchUpdateSeriesID`,更新 SQL 字段名
- [x] 4.4 修改 `internal/store/postgres/iot_card_store.go`:重命名方法 `ListBySeriesAllocationID``ListBySeriesID`,更新 WHERE 条件
- [x] 4.5 修改 `internal/store/postgres/device_store.go`:更新 `List` 方法,将过滤条件 `series_allocation_id` 改为 `series_id`
- [x] 4.6 修改 `internal/store/postgres/device_store.go`:重命名方法 `BatchUpdateSeriesAllocation``BatchUpdateSeriesID`,更新 SQL 字段名
- [x] 4.7 修改 `internal/store/postgres/device_store.go`:重命名方法 `ListBySeriesAllocationID``ListBySeriesID`,更新 WHERE 条件
- [x] 4.8 修改 `internal/store/postgres/shop_series_allocation_store.go`:新增方法 `GetByShopAndSeries(ctx, shopID, seriesID)`,实现根据店铺和系列查询分配配置
- [x] 4.9 验证或创建 `internal/store/postgres/package_series_store.go`:如不存在则创建,实现 `GetByID(ctx, id)` 方法
- [x] 4.10 如果创建了新 Store`internal/bootstrap/stores.go` 中注册 `PackageSeriesStore`
- [x] 4.11 验证编译:运行 `go build ./internal/store/...`,确认无编译错误
## 5. Service 层修改 - iot_card
- [x] 5.1 修改 `internal/service/iot_card/service.go`:更新 `ListStandalone` 方法,将过滤条件 key `series_allocation_id` 改为 `series_id`
- [x] 5.2 修改 `internal/service/iot_card/service.go`:更新 `buildStandaloneResponse` 方法,将字段 `SeriesAllocationID` 改为 `SeriesID`
- [x] 5.3 修改 `internal/service/iot_card/service.go`:重构 `BatchSetSeriesBinding` 方法
- [x] 5.4 在 `internal/service/iot_card/service.go``Service` 结构体中添加 `packageSeriesStore` 依赖(如果不存在)
- [x] 5.5 验证编译:运行 `go build ./internal/service/iot_card/...`,确认无编译错误
- [x] 5.6 运行 `lsp_diagnostics` 检查 `internal/service/iot_card/service.go`,确认无类型错误
## 6. Service 层修改 - device
- [x] 6.1 修改 `internal/service/device/service.go`:更新 `List` 方法,将过滤条件 key `series_allocation_id` 改为 `series_id`
- [x] 6.2 修改 `internal/service/device/service.go`:更新 `buildDeviceResponse` 方法,将字段 `SeriesAllocationID` 改为 `SeriesID`
- [x] 6.3 修改 `internal/service/device/service.go`:重构 `BatchSetSeriesBinding` 方法
- [x] 6.4 在 `internal/service/device/service.go``Service` 结构体中添加 `packageSeriesStore` 依赖(如果不存在)
- [x] 6.5 验证编译:运行 `go build ./internal/service/device/...`,确认无编译错误
- [x] 6.6 运行 `lsp_diagnostics` 检查 `internal/service/device/service.go`,确认无类型错误
## 7. Service 层修改 - purchase_validation关键
- [x] 7.1 修改 `internal/service/purchase_validation/service.go`:重构 `ValidateCardPurchase` 方法
- [x] 7.2 修改 `internal/service/purchase_validation/service.go`:重构 `ValidateDevicePurchase` 方法
- [x] 7.3 更新 `ValidateCardPurchase``ValidateDevicePurchase` 的错误消息,从 "套餐系列分配不存在" 改为 "该卡/设备未关联套餐系列"
- [x] 7.4 验证编译:运行 `go build ./internal/service/purchase_validation/...`,确认无编译错误
- [x] 7.5 运行 `lsp_diagnostics` 检查 `internal/service/purchase_validation/service.go`,确认无类型错误
## 8. Service 层修改 - commission_calculation关键
- [x] 8.1 修改 `internal/service/commission_calculation/service.go`:重构 `CalculateOrderCommission` 方法
- [x] 8.2 修改 `internal/service/commission_calculation/service.go`:重构 `CalculateDeviceOrderCommission` 方法(同样的逻辑)
- [x] 8.3 验证编译:运行 `go build ./internal/service/commission_calculation/...`,确认无编译错误
- [x] 8.4 运行 `lsp_diagnostics` 检查 `internal/service/commission_calculation/service.go`,确认无类型错误
## 9. Service 层修改 - recharge
- [x] 9.1 修改 `internal/service/recharge/service.go`:重构充值相关方法,将获取 `seriesAllocationID` 的逻辑改为直接使用 `seriesID`
- [x] 9.2 修改 `internal/service/recharge/service.go`:更新返佣查询逻辑,使用 `GetByShopAndSeries(shopID, seriesID)` 而不是 `GetByID(allocationID)`
- [x] 9.3 验证编译:运行 `go build ./internal/service/recharge/...`,确认无编译错误
- [x] 9.4 运行 `lsp_diagnostics` 检查 `internal/service/recharge/service.go`,确认无类型错误
## 10. Service 层修改 - order
- [x] 10.1 检查 `internal/service/order/service.go` 中是否有直接使用 `SeriesAllocationID` 的地方,如有则更新为 `SeriesID`
- [x] 10.2 验证编译:运行 `go build ./internal/service/order/...`,确认无编译错误
- [x] 10.3 运行 `lsp_diagnostics` 检查 `internal/service/order/service.go`,确认无类型错误
## 11. Bootstrap 依赖注入
- [x] 11.1 如果创建了 `PackageSeriesStore`,在 `internal/bootstrap/stores.go` 中初始化并添加到 `Stores` 结构体
- [x] 11.2 在 `internal/bootstrap/services.go` 中,为 `iot_card.Service``device.Service` 注入 `packageSeriesStore` 依赖
- [x] 11.3 验证编译:运行 `go build ./internal/bootstrap/...`,确认无编译错误
## 12. Handler & Routes 层
- [x] 12.1 修改 `internal/routes/iot_card.go`:更新 `/series-binding` 路由的 Description说明参数从 `series_allocation_id` 改为 `series_id`
- [x] 12.2 修改 `internal/routes/device.go`:更新 `/series-binding` 路由的 Description说明参数从 `series_allocation_id` 改为 `series_id`
- [x] 12.3 验证 Handler 层代码:`internal/handler/admin/iot_card.go``device.go` 无需修改(使用 DTO
- [x] 12.4 验证编译:运行 `go build ./internal/routes/... ./internal/handler/...`,确认无编译错误
## 13. Store 层测试更新
- [x] 13.1 修改 `internal/store/postgres/iot_card_store_test.go`:更新所有测试用例,将 `SeriesAllocationID` 改为 `SeriesID`
- [x] 13.2 修改 `internal/store/postgres/iot_card_store_test.go`:重命名测试函数 `TestIotCardStore_ListBySeriesAllocationID``TestIotCardStore_ListBySeriesID`
- [x] 13.3 修改 `internal/store/postgres/iot_card_store_test.go`:更新过滤条件测试,将 `series_allocation_id` 改为 `series_id`
- [x] 13.4 修改 `internal/store/postgres/device_store_test.go`:更新所有测试用例,将 `SeriesAllocationID` 改为 `SeriesID`
- [x] 13.5 修改 `internal/store/postgres/device_store_test.go`:重命名测试函数 `TestDeviceStore_ListBySeriesAllocationID``TestDeviceStore_ListBySeriesID`
- [x] 13.6 新增测试:在 `shop_series_allocation_store_test.go` 中添加 `TestShopSeriesAllocationStore_GetByShopAndSeries` 测试
- [x] 13.7 运行 Store 层测试:`source .env.local && go test -v ./internal/store/postgres/...`,确认全部通过
## 14. Service 层测试更新 - iot_card
- [x] 14.1 修改 `internal/service/iot_card/service_test.go`:更新 `TestIotCardService_BatchSetSeriesBinding` 测试
- [x] 14.2 更新测试数据准备顺序:先 `PackageSeries`,再 `ShopSeriesAllocation`,最后 `IotCard`
- [x] 14.3 运行 Service 层测试:`source .env.local && go test -v ./internal/service/iot_card/...`,确认全部通过
## 15. Service 层测试更新 - device
- [x] 15.1 修改 `internal/service/device/service_test.go`:更新 `TestDeviceService_BatchSetSeriesBinding` 测试
- [x] 15.2 更新测试数据准备顺序:先 `PackageSeries`,再 `ShopSeriesAllocation`,最后 `Device`
- [x] 15.3 运行 Service 层测试:`source .env.local && go test -v ./internal/service/device/...`,确认全部通过
## 16. Service 层测试更新 - purchase_validation
- [x] 16.1 修改 `internal/service/purchase_validation/service_test.go`:更新所有测试用例
- [x] 16.2 运行 Service 层测试:`source .env.local && go test -v ./internal/service/purchase_validation/...`,确认全部通过
## 17. Service 层测试更新 - commission_calculation
- [x] 17.1 修改 `internal/service/commission_calculation/service_test.go`:更新所有测试用例
- [x] 17.2 运行 Service 层测试:`source .env.local && go test -v ./internal/service/commission_calculation/...`,确认全部通过
## 18. Service 层测试更新 - recharge & order
- [x] 18.1 修改 `internal/service/recharge/service_test.go`:更新测试用例,将 `SeriesAllocationID` 改为 `SeriesID`
- [x] 18.2 修改 `internal/service/order/service_test.go`:更新测试用例,将 `SeriesAllocationID` 改为 `SeriesID`
- [x] 18.3 运行 Service 层测试:`source .env.local && go test -v ./internal/service/recharge/... ./internal/service/order/...`,确认全部通过
## 19. 集成测试更新 - iot_card
- [x] 19.1 修改 `tests/integration/iot_card_test.go`:更新 `TestIotCard_BatchSetSeriesBinding` 测试
- [x] 19.2 更新所有子测试用例的 JSON 请求体(约 10 个)
- [x] 19.3 运行集成测试:`source .env.local && cd tests/integration && go test -v -run "TestIotCard_BatchSetSeriesBinding"`,确认全部通过
## 20. 集成测试更新 - device
- [x] 20.1 修改 `tests/integration/device_test.go`:更新 `TestDevice_BatchSetSeriesBinding` 测试
- [x] 20.2 更新所有子测试用例的 JSON 请求体(约 10 个)
- [x] 20.3 运行集成测试:`source .env.local && cd tests/integration && go test -v -run "TestDevice_BatchSetSeriesBinding"`,确认全部通过
## 21. 单元测试更新 - commission_calculation
- [x] 21.1 修改 `tests/unit/commission_calculation_service_test.go`:更新所有测试用例
- [x] 21.2 运行单元测试:`source .env.local && go test -v ./tests/unit/...`,确认全部通过
## 22. 全量测试验证
- [x] 22.1 运行所有测试:`source .env.local && go test -v ./...`,确认全部通过,无遗漏
- [x] 22.2 运行编译检查:`go build ./...`,确认无编译错误
- [x] 22.3 使用 grep 搜索遗漏:`grep -r "SeriesAllocationID" internal/ --include="*.go"`,确认无遗漏
- [x] 22.4 使用 grep 搜索遗漏:`grep -r "series_allocation_id" internal/ --include="*.go"`,确认无遗漏(仅数据库注释除外)
## 23. API 手动测试
- [ ] 23.1 启动本地服务:`go run cmd/api/main.go`
- [ ] 23.2 测试 IoT 卡系列绑定 API`PATCH /api/admin/iot-cards/series-binding`,使用 Postman 或 curl 发送请求,验证参数 `series_id` 生效
- [ ] 23.3 测试设备系列绑定 API`PATCH /api/admin/devices/series-binding`,使用 Postman 或 curl 发送请求,验证参数 `series_id` 生效
- [ ] 23.4 测试卡列表查询:`GET /api/admin/iot-cards/standalone?series_id=1`,验证过滤生效
- [ ] 23.5 测试设备列表查询:`GET /api/admin/devices?series_id=1`,验证过滤生效
- [ ] 23.6 检查日志确认无错误日志SQL 查询使用 `series_id` 而不是 `series_allocation_id`
## 24. API 文档更新
- [x] 24.1 更新 OpenAPI 文档注释:确保路由描述中明确说明参数从 `series_allocation_id` 改为 `series_id`
- [x] 24.2 重新生成 API 文档:运行 `go run cmd/gendocs/main.go`,生成最新的 OpenAPI spec
- [x] 24.3 验证生成的文档:检查 `docs/admin-openapi.yaml``/iot-cards/series-binding``/devices/series-binding` 的参数定义
- [x] 24.4 如有前端文档,更新前端接口文档,说明 BREAKING CHANGE
## 25. 清理和最终验证
- [x] 25.1 删除所有临时代码和注释
- [x] 25.2 运行 `gofmt -w .` 格式化所有代码
- [x] 25.3 运行 `go mod tidy` 清理依赖
- [x] 25.4 再次运行全量测试:`source .env.local && go test -v ./...`,确认全部通过
- [x] 25.5 使用 `git diff` 检查所有改动,确认无遗漏,无多余修改
- [x] 25.6 更新 CHANGELOG如有记录 BREAKING CHANGE
## 26. 提交和归档
- [ ] 26.1 提交代码:创建 Git commit使用中文 commit message"重构: 将卡/设备的套餐系列绑定从分配ID改为系列ID"
- [ ] 26.2 运行 OpenSpec 归档:`openspec archive --change refactor-series-binding-to-series-id`
- [ ] 26.3 验证归档成功:检查 `openspec/changes/archive/` 目录,确认变更已归档
- [ ] 26.4 清理工作目录:删除 `openspec/changes/refactor-series-binding-to-series-id/`(已归档)