Files
huang d977000a66
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m40s
feat: 归档佣金计算触发和快照变更,同步规范文档
- 归档 OpenSpec 变更到 archive 目录
- 创建 2 个新的主规范文件:commission-trigger 和 order-commission-snapshot
- 实现订单佣金快照字段和支付自动触发
- 确保事务一致性,所有佣金操作在同一事务内完成
- 提取成本价计算为公共工具函数
2026-01-29 14:58:35 +08:00

4.7 KiB
Raw Permalink Blame History

commission-trigger Specification

Purpose

TBD - created by archiving change fix-commission-calculation-trigger-and-snapshot. Update Purpose after archive.

Requirements

Requirement: 支付成功后自动入队佣金计算任务

系统 SHALL 在订单首次支付成功时自动 enqueue 佣金计算异步任务(commission:calculate),确保佣金及时发放。

触发条件

  • 订单从"待支付"变为"已支付"(首次成功支付)
  • 订单 commission_statuspending(未计算)

任务参数

  • 任务类型:commission:calculate
  • Payload{"order_id": <订单ID>}

Scenario: 首次支付成功触发计算

  • WHEN 订单支付成功,订单状态从"待支付"变为"已支付"
  • THEN 系统自动 enqueue commission:calculate 任务payload 包含订单 ID
  • AND 订单 commission_status 保持为 pending(任务执行后才更新为 calculated

Scenario: 重复支付不重复触发

  • WHEN 订单已经是"已支付"状态,再次收到支付成功通知(幂等场景)
  • THEN 系统不重复 enqueue 佣金计算任务
  • AND 日志记录"订单已支付,跳过重复入队"

Scenario: 已计算佣金的订单不触发

  • WHEN 订单 commission_statuscalculated(已计算)
  • THEN 系统跳过入队操作
  • AND 日志记录"订单佣金已计算,跳过入队"

Requirement: 入队失败不影响支付主链路

系统 SHALL 确保佣金任务入队失败时不回滚订单支付成功状态,保障主业务链路稳定。

失败处理策略

  • 入队失败时记录 ERROR 级别日志(包含订单 ID、失败原因
  • 订单状态保持为"已支付",不回滚
  • 订单 commission_status 保持为 pending,允许后续补偿

补偿机制

  • 后台补偿任务扫描 commission_status=pending 且已支付的订单
  • 人工触发佣金计算(后台接口)
  • 定时任务重试入队(可选)

Scenario: 入队失败记录日志

  • WHEN 佣金任务入队失败(队列服务不可用或网络超时)
  • THEN 系统记录 ERROR 日志,包含订单 ID、失败原因、队列配置信息
  • AND 订单支付状态保持为"已支付",不回滚

Scenario: 失败后允许补偿

  • WHEN 后台补偿任务扫描到 commission_status=pendingpayment_status=paid 的订单
  • THEN 系统可重新 enqueue 佣金计算任务或直接执行计算
  • AND 避免佣金永久丢失

Requirement: 佣金计算任务幂等性

系统 SHALL 确保佣金计算任务可重复执行,不重复发放佣金。

幂等检查

  • 任务执行前检查订单 commission_status
  • 如果已为 calculated,跳过计算并返回成功

状态更新

  • 计算完成后将订单 commission_status 更新为 calculated
  • 状态更新与佣金记录创建在同一事务中

Scenario: 任务重复执行跳过计算

  • WHEN 佣金计算任务执行时,订单 commission_status 已为 calculated
  • THEN 系统跳过佣金计算和钱包入账操作
  • AND 任务返回成功(避免 Asynq 重试)
  • AND 日志记录"订单佣金已计算,跳过执行"

Scenario: 并发任务只有一个成功

  • WHEN 同一订单的佣金计算任务被重复入队,两个 worker 并发执行
  • THEN 第一个任务成功完成计算并更新状态为 calculated
  • AND 第二个任务检查到状态已为 calculated,跳过计算

Scenario: 任务失败可安全重试

  • WHEN 佣金计算任务执行失败(数据库异常、钱包服务不可用)
  • THEN Asynq 自动重试任务
  • AND 重试时幂等检查确保不重复发放佣金

Requirement: 队列客户端依赖注入

系统 SHALL 通过依赖注入方式将队列客户端注入到订单服务,遵循现有 bootstrap 架构。

注入位置

  • internal/service/order/service.goService 结构体
  • 添加 queueClient *asynq.Client 字段

注入方式

  • internal/bootstrap/services.go 中初始化订单服务时传入队列客户端
  • 队列客户端在 bootstrap.Bootstrap() 中统一创建

Scenario: 订单服务接收队列客户端

  • WHEN 系统启动时执行 bootstrap.Bootstrap()
  • THEN 订单服务(order.Service)通过构造函数接收队列客户端实例
  • AND 队列客户端可在服务内部调用 Enqueue() 方法

Scenario: 支付成功时调用队列客户端

  • WHEN 订单支付成功,订单服务执行入队操作
  • THEN 系统通过注入的队列客户端调用 Enqueue("commission:calculate", payload)
  • AND 不在服务内部直接创建队列客户端(遵循依赖注入原则)