This commit is contained in:
2026-05-13 14:43:10 +08:00
parent 2312f54e1e
commit 3399de0dc5
49 changed files with 1637 additions and 287 deletions

View File

@@ -11,6 +11,7 @@ import (
"qnc-server/common/globalkey"
"qnc-server/pkg/lzkit/lzUtils"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
@@ -120,7 +121,7 @@ func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
// 5.2 使用产品配置的底价计算实际底价
basePrice := productConfig.BasePrice
actualBasePrice := basePrice + float64(levelBonus)
actualBasePrice := lzUtils.RoundMoney(basePrice + float64(levelBonus))
// 5.3 计算提价成本(使用产品配置)
priceThreshold := 0.0
@@ -134,7 +135,7 @@ func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
priceCost := s.calculatePriceCost(agentOrder.SetPrice, priceThreshold, priceFeeRate)
// 5.4 计算代理收益
agentProfit := agentOrder.SetPrice - actualBasePrice - priceCost
agentProfit := lzUtils.RoundMoney(agentOrder.SetPrice - actualBasePrice - priceCost)
// 5.5 更新代理订单记录
agentOrder.ProcessStatus = 1
@@ -200,7 +201,7 @@ func (s *AgentService) calculatePriceCost(setPrice, priceThreshold, priceFeeRate
if setPrice <= priceThreshold {
return 0
}
return (setPrice - priceThreshold) * priceFeeRate
return lzUtils.RoundMoney((setPrice - priceThreshold) * priceFeeRate)
}
// giveAgentCommission 发放代理佣金
@@ -244,7 +245,7 @@ func (s *AgentService) giveAgentCommission(ctx context.Context, session sqlx.Ses
}
// 计算冻结金额订单单价的10%
freezeAmountByPrice := orderPrice * freezeRatio
freezeAmountByPrice := lzUtils.RoundMoney(orderPrice * freezeRatio)
// 冻结金额不能超过佣金金额
if freezeAmountByPrice > amount {
@@ -295,7 +296,7 @@ func (s *AgentService) giveAgentCommission(ctx context.Context, session sqlx.Ses
}
// 实际到账金额 = 佣金金额 - 冻结金额
actualAmount := amount - freezeAmount
actualAmount := lzUtils.RoundMoney(amount - freezeAmount)
wallet.Balance += actualAmount
wallet.FrozenBalance += freezeAmount
@@ -431,7 +432,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
// ========== 步骤2计算剩余金额并分配给钻石上级 ==========
// 剩余金额 = 总等级加成 - 已给黄金上级的金额
// 例如等级加成6元 - 给黄金上级3元 = 剩余3元
remaining := amount - goldAmount
remaining := lzUtils.RoundMoney(amount - goldAmount)
if remaining > 0 {
// 从黄金上级开始向上查找钻石上级
// 场景示例:
@@ -481,7 +482,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
// ========== 步骤2计算剩余金额 ==========
// 剩余金额 = 总等级加成 - 已给直接上级的金额
// 例如等级加成6元 - 给直接上级2元 = 剩余4元
remaining := amount - directAmount
remaining := lzUtils.RoundMoney(amount - directAmount)
if remaining <= 0 {
// 如果没有剩余,直接返回(所有金额已分配给直接上级)
return nil
@@ -524,8 +525,8 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
logx.Errorf("获取黄金等级加成配置失败使用默认值3元: %v", err)
goldBonus = 3 // 默认3元
}
// 计算等级加成的差
bonusDiff := normalBonus - float64(goldBonus) // 例如6元 - 3元 = 3元
// 计算等级加成的差
bonusDiff := lzUtils.RoundMoney(normalBonus - float64(goldBonus)) // 例如6元 - 3元 = 3元
// 步骤A2如果有黄金上级给黄金上级一部分返佣
// 规则说明:
@@ -536,7 +537,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
if goldParent != nil && bonusDiff > 0 {
// 计算给黄金上级的金额 = 等级加成差 - 已给普通上级的金额
// 例如等级加成差3元 - 已给普通2元 = 给黄金1元
goldRebateAmount := bonusDiff - directAmount
goldRebateAmount := lzUtils.RoundMoney(bonusDiff - directAmount)
// 如果计算出的金额小于等于0说明差已经被普通代理全部占用了不给黄金
// 例如如果差是2元已给普通2元则 goldRebateAmount = 0不给黄金
@@ -554,7 +555,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
// 更新剩余金额(用于后续分配给钻石)
// 例如剩余4元 - 给黄金1元 = 剩余3元给钻石
remaining = remaining - goldRebateAmount
remaining = lzUtils.RoundMoney(remaining - goldRebateAmount)
}
}
@@ -1073,3 +1074,150 @@ func (s *AgentService) GetUpgradeRebate(ctx context.Context, fromLevel, toLevel
}
return 0, nil
}
// ReverseAgentSettlementOnOrderRefund 订单退款成功后冲正本单代理销售佣金、冻结任务与返佣(幂等)。
// 与退款是否部分/全额无关:凡触发本方法,均按本单佣金与返佣记录全额冲回。
// session 非 nil 时与外层共用事务;为 nil 时在独立事务中执行。
func (s *AgentService) ReverseAgentSettlementOnOrderRefund(ctx context.Context, session sqlx.Session, orderID string) error {
run := func(c context.Context, sess sqlx.Session) error {
return s.reverseAgentSettlementInTx(c, sess, orderID)
}
if session != nil {
return run(ctx, session)
}
return s.AgentWalletModel.Trans(ctx, run)
}
func (s *AgentService) reverseAgentSettlementInTx(ctx context.Context, session sqlx.Session, orderID string) error {
cb := s.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{
"order_id": orderID,
"del_state": globalkey.DelStateNo,
})
commissions, err := s.AgentCommissionModel.FindAll(ctx, cb, "")
if err != nil {
return errors.Wrapf(err, "查询订单佣金失败 orderId=%s", orderID)
}
for _, c := range commissions {
if err := s.reverseOneCommissionForRefund(ctx, session, c); err != nil {
return err
}
}
rb := s.AgentRebateModel.SelectBuilder().Where(squirrel.Eq{
"order_id": orderID,
"del_state": globalkey.DelStateNo,
})
rebates, err := s.AgentRebateModel.FindAll(ctx, rb, "")
if err != nil {
return errors.Wrapf(err, "查询订单返佣失败 orderId=%s", orderID)
}
for _, r := range rebates {
if err := s.reverseOneRebateForRefund(ctx, session, r); err != nil {
return err
}
}
return nil
}
func (s *AgentService) reverseOneCommissionForRefund(ctx context.Context, session sqlx.Session, commission *model.AgentCommission) error {
if commission.Status == 3 {
return nil
}
A := lzUtils.RoundMoney(commission.Amount)
if A <= 0 {
commission.Status = 3
return s.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
}
tb := s.AgentFreezeTaskModel.SelectBuilder().Where(squirrel.Eq{
"commission_id": commission.Id,
"del_state": globalkey.DelStateNo,
})
tasks, err := s.AgentFreezeTaskModel.FindAll(ctx, tb, "")
if err != nil {
return errors.Wrapf(err, "查询佣金冻结任务失败 commissionId=%s", commission.Id)
}
var takeFromFrozen float64
for _, ft := range tasks {
if ft.Status == 1 {
takeFromFrozen = lzUtils.RoundMoney(takeFromFrozen + ft.FreezeAmount)
ft.Status = 3
if err := s.AgentFreezeTaskModel.UpdateWithVersion(ctx, session, ft); err != nil {
return errors.Wrapf(err, "取消冻结任务失败 freezeTaskId=%s", ft.Id)
}
}
}
takeFromFrozen = lzUtils.RoundMoney(takeFromFrozen)
wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId)
if err != nil {
return errors.Wrapf(err, "冲正佣金时查询钱包失败 agentId=%s", commission.AgentId)
}
if takeFromFrozen > 0 {
if wallet.FrozenBalance < takeFromFrozen {
takeFromFrozen = lzUtils.RoundMoney(wallet.FrozenBalance)
}
wallet.FrozenBalance = lzUtils.RoundMoney(wallet.FrozenBalance - takeFromFrozen)
}
takeFromBalance := lzUtils.RoundMoney(A - takeFromFrozen)
if takeFromBalance < 0 {
takeFromBalance = 0
}
// 可用余额不足时允许扣成负数(例如代理已提现),退款侧事务仍可完成
wallet.Balance = lzUtils.RoundMoney(wallet.Balance - takeFromBalance)
if wallet.Balance < 0 {
logx.Infof("退款冲正佣金后代理可用余额为负: agentId=%s, orderId=%s, balance=%.2f",
commission.AgentId, commission.OrderId, wallet.Balance)
}
wallet.TotalEarnings = lzUtils.RoundMoney(wallet.TotalEarnings - A)
if wallet.TotalEarnings < 0 {
wallet.TotalEarnings = 0
}
if err := s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(err, "冲正佣金更新钱包失败 agentId=%s", commission.AgentId)
}
commission.Status = 3
if err := s.AgentCommissionModel.UpdateWithVersion(ctx, session, commission); err != nil {
return errors.Wrapf(err, "更新佣金状态为已取消失败 commissionId=%s", commission.Id)
}
return nil
}
func (s *AgentService) reverseOneRebateForRefund(ctx context.Context, session sqlx.Session, rebate *model.AgentRebate) error {
if rebate.Status == 3 {
return nil
}
amt := lzUtils.RoundMoney(rebate.RebateAmount)
if amt <= 0 {
rebate.Status = 3
return s.AgentRebateModel.UpdateWithVersion(ctx, session, rebate)
}
wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, rebate.AgentId)
if err != nil {
return errors.Wrapf(err, "冲正返佣时查询钱包失败 agentId=%s", rebate.AgentId)
}
// 可用余额不足时允许扣成负数(例如代理已提现)
wallet.Balance = lzUtils.RoundMoney(wallet.Balance - amt)
if wallet.Balance < 0 {
logx.Infof("退款冲正返佣后代理可用余额为负: agentId=%s, orderId=%s, balance=%.2f",
rebate.AgentId, rebate.OrderId, wallet.Balance)
}
wallet.TotalEarnings = lzUtils.RoundMoney(wallet.TotalEarnings - amt)
if wallet.TotalEarnings < 0 {
wallet.TotalEarnings = 0
}
if err := s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(err, "冲正返佣更新钱包失败 agentId=%s", rebate.AgentId)
}
rebate.Status = 3
if err := s.AgentRebateModel.UpdateWithVersion(ctx, session, rebate); err != nil {
return errors.Wrapf(err, "更新返佣状态为已取消失败 rebateId=%s", rebate.Id)
}
return nil
}