修复佣金错误计算的问题
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m15s

This commit is contained in:
Break
2026-06-03 10:25:49 +08:00
parent 36e68e6672
commit 46ede81aef
2 changed files with 34 additions and 8 deletions

View File

@@ -228,7 +228,7 @@ default:
- **B 端认证系统**:完整的后台和 H5 认证功能,支持基于 Redis 的 Token 管理和双令牌机制Access Token 24h + Refresh Token 7天包含登录、登出、Token 刷新、用户信息查询和密码修改功能通过用户类型隔离确保后台SuperAdmin、Platform、Agent和 H5Agent、Enterprise的访问控制详见 [API 文档](docs/api/auth.md)、[使用指南](docs/auth-usage-guide.md) 和 [架构说明](docs/auth-architecture.md) - **B 端认证系统**:完整的后台和 H5 认证功能,支持基于 Redis 的 Token 管理和双令牌机制Access Token 24h + Refresh Token 7天包含登录、登出、Token 刷新、用户信息查询和密码修改功能通过用户类型隔离确保后台SuperAdmin、Platform、Agent和 H5Agent、Enterprise的访问控制详见 [API 文档](docs/api/auth.md)、[使用指南](docs/auth-usage-guide.md) 和 [架构说明](docs/auth-architecture.md)
- **生命周期管理**:物联网卡/号卡的开卡、激活、停机、复机、销户 - **生命周期管理**:物联网卡/号卡的开卡、激活、停机、复机、销户
- **代理商体系**:层级管理和分佣结算,支持差价佣金和一次性佣金两种佣金类型,详见 [套餐与佣金业务模型](docs/commission-package-model.md) - **代理商体系**:层级管理和分佣结算,支持差价佣金和一次性佣金两种佣金类型,详见 [套餐与佣金业务模型](docs/commission-package-model.md)
- **代理开放接口**:新增 `/api/open/v1` 签名接口,代理店铺第三方系统可调用卡流量、卡状态、实名状态、套餐列表、预充值钱包余额/流水和钱包套餐购买能力。详见 [对接说明](docs/agent-open-api/功能总结.md) - **代理开放接口**:新增 `/api/open/v1` 签名接口,代理店铺第三方系统可调用卡流量、卡状态、实名状态、套餐列表、预充值钱包余额/流水和钱包套餐购买能力。详见 [对接说明](docs/agent-open-api/功能总结.md) 与 [误发差价佣金修复说明](docs/agent-open-api/开放接口误发差价佣金修复说明.md)
- **批量同步**:卡状态、实名状态、流量使用情况 - **批量同步**:卡状态、实名状态、流量使用情况
- **轮询系统**IoT 卡实名状态、流量使用、套餐余额的定时轮询检查;支持配置化轮询策略、动态并发控制、告警系统、数据清理和手动触发功能;详见 [轮询系统文档](docs/polling-system/README.md) - **轮询系统**IoT 卡实名状态、流量使用、套餐余额的定时轮询检查;支持配置化轮询策略、动态并发控制、告警系统、数据清理和手动触发功能;详见 [轮询系统文档](docs/polling-system/README.md)
- **套餐系统升级**:完整的套餐生命周期管理,支持主套餐排队激活、加油包绑定主套餐、囤货待实名激活、流量按优先级扣减、自然月/按天有效期计算、日/月/年流量重置、客户端流量查询和套餐流量详单;详见 [套餐系统升级文档](docs/package-system-upgrade/) - **套餐系统升级**:完整的套餐生命周期管理,支持主套餐排队激活、加油包绑定主套餐、囤货待实名激活、流量按优先级扣减、自然月/按天有效期计算、日/月/年流量重置、客户端流量查询和套餐流量详单;详见 [套餐系统升级文档](docs/package-system-upgrade/)

View File

@@ -141,13 +141,17 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
return nil, errors.Wrap(errors.CodeDatabaseError, err, "获取销售店铺失败") return nil, errors.Wrap(errors.CodeDatabaseError, err, "获取销售店铺失败")
} }
// 卖家利润 = 实际收款 - 成本价,必须用 ActualPaidAmount 而非 TotalAmount sellerProfit, shouldCalculateSellerProfit := calculateSellerProfit(order)
// TotalAmount 在 fix-order-price-semantics 后始终存零售价,代理自购时会错误产生佣金 if !shouldCalculateSellerProfit {
var sellerProfit int64 s.logger.Warn("订单缺少实际支付金额,跳过终端销售代理成本价差佣金计算",
if order.ActualPaidAmount != nil { zap.Uint("order_id", order.ID),
sellerProfit = *order.ActualPaidAmount - order.SellerCostPrice zap.String("source", order.Source),
} else { zap.String("payment_method", order.PaymentMethod),
sellerProfit = order.TotalAmount - order.SellerCostPrice zap.String("buyer_type", order.BuyerType),
zap.String("purchase_role", order.PurchaseRole),
zap.Int64("total_amount", order.TotalAmount),
zap.Int64("seller_cost_price", order.SellerCostPrice),
)
} }
if sellerProfit > 0 { if sellerProfit > 0 {
records = append(records, &model.CommissionRecord{ records = append(records, &model.CommissionRecord{
@@ -232,6 +236,28 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
return records, nil return records, nil
} }
// calculateSellerProfit 计算终端销售代理的成本价差收益。
// 代理钱包/代购链路必须以 actual_paid_amount 为准,禁止回退到 total_amount
// 否则在 total_amount 已改成零售价后会再次产生“建议售价-成本价”的错误佣金。
func calculateSellerProfit(order *model.Order) (int64, bool) {
if order == nil {
return 0, false
}
if order.ActualPaidAmount != nil {
return *order.ActualPaidAmount - order.SellerCostPrice, true
}
if order.BuyerType == model.BuyerTypeAgent ||
order.PaymentMethod == model.PaymentMethodWallet ||
order.IsPurchaseOnBehalf ||
order.Source == constants.OrderSourceAdmin {
return 0, false
}
return order.TotalAmount - order.SellerCostPrice, true
}
func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *gorm.DB, order *model.Order, cardID uint) error { func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *gorm.DB, order *model.Order, cardID uint) error {
if order.IsPurchaseOnBehalf || order.Source != constants.OrderSourceClient { if order.IsPurchaseOnBehalf || order.Source != constants.OrderSourceClient {
return nil return nil