f
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user