From 15defa991fcd30b9fa60bab17d2230b5929bf2a2 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期一, 14 九月 2026 17:20:20 +0800
Subject: [PATCH] update

---
 backend/src/main/java/com/tourguide/modules/finance/service/GuideBalanceSettlementService.java |   64 ++++++++++++++++++++++++++++++-
 1 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/backend/src/main/java/com/tourguide/modules/finance/service/GuideBalanceSettlementService.java b/backend/src/main/java/com/tourguide/modules/finance/service/GuideBalanceSettlementService.java
index 215d7bd..72b9a9c 100644
--- a/backend/src/main/java/com/tourguide/modules/finance/service/GuideBalanceSettlementService.java
+++ b/backend/src/main/java/com/tourguide/modules/finance/service/GuideBalanceSettlementService.java
@@ -2,6 +2,7 @@
 
 import com.tourguide.common.exception.BusinessException;
 import com.tourguide.modules.order.domain.OrderStatus;
+import com.tourguide.modules.order.domain.RefundAmountCalculator;
 import com.tourguide.modules.order.domain.RefundStatus;
 import com.tourguide.modules.order.entity.TourOrder;
 import com.tourguide.modules.order.mapper.TourOrderMapper;
@@ -9,6 +10,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 
 /** 将到期且未分账的订单收益幂等计入导游余额。 */
@@ -28,15 +30,46 @@
         TourOrder order = orderMapper.selectForUpdate(orderId);
         if (!canSettle(order, settledAt)) return false;
 
-        BigDecimal earning = order.getGuideEarningAmount();
+        BigDecimal earning = effectiveEarning(order);
         if (earning.signum() > 0) {
-            balanceService.credit(order.getGuideId(), earning);
+            if (order.getCommissionTeamId() != null) {
+                balanceService.creditTeam(order.getCommissionTeamId(), earning);
+            } else {
+                balanceService.credit(order.getGuideId(), earning);
+            }
         }
         order.setSettledAt(settledAt);
         if (orderMapper.updateById(order) != 1) {
             throw new BusinessException(500, "订单分账状态更新失败");
         }
         return true;
+    }
+
+    /** 退款成功后冲正已入账收益;按累计退款差额计算,保证部分退款幂等。 */
+    @Transactional
+    public void reverseRefund(TourOrder order, BigDecimal previousRefundedAmount,
+                              BigDecimal currentRefundedAmount) {
+        if (order == null || order.getSettledAt() == null
+                || order.getGuideEarningAmount() == null) return;
+        BigDecimal paidAmount = RefundAmountCalculator.paidAmount(order);
+        if (paidAmount.signum() <= 0) return;
+        BigDecimal previous = nonNegative(previousRefundedAmount);
+        BigDecimal current = nonNegative(currentRefundedAmount).min(paidAmount);
+        BigDecimal previousReversal = proratedEarning(order.getGuideEarningAmount(), previous, paidAmount);
+        BigDecimal currentReversal = proratedEarning(order.getGuideEarningAmount(), current, paidAmount);
+        BigDecimal reversal = currentReversal.subtract(previousReversal).setScale(2, RoundingMode.HALF_UP);
+        if (reversal.signum() <= 0) return;
+        boolean debited;
+        if (order.getCommissionTeamId() != null) {
+            debited = balanceService.debitTeamForRefund(order.getCommissionTeamId(), reversal);
+        } else if (order.getGuideId() != null) {
+            debited = balanceService.debitForRefund(order.getGuideId(), reversal);
+        } else {
+            return;
+        }
+        if (!debited) {
+            throw new BusinessException(500, "退款后导游收益冲正失败");
+        }
     }
 
     private boolean canSettle(TourOrder order, LocalDateTime now) {
@@ -48,6 +81,31 @@
                 && order.getGuideId() != null
                 && order.getGuideEarningAmount() != null
                 && order.getGuideEarningAmount().signum() >= 0
-                && !RefundStatus.REFUNDED.matches(order.getRefundStatus());
+                && canSettleRefund(order.getRefundStatus());
+    }
+
+    private BigDecimal effectiveEarning(TourOrder order) {
+        BigDecimal earning = order.getGuideEarningAmount();
+        BigDecimal paidAmount = RefundAmountCalculator.paidAmount(order);
+        if (paidAmount.signum() <= 0) return earning;
+        BigDecimal refunded = RefundAmountCalculator.refundedAmount(order).min(paidAmount);
+        return earning.multiply(paidAmount.subtract(refunded))
+                .divide(paidAmount, 2, RoundingMode.HALF_UP);
+    }
+
+    private BigDecimal proratedEarning(BigDecimal earning, BigDecimal refunded, BigDecimal paidAmount) {
+        return earning.multiply(refunded).divide(paidAmount, 2, RoundingMode.HALF_UP);
+    }
+
+    private BigDecimal nonNegative(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value.max(BigDecimal.ZERO);
+    }
+
+    private boolean canSettleRefund(String refundStatus) {
+        return refundStatus == null
+                || refundStatus.trim().isEmpty()
+                || RefundStatus.NONE.matches(refundStatus)
+                || RefundStatus.PARTIAL.matches(refundStatus)
+                || RefundStatus.REFUND_FAILED.matches(refundStatus);
     }
 }

--
Gitblit v1.7.1