package com.tourguide.modules.order.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tourguide.modules.operation.service.PlatformSettingsService;
|
import com.tourguide.modules.order.domain.OrderStatus;
|
import com.tourguide.modules.order.domain.RefundStatus;
|
import com.tourguide.modules.order.entity.TourOrder;
|
import com.tourguide.modules.order.mapper.TourOrderMapper;
|
import com.tourguide.modules.order.payment.PaymentRefundGateway;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.util.List;
|
|
/** 处理已支付但长时间无人接单的订单自动退款。 */
|
@Service
|
public class OrderAutoRefundService {
|
private static final Logger log = LoggerFactory.getLogger(OrderAutoRefundService.class);
|
private static final int BATCH_SIZE = 100;
|
|
private final TourOrderMapper orderMapper;
|
private final PlatformSettingsService platformSettingsService;
|
private final PaymentRefundGateway paymentRefundGateway;
|
|
public OrderAutoRefundService(TourOrderMapper orderMapper,
|
PlatformSettingsService platformSettingsService,
|
PaymentRefundGateway paymentRefundGateway) {
|
this.orderMapper = orderMapper;
|
this.platformSettingsService = platformSettingsService;
|
this.paymentRefundGateway = paymentRefundGateway;
|
}
|
|
@Scheduled(fixedDelayString = "${tourguide.order.auto-refund-scan-delay-millis:10000}")
|
@Transactional
|
public void refundExpiredUnacceptedOrders() {
|
Integer configuredMinutes = platformSettingsService.getSettings().getUnacceptedAutoRefundMinutes();
|
int timeoutMinutes = configuredMinutes == null || configuredMinutes < 1 ? 5 : configuredMinutes;
|
LocalDateTime expiredBefore = LocalDateTime.now().minusMinutes(timeoutMinutes);
|
List<TourOrder> orders = orderMapper.selectList(
|
new LambdaQueryWrapper<TourOrder>()
|
.eq(TourOrder::getStatus, OrderStatus.PENDING_ACCEPT.getCode())
|
.isNotNull(TourOrder::getPaymentTime)
|
.le(TourOrder::getPaymentTime, expiredBefore)
|
.and(wrapper -> wrapper.isNull(TourOrder::getRefundStatus)
|
.or().eq(TourOrder::getRefundStatus, RefundStatus.NONE.getCode()))
|
.orderByAsc(TourOrder::getPaymentTime)
|
.last("LIMIT " + BATCH_SIZE));
|
for (TourOrder order : orders) {
|
refundIfStillUnaccepted(order.getId());
|
}
|
}
|
|
void refundIfStillUnaccepted(Long orderId) {
|
if (orderMapper.claimUnacceptedOrderForRefund(orderId) != 1) {
|
return;
|
}
|
TourOrder order = orderMapper.selectById(orderId);
|
BigDecimal refundAmount = order.getPaymentAmount() == null
|
? order.getAmount() : order.getPaymentAmount();
|
try {
|
String refundOrderNo = paymentRefundGateway.refund(order, refundAmount);
|
order.setRefundStatus(RefundStatus.REFUNDED.getCode());
|
order.setRefundAmount(refundAmount);
|
order.setRefundTime(LocalDateTime.now());
|
order.setRefundOrderNo(refundOrderNo);
|
order.setRefundMethod(order.getPaymentMethod());
|
} catch (RuntimeException exception) {
|
order.setRefundStatus(RefundStatus.REFUND_FAILED.getCode());
|
log.error("订单自动退款失败,orderId={}, orderNo={}", order.getId(), order.getOrderNo(), exception);
|
}
|
orderMapper.updateById(order);
|
}
|
}
|