diff --git a/README.md b/README.md index 3ca57a9..2d19ddb 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ default: - **B 端认证系统**:完整的后台和 H5 认证功能,支持基于 Redis 的 Token 管理和双令牌机制(Access Token 24h + Refresh Token 7天);包含登录、登出、Token 刷新、用户信息查询和密码修改功能;通过用户类型隔离确保后台(SuperAdmin、Platform、Agent)和 H5(Agent、Enterprise)的访问控制;详见 [API 文档](docs/api/auth.md)、[使用指南](docs/auth-usage-guide.md) 和 [架构说明](docs/auth-architecture.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) - **套餐系统升级**:完整的套餐生命周期管理,支持主套餐排队激活、加油包绑定主套餐、囤货待实名激活、流量按优先级扣减、自然月/按天有效期计算、日/月/年流量重置、客户端流量查询和套餐流量详单;详见 [套餐系统升级文档](docs/package-system-upgrade/) diff --git a/internal/service/commission_calculation/service.go b/internal/service/commission_calculation/service.go index 8f9c28e..ad73db5 100644 --- a/internal/service/commission_calculation/service.go +++ b/internal/service/commission_calculation/service.go @@ -141,13 +141,17 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model. return nil, errors.Wrap(errors.CodeDatabaseError, err, "获取销售店铺失败") } - // 卖家利润 = 实际收款 - 成本价,必须用 ActualPaidAmount 而非 TotalAmount - // TotalAmount 在 fix-order-price-semantics 后始终存零售价,代理自购时会错误产生佣金 - var sellerProfit int64 - if order.ActualPaidAmount != nil { - sellerProfit = *order.ActualPaidAmount - order.SellerCostPrice - } else { - sellerProfit = order.TotalAmount - order.SellerCostPrice + sellerProfit, shouldCalculateSellerProfit := calculateSellerProfit(order) + if !shouldCalculateSellerProfit { + s.logger.Warn("订单缺少实际支付金额,跳过终端销售代理成本价差佣金计算", + zap.Uint("order_id", order.ID), + zap.String("source", order.Source), + zap.String("payment_method", order.PaymentMethod), + 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 { records = append(records, &model.CommissionRecord{ @@ -232,6 +236,28 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model. 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 { if order.IsPurchaseOnBehalf || order.Source != constants.OrderSourceClient { return nil