package com.tourguide.modules.order.service; import com.tourguide.common.huifu.RequestSeqUtil; import com.tourguide.modules.order.domain.OrderStatus; import com.tourguide.modules.order.domain.PaymentMethod; 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.entity.TourOrderRefund; import com.tourguide.modules.order.mapper.TourOrderMapper; import com.tourguide.modules.order.mapper.TourOrderRefundMapper; import com.tourguide.modules.order.payment.PaymentRefundGateway; import com.tourguide.modules.finance.service.GuideBalanceSettlementService; import org.springframework.beans.factory.annotation.Autowired; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDateTime; @Service public class OrderRefundService { private static final Logger log = LoggerFactory.getLogger(OrderRefundService.class); private final TourOrderMapper orderMapper; private final PaymentRefundGateway paymentRefundGateway; private final TourOrderRefundMapper refundMapper; private final GuideBalanceSettlementService balanceSettlementService; public OrderRefundService(TourOrderMapper orderMapper, PaymentRefundGateway paymentRefundGateway) { this(orderMapper, paymentRefundGateway, null, null); } public OrderRefundService(TourOrderMapper orderMapper, PaymentRefundGateway paymentRefundGateway, TourOrderRefundMapper refundMapper) { this(orderMapper, paymentRefundGateway, refundMapper, null); } @Autowired public OrderRefundService(TourOrderMapper orderMapper, PaymentRefundGateway paymentRefundGateway, TourOrderRefundMapper refundMapper, GuideBalanceSettlementService balanceSettlementService) { this.orderMapper = orderMapper; this.paymentRefundGateway = paymentRefundGateway; this.refundMapper = refundMapper; this.balanceSettlementService = balanceSettlementService; } public boolean requestRefund(TourOrder order) { if (order == null) return false; BigDecimal refundAmount = RefundAmountCalculator.refundableAmount(order); return requestRefund(order, refundAmount); } public boolean requestRefund(TourOrder order, BigDecimal refundAmount) { if (order == null || order.getPaymentTime() == null) { return false; } if (RefundStatus.REFUNDED.matches(order.getRefundStatus()) || OrderStatus.REFUNDED.matches(order.getStatus())) { return true; } if (RefundStatus.REFUNDING.matches(order.getRefundStatus()) && StringUtils.hasText(order.getRefundOrderNo())) { return true; } if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { return false; } BigDecimal refundableAmount = RefundAmountCalculator.refundableAmount(order); if (refundAmount.compareTo(refundableAmount) > 0) { return false; } order.setRefundOrderNo(RequestSeqUtil.generateForBusiness("RF")); order.setRefundStatus(RefundStatus.REFUNDING.getCode()); order.setRefundAmount(refundAmount); order.setRefundMethod(order.getPaymentMethod()); TourOrderRefund refundRecord = createRefundRecord(order, refundAmount); if (PaymentMethod.TEMPORARY_BYPASS.matches(order.getPaymentMethod())) { if (order.getCancelledAt() == null) order.setCancelledAt(LocalDateTime.now()); completeLocalRefund(order, refundAmount); LocalDateTime refundTime = LocalDateTime.now(); order.setRefundTime(refundTime); // 明细状态描述当前这一笔退款;订单状态则描述累计退款进度。 updateRefundRecord(refundRecord, RefundStatus.REFUNDED.getCode(), refundTime); orderMapper.updateById(order); return true; } orderMapper.updateById(order); try { String refundRequestNo = paymentRefundGateway.refund(order, refundAmount); if (!StringUtils.hasText(refundRequestNo)) { throw new IllegalStateException("Payment provider did not accept refund request"); } if (!OrderStatus.CANCELLED.matches(order.getStatus())) { order.setStatus(OrderStatus.CANCELLED.getCode()); } if (order.getCancelledAt() == null) order.setCancelledAt(LocalDateTime.now()); orderMapper.updateById(order); return true; } catch (RuntimeException exception) { order.setRefundStatus(RefundStatus.REFUND_FAILED.getCode()); updateRefundRecord(refundRecord, RefundStatus.REFUND_FAILED.getCode(), null); orderMapper.updateById(order); log.error("Order refund request failed, orderId={}, orderNo={}, refundOrderNo={}", order.getId(), order.getOrderNo(), order.getRefundOrderNo(), exception); return false; } } private void completeLocalRefund(TourOrder order, BigDecimal refundAmount) { BigDecimal previousRefundedAmount = RefundAmountCalculator.refundedAmount(order); BigDecimal refundedAmount = previousRefundedAmount.add(refundAmount); order.setRefundedAmount(refundedAmount); if (refundedAmount.compareTo(RefundAmountCalculator.paidAmount(order)) >= 0) { order.setRefundStatus(RefundStatus.REFUNDED.getCode()); order.setStatus(OrderStatus.REFUNDED.getCode()); } else { order.setRefundStatus(RefundStatus.PARTIAL.getCode()); order.setStatus(OrderStatus.CANCELLED.getCode()); } reverseSettledEarning(order, previousRefundedAmount, refundedAmount); } private void reverseSettledEarning(TourOrder order, BigDecimal previousRefundedAmount, BigDecimal currentRefundedAmount) { if (balanceSettlementService != null) { balanceSettlementService.reverseRefund(order, previousRefundedAmount, currentRefundedAmount); } } private TourOrderRefund createRefundRecord(TourOrder order, BigDecimal refundAmount) { if (refundMapper == null || order.getId() == null) return null; TourOrderRefund refund = new TourOrderRefund(); refund.setOrderId(order.getId()); refund.setRefundOrderNo(order.getRefundOrderNo()); refund.setRefundAmount(refundAmount); refund.setRefundStatus(RefundStatus.REFUNDING.getCode()); refund.setRefundMethod(order.getRefundMethod()); refundMapper.insert(refund); return refund; } private void updateRefundRecord(TourOrderRefund refund, String status, LocalDateTime refundTime) { if (refundMapper == null || refund == null) return; refund.setRefundStatus(status); refund.setRefundTime(refundTime); refundMapper.updateById(refund); } }