Files
junhong_cmp_fiber/openspec/changes/archive/2026-02-28-fix-agent-wallet-order-creation/tasks.md
huang 8ed3d9da93
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m0s
feat: 实现代理钱包订单创建和订单角色追踪功能
新增功能:
- 代理在后台使用 wallet 支付时,订单直接完成(扣款 + 激活套餐)
- 支持代理自购和代理代购场景
- 新增订单角色追踪字段(operator_id、operator_type、actual_paid_amount、purchase_role)
- 订单查询支持 OR 逻辑(buyer_id 或 operator_id)
- 钱包流水记录交易子类型和关联店铺
- 佣金逻辑调整:代理代购不产生佣金

数据库变更:
- 订单表新增 4 个字段和 2 个索引
- 钱包流水表新增 2 个字段
- 包含迁移脚本和回滚脚本

文档:
- 功能总结文档
- 部署指南
- OpenAPI 文档更新
- Specs 同步(新增 agent-order-role-tracking capability)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-28 14:11:42 +08:00

13 KiB
Raw Blame History

1. 数据库结构变更

  • 1.1 创建订单表字段迁移脚本(migrations/xxx_add_operator_fields_to_orders.up.sql),新增 operator_idoperator_typeactual_paid_amountpurchase_role 字段,添加字段注释
  • 1.2 在迁移脚本中创建索引(idx_orders_operator_ididx_orders_purchase_role),使用 CONCURRENTLY 避免锁表
  • 1.3 创建钱包流水表字段迁移脚本(migrations/xxx_add_transaction_subtype_to_wallet_transaction.up.sql),检查并添加 transaction_subtyperelated_shop_id 字段(如果不存在)
  • 1.4 创建数据回滚迁移脚本(*.down.sql),包含 DROP INDEX 和 DROP COLUMN 语句
  • 1.5 在测试环境执行迁移,验证字段创建成功,检查 \d tb_order\d tb_agent_wallet_transaction 输出

2. Model 层:订单角色追踪

  • 2.1 在 internal/model/order.go 中的 Order 结构体添加新字段:OperatorIDOperatorTypeActualPaidAmountPurchaseRole,添加 gorm 标签和中文注释
  • 2.2 在 internal/model/order.go 中定义订单角色枚举常量(PurchaseRoleSelfPurchasePurchaseRolePurchasedByParentPurchaseRolePurchasedByPlatformPurchaseRolePurchaseForSubordinate),添加中文注释
  • 2.3 在 internal/model/agent_wallet.go 中确认 AgentWalletTransaction 结构体包含 TransactionSubtypeRelatedShopID 字段(如果不存在则添加)
  • 2.4 运行 go build ./... 验证编译通过

3. 常量定义:钱包流水子类型

  • 3.1 在 pkg/constants/wallet.go 中新增钱包交易子类型常量(WalletTransactionSubtypeSelfPurchaseWalletTransactionSubtypePurchaseForSubordinate),添加中文注释
  • 3.2 运行 go build ./... 验证编译通过

4. DTO 层:订单请求和响应

  • 4.1 在 internal/model/dto/order_dto.goOrderResponse 中添加新字段:OperatorIDOperatorTypeOperatorNameActualPaidAmountPurchaseRoleIsPurchasedByParentPurchaseRemark,添加 JSON 标签和 description 注释
  • 4.2 在 internal/model/dto/order_dto.goOrderListRequest 中添加 PurchaseRole 筛选字段,添加验证标签(validate:"omitempty,oneof=self_purchase purchased_by_parent purchased_by_platform purchase_for_subordinate"
  • 4.3 运行 go build ./... 验证编译通过

5. Store 层:订单查询 OR 逻辑

  • 5.1 修改 internal/store/postgres/order_store.goList() 方法,支持 shop_id 筛选时使用 OR 查询:WHERE (buyer_type = 'agent' AND buyer_id = ?) OR operator_id = ?
  • 5.2 在 List() 方法中添加 purchase_role 精确匹配筛选支持
  • 5.3 运行 go build ./... 验证编译通过
  • 5.4 使用 PostgreSQL MCP 工具验证查询逻辑:创建测试订单,执行 SELECT * FROM tb_order WHERE (buyer_id = X) OR (operator_id = X) 并检查 EXPLAIN 输出

6. Service 层:成本价查询辅助方法

  • 6.1 在 internal/service/order/service.go 中新增 getCostPrice(ctx, shopID, packageID) 方法,通过 ShopPackageAllocation 查询店铺对套餐的成本价
  • 6.2 添加错误处理:如果查询失败,返回 errors.New(errors.CodeInvalidParam, "店铺没有该套餐的分配配置")
  • 6.3 运行 go build ./... 验证编译通过

7. Service 层:钱包流水创建方法

  • 7.1 在 internal/service/order/service.go 中新增 createWalletTransaction(ctx, tx, walletID, orderID, amount, purchaseRole, relatedShopID) 方法
  • 7.2 在方法中根据 purchaseRole 确定 transaction_subtyperemark:自购场景填充"购买套餐",代购场景查询下级店铺名称填充"为下级代理【XX】购买套餐"
  • 7.3 创建 AgentWalletTransaction 记录,设置 TransactionType = AgentTransactionTypeDeductTransactionSubtypeAmount(负数)、RelatedShopIDRemark
  • 7.4 运行 go build ./... 验证编译通过

8. Service 层:钱包支付订单创建方法

  • 8.1 在 internal/service/order/service.go 中新增 createOrderWithWalletPayment(ctx, order, items, operatorShopID, buyerShopID) 方法
  • 8.2 在方法开头(事务外)检查钱包余额,如果余额不足返回错误
  • 8.3 开启 GORM 事务,在事务中依次执行:创建订单(tx.Create(order))、创建订单明细(tx.CreateInBatches(items, 100)
  • 8.4 在事务中扣减钱包余额,使用乐观锁:WHERE id = ? AND balance >= ? AND version = ?,更新 balance = balance - ?version = version + 1
  • 8.5 检查 RowsAffected,如果为 0 返回 errors.New(errors.CodeInsufficientBalance, "余额不足或并发冲突")
  • 8.6 在事务中调用 createWalletTransaction() 创建钱包流水
  • 8.7 在事务中调用 activatePackage() 激活套餐
  • 8.8 事务外判断是否入队佣金计算:if order.OperatorID == nil { s.enqueueCommissionCalculation() }(平台代购才入队)
  • 8.9 运行 go build ./... 验证编译通过

9. Service 层:订单创建流程重构

  • 9.1 在 internal/service/order/service.goCreate() 方法中,在幂等性检查后添加场景判断逻辑
  • 9.2 提取资源所属店铺 IDvalidationResult.Card.ShopIDvalidationResult.Device.ShopID
  • 9.3 处理 offline 场景:设置 operator_id = niloperator_type = "platform"purchase_role = "purchased_by_platform",调用 resolvePurchaseOnBehalfInfo() 获取买家成本价,保持现有逻辑调用 createOrderWithActivation()
  • 9.4 处理 wallet 场景:获取操作者店铺 ID判断资源是否属于操作者
  • 9.5 如果资源属于操作者(自购):设置 buyer = operatorpurchase_role = "self_purchase"is_purchase_on_behalf = false,调用 getCostPrice() 获取成本价,total_amount = actual_paid_amount = 操作者成本价
  • 9.6 如果资源不属于操作者(代购):设置 buyer = 资源所属者operator = 操作者purchase_role = "purchase_for_subordinate"is_purchase_on_behalf = true,分别调用 getCostPrice() 获取买家和操作者成本价,total_amount = 买家成本价actual_paid_amount = 操作者成本价
  • 9.7 wallet 场景调用 createOrderWithWalletPayment() 而不是 orderStore.Create()
  • 9.8 运行 go build ./... 验证编译通过

10. Service 层:订单响应构建方法

  • 10.1 在 internal/service/order/service.gobuildOrderResponse() 方法中添加新字段映射:OperatorIDOperatorTypeActualPaidAmountPurchaseRole
  • 10.2 添加 OperatorName 字段逻辑:如果 operator_type = "agent"operator_id 不为空,查询 Shop 表获取店铺名称
  • 10.3 添加 IsPurchasedByParent 派生字段:purchase_role == "purchased_by_parent"
  • 10.4 添加 PurchaseRemark 派生字段:根据 purchase_roleoperator_name 生成备注文本(如"由上级代理【XX】购买"、"由平台代购"
  • 10.5 运行 go build ./... 验证编译通过

11. Handler 层:权限检查调整

  • 11.1 在 internal/handler/admin/order.goCreate() 方法中,修改 wallet 支付方式的权限检查,允许代理、平台、超管使用
  • 11.2 保持 offline 支付方式只允许平台和超管使用的限制
  • 11.3 运行 go build ./... 验证编译通过

12. Handler 层:订单查询参数传递

  • 12.1 在 internal/handler/admin/order.goList() 方法中,从查询参数解析 purchase_role
  • 12.2 将 purchase_role 传递给 Service 层的 List() 方法
  • 12.3 运行 go build ./... 验证编译通过

13. 文档生成器更新OpenAPI

  • 13.1 确认 cmd/api/docs.gocmd/gendocs/main.go 中的 Handlers 结构体已包含 Order Handler如果不存在则添加
  • 13.2 运行 go run cmd/gendocs/main.go 生成 OpenAPI 文档
  • 13.3 检查生成的文档中订单创建和列表接口的请求/响应字段是否包含新字段

14. 集成测试:代理自购场景

  • 14.1 使用 PostgreSQL MCP 工具创建测试数据:创建代理账号、代理钱包(余额 10000 分、IoT 卡shop_id = 代理店铺 ID、套餐分配配置成本价 8000 分)
  • 14.2 使用 Postman/curl 调用后台订单创建 API代理账号创建订单支付方式 wallet选择自己的卡和套餐
  • 14.3 验证响应:payment_status = 2operator_id = 代理店铺 IDbuyer_id = 代理店铺 IDpurchase_role = "self_purchase"total_amount = 8000actual_paid_amount = 8000
  • 14.4 使用 PostgreSQL MCP 查询订单表,验证订单记录正确
  • 14.5 使用 PostgreSQL MCP 查询钱包表,验证余额扣减:balance = 200010000 - 8000
  • 14.6 使用 PostgreSQL MCP 查询钱包流水表,验证流水记录:transaction_subtype = "self_purchase"amount = -8000remark = "购买套餐"
  • 14.7 使用 PostgreSQL MCP 查询套餐使用表(tb_package_usage),验证套餐已激活:status = 1

15. 集成测试:代理代购场景

  • 15.1 使用 PostgreSQL MCP 工具创建测试数据:一级代理(成本价 8000、二级代理成本价 10000parent_shop_id = 一级代理)、一级代理钱包(余额 10000、IoT 卡shop_id = 二级代理店铺 ID、套餐分配配置
  • 15.2 使用 Postman/curl 调用后台订单创建 API一级代理账号创建订单支付方式 wallet选择二级代理的卡和套餐
  • 15.3 验证响应:payment_status = 2operator_id = 一级代理店铺 IDbuyer_id = 二级代理店铺 IDpurchase_role = "purchase_for_subordinate"total_amount = 10000actual_paid_amount = 8000
  • 15.4 使用 PostgreSQL MCP 查询订单表,验证订单记录正确
  • 15.5 使用 PostgreSQL MCP 查询一级代理钱包,验证余额扣减:balance = 200010000 - 8000
  • 15.6 使用 PostgreSQL MCP 查询钱包流水表,验证流水记录:transaction_subtype = "purchase_for_subordinate"amount = -8000related_shop_id = 二级代理店铺 IDremark 包含二级代理店铺名称
  • 15.7 使用 PostgreSQL MCP 查询套餐使用表,验证套餐已激活
  • 15.8 使用 PostgreSQL MCP 查询佣金表,验证未产生佣金记录(代理代购不产生佣金)

16. 集成测试:平台代购场景(回归测试)

  • 16.1 使用 PostgreSQL MCP 工具创建测试数据代理、IoT 卡shop_id = 代理店铺 ID、套餐分配配置成本价 10000
  • 16.2 使用 Postman/curl 调用后台订单创建 API平台账号创建订单支付方式 offline选择代理的卡和套餐
  • 16.3 验证响应:payment_status = 2operator_id = NULLoperator_type = "platform"buyer_id = 代理店铺 IDpurchase_role = "purchased_by_platform"total_amount = 10000actual_paid_amount = NULL
  • 16.4 使用 PostgreSQL MCP 查询订单表,验证订单记录正确
  • 16.5 使用 PostgreSQL MCP 查询套餐使用表,验证套餐已激活
  • 16.6 验证平台代购逻辑未被破坏(不扣款、立即激活、产生佣金)

17. 集成测试:订单查询场景

  • 17.1 使用 PostgreSQL MCP 工具创建测试数据:一级代理、二级代理、多个订单(自购、代购、被代购)
  • 17.2 使用 Postman/curl 调用后台订单列表 API一级代理账号查询订单列表不指定 purchase_role
  • 17.3 验证响应包含buyer_id = 一级代理的订单 + operator_id = 一级代理的订单
  • 17.4 使用 Postman/curl 调用后台订单列表 API一级代理账号查询订单列表指定 purchase_role=self_purchase
  • 17.5 验证响应只包含自购订单
  • 17.6 使用 Postman/curl 调用后台订单列表 API一级代理账号查询订单列表指定 purchase_role=purchase_for_subordinate
  • 17.7 验证响应只包含为下级代理购买的订单

18. 集成测试:边界场景

  • 18.1 测试钱包余额不足:代理钱包余额 5000创建订单金额 8000验证返回错误"余额不足"
  • 18.2 测试并发扣款:模拟两个请求同时为同一钱包扣款,验证乐观锁生效,只有一个请求成功
  • 18.3 测试幂等性:同一买家对同一载体的同一套餐组合短时间内重复创建订单,验证返回相同订单 ID不重复扣款
  • 18.4 测试 H5 端 wallet 订单:使用 H5 端 API 创建 wallet 订单,验证订单状态为待支付(payment_status = 1不影响后台逻辑

19. 数据回填(可选)

  • 19.1 编写数据回填脚本,将现有 payment_method = 'offline'is_purchase_on_behalf = true 的订单回填 purchase_role = 'purchased_by_platform'operator_type = 'platform'
  • 19.2 在测试环境执行回填脚本,验证历史订单可正常查询

20. 文档更新

  • 20.1 更新接口文档说明订单创建 API 的行为变更(后台 wallet 支付一步完成)
  • 20.2 更新接口文档说明订单响应新增字段的含义
  • 20.3 更新接口文档说明订单列表 API 新增 purchase_role 查询参数

21. 生产环境部署准备

  • 21.1 在测试环境充分验证所有场景通过
  • 21.2 准备生产环境迁移脚本和回滚脚本
  • 21.3 准备灰度发布计划:代码部署 → 观察日志 → 验证核心功能 → 全量发布
  • 21.4 准备监控指标:订单创建成功率、钱包扣款成功率、错误日志(余额不足、并发冲突)