feat(payment): 实现微信支付退款及状态查询功能
- 新增微信支付退款接口实现,支持全额或部分退款 - 实现微信退款状态查询接口,用于轮询退款进度 - 添加退款状态转换逻辑,映射微信状态到系统内部状态 - 创建定时任务定期查询处理中的退款订单并更新状态 - 在订单控制器中集成退款操作,支持手动触发退款 - 完善退款参数校验和异常处理机制 - 添加退款日志记录,便于追踪退款流程和问题排查
This commit is contained in:
@@ -18,6 +18,8 @@ import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
|
|||||||
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
|
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
|
||||||
import com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest;
|
import com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest;
|
||||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
|
import com.wechat.pay.java.service.refund.RefundService;
|
||||||
|
import com.wechat.pay.java.service.refund.model.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@@ -196,14 +198,81 @@ public class WechatNativeStrategy implements PaymentStrategy {
|
|||||||
@Override
|
@Override
|
||||||
public PaymentResponse refund(String orderNo, String refundNo, BigDecimal totalAmount,
|
public PaymentResponse refund(String orderNo, String refundNo, BigDecimal totalAmount,
|
||||||
BigDecimal refundAmount, String reason, Integer tenantId) throws PaymentException {
|
BigDecimal refundAmount, String reason, Integer tenantId) throws PaymentException {
|
||||||
// TODO: 实现微信支付退款逻辑
|
log.info("{}, 支付类型: {}, 订单号: {}, 退款单号: {}, 退款金额: {}, 租户ID: {}",
|
||||||
throw PaymentException.unsupportedPayment("暂不支持微信支付退款", PaymentType.WECHAT_NATIVE);
|
PaymentConstants.LogMessage.REFUND_START, getSupportedPaymentType(),
|
||||||
|
orderNo, refundNo, refundAmount, tenantId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 参数验证
|
||||||
|
validateRefundRequest(orderNo, refundNo, totalAmount, refundAmount, tenantId);
|
||||||
|
|
||||||
|
// 获取支付配置
|
||||||
|
Payment paymentConfig = wxPayConfigService.getPaymentConfigForStrategy(tenantId);
|
||||||
|
Config wxPayConfig = wxPayConfigService.getWxPayConfig(tenantId);
|
||||||
|
|
||||||
|
// 构建退款请求
|
||||||
|
CreateRequest refundRequest = buildRefundRequest(
|
||||||
|
orderNo, refundNo, totalAmount, refundAmount, reason, paymentConfig);
|
||||||
|
|
||||||
|
// 调用微信退款API
|
||||||
|
Refund refundResult = callWechatRefundApi(refundRequest, wxPayConfig);
|
||||||
|
|
||||||
|
// 构建响应
|
||||||
|
PaymentResponse response = buildRefundResponse(refundResult, orderNo, refundNo, tenantId);
|
||||||
|
|
||||||
|
log.info("{}, 支付类型: {}, 订单号: {}, 退款单号: {}, 微信退款单号: {}",
|
||||||
|
PaymentConstants.LogMessage.REFUND_SUCCESS, getSupportedPaymentType(),
|
||||||
|
orderNo, refundNo, refundResult.getRefundId());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} catch (PaymentException e) {
|
||||||
|
log.error("{}, 支付类型: {}, 订单号: {}, 退款单号: {}, 错误: {}",
|
||||||
|
PaymentConstants.LogMessage.REFUND_FAILED, getSupportedPaymentType(),
|
||||||
|
orderNo, refundNo, e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("{}, 支付类型: {}, 订单号: {}, 退款单号: {}, 系统错误: {}",
|
||||||
|
PaymentConstants.LogMessage.REFUND_FAILED, getSupportedPaymentType(),
|
||||||
|
orderNo, refundNo, e.getMessage(), e);
|
||||||
|
throw PaymentException.systemError("微信支付退款失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaymentResponse queryRefund(String refundNo, Integer tenantId) throws PaymentException {
|
public PaymentResponse queryRefund(String refundNo, Integer tenantId) throws PaymentException {
|
||||||
// TODO: 实现微信退款查询逻辑
|
log.info("开始查询微信退款状态, 退款单号: {}, 租户ID: {}", refundNo, tenantId);
|
||||||
throw PaymentException.unsupportedPayment("暂不支持微信退款查询", PaymentType.WECHAT_NATIVE);
|
|
||||||
|
try {
|
||||||
|
// 参数验证
|
||||||
|
if (!StringUtils.hasText(refundNo)) {
|
||||||
|
throw PaymentException.paramError("退款单号不能为空");
|
||||||
|
}
|
||||||
|
if (tenantId == null) {
|
||||||
|
throw PaymentException.paramError("租户ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取支付配置
|
||||||
|
Config wxPayConfig = wxPayConfigService.getWxPayConfig(tenantId);
|
||||||
|
|
||||||
|
// 调用微信退款查询API
|
||||||
|
Refund refundResult = queryWechatRefundStatus(refundNo, wxPayConfig);
|
||||||
|
|
||||||
|
// 构建响应
|
||||||
|
PaymentResponse response = buildRefundQueryResponse(refundResult, tenantId);
|
||||||
|
|
||||||
|
log.info("微信退款状态查询成功, 退款单号: {}, 状态: {}, 微信退款单号: {}",
|
||||||
|
refundNo, refundResult.getStatus(), refundResult.getRefundId());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} catch (PaymentException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("查询微信退款状态失败, 退款单号: {}, 租户ID: {}, 错误: {}",
|
||||||
|
refundNo, tenantId, e.getMessage(), e);
|
||||||
|
throw PaymentException.systemError("查询微信退款状态失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -398,4 +467,200 @@ public class WechatNativeStrategy implements PaymentStrategy {
|
|||||||
throw PaymentException.networkError("调用微信支付API失败: " + e.getMessage(), PaymentType.WECHAT_NATIVE, e);
|
throw PaymentException.networkError("调用微信支付API失败: " + e.getMessage(), PaymentType.WECHAT_NATIVE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证退款请求参数
|
||||||
|
*/
|
||||||
|
private void validateRefundRequest(String orderNo, String refundNo, BigDecimal totalAmount,
|
||||||
|
BigDecimal refundAmount, Integer tenantId) throws PaymentException {
|
||||||
|
if (!StringUtils.hasText(orderNo)) {
|
||||||
|
throw PaymentException.paramError("订单号不能为空");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(refundNo)) {
|
||||||
|
throw PaymentException.paramError("退款单号不能为空");
|
||||||
|
}
|
||||||
|
if (tenantId == null) {
|
||||||
|
throw PaymentException.paramError("租户ID不能为空");
|
||||||
|
}
|
||||||
|
if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw PaymentException.amountError("订单总金额必须大于0");
|
||||||
|
}
|
||||||
|
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw PaymentException.amountError("退款金额必须大于0");
|
||||||
|
}
|
||||||
|
if (refundAmount.compareTo(totalAmount) > 0) {
|
||||||
|
throw PaymentException.amountError("退款金额不能大于订单总金额");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("退款请求参数验证通过, 订单号: {}, 退款单号: {}, 订单总额: {}, 退款金额: {}",
|
||||||
|
orderNo, refundNo, totalAmount, refundAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建微信退款请求
|
||||||
|
*/
|
||||||
|
private CreateRequest buildRefundRequest(String orderNo, String refundNo, BigDecimal totalAmount,
|
||||||
|
BigDecimal refundAmount, String reason, Payment paymentConfig) {
|
||||||
|
CreateRequest refundRequest = new CreateRequest();
|
||||||
|
|
||||||
|
// 设置订单号和退款单号
|
||||||
|
refundRequest.setOutTradeNo(orderNo);
|
||||||
|
refundRequest.setOutRefundNo(refundNo);
|
||||||
|
|
||||||
|
// 设置金额信息(微信支付金额单位为分)
|
||||||
|
AmountReq amountReq = new AmountReq();
|
||||||
|
amountReq.setTotal(totalAmount.multiply(new BigDecimal("100")).longValue());
|
||||||
|
amountReq.setRefund(refundAmount.multiply(new BigDecimal("100")).longValue());
|
||||||
|
amountReq.setCurrency(PaymentConstants.Wechat.CURRENCY);
|
||||||
|
refundRequest.setAmount(amountReq);
|
||||||
|
|
||||||
|
// 设置退款原因(可选,但建议填写)
|
||||||
|
if (StringUtils.hasText(reason)) {
|
||||||
|
// 退款原因最多80个字符
|
||||||
|
String effectiveReason = CommonUtil.truncateString(reason, 80);
|
||||||
|
refundRequest.setReason(effectiveReason);
|
||||||
|
} else {
|
||||||
|
refundRequest.setReason("用户申请退款");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("构建微信退款请求完成 - 订单号: {}, 退款单号: {}, 订单总额: {}分, 退款金额: {}分",
|
||||||
|
orderNo, refundNo, amountReq.getTotal(), amountReq.getRefund());
|
||||||
|
|
||||||
|
return refundRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用微信退款API
|
||||||
|
*/
|
||||||
|
private Refund callWechatRefundApi(CreateRequest refundRequest, Config wxPayConfig) throws PaymentException {
|
||||||
|
try {
|
||||||
|
// 构建退款服务
|
||||||
|
RefundService refundService = new RefundService.Builder().config(wxPayConfig).build();
|
||||||
|
|
||||||
|
// 调用退款接口
|
||||||
|
Refund refund = refundService.create(refundRequest);
|
||||||
|
|
||||||
|
if (refund == null) {
|
||||||
|
throw PaymentException.networkError("微信退款API返回数据异常", PaymentType.WECHAT_NATIVE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("微信退款API调用成功, 退款单号: {}, 微信退款单号: {}, 状态: {}",
|
||||||
|
refundRequest.getOutRefundNo(), refund.getRefundId(), refund.getStatus());
|
||||||
|
|
||||||
|
return refund;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof PaymentException) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
log.error("调用微信退款API失败: {}", e.getMessage(), e);
|
||||||
|
throw PaymentException.networkError("调用微信退款API失败: " + e.getMessage(), PaymentType.WECHAT_NATIVE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建退款响应
|
||||||
|
*/
|
||||||
|
private PaymentResponse buildRefundResponse(Refund refund, String orderNo, String refundNo, Integer tenantId) {
|
||||||
|
PaymentResponse response = new PaymentResponse();
|
||||||
|
response.setSuccess(true);
|
||||||
|
response.setOrderNo(orderNo);
|
||||||
|
response.setTransactionId(refund.getRefundId()); // 使用微信退款单号
|
||||||
|
response.setPaymentType(PaymentType.WECHAT_NATIVE);
|
||||||
|
response.setTenantId(tenantId);
|
||||||
|
|
||||||
|
// 转换退款状态
|
||||||
|
PaymentStatus paymentStatus = convertWechatRefundStatus(refund.getStatus());
|
||||||
|
response.setPaymentStatus(paymentStatus);
|
||||||
|
|
||||||
|
// 设置退款金额(微信返回的金额是分,需要转换为元)
|
||||||
|
if (refund.getAmount() != null && refund.getAmount().getRefund() != null) {
|
||||||
|
BigDecimal refundAmount = new BigDecimal(refund.getAmount().getRefund()).divide(new BigDecimal("100"));
|
||||||
|
response.setAmount(refundAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("构建退款响应完成 - 订单号: {}, 退款单号: {}, 微信退款单号: {}, 状态: {}",
|
||||||
|
orderNo, refundNo, refund.getRefundId(), paymentStatus);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询微信退款状态
|
||||||
|
*/
|
||||||
|
private Refund queryWechatRefundStatus(String refundNo, Config wxPayConfig) throws PaymentException {
|
||||||
|
try {
|
||||||
|
// 构建退款服务
|
||||||
|
RefundService refundService = new RefundService.Builder().config(wxPayConfig).build();
|
||||||
|
|
||||||
|
// 构建查询请求
|
||||||
|
QueryByOutRefundNoRequest queryRequest = new QueryByOutRefundNoRequest();
|
||||||
|
queryRequest.setOutRefundNo(refundNo);
|
||||||
|
|
||||||
|
// 调用查询接口
|
||||||
|
Refund refund = refundService.queryByOutRefundNo(queryRequest);
|
||||||
|
|
||||||
|
if (refund == null) {
|
||||||
|
throw PaymentException.systemError("微信退款查询返回空结果", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("微信退款查询成功, 退款单号: {}, 微信退款单号: {}, 状态: {}",
|
||||||
|
refundNo, refund.getRefundId(), refund.getStatus());
|
||||||
|
|
||||||
|
return refund;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof PaymentException) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
log.error("查询微信退款状态失败, 退款单号: {}, 错误: {}", refundNo, e.getMessage(), e);
|
||||||
|
throw PaymentException.networkError("查询微信退款状态失败: " + e.getMessage(), PaymentType.WECHAT_NATIVE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建退款查询响应
|
||||||
|
*/
|
||||||
|
private PaymentResponse buildRefundQueryResponse(Refund refund, Integer tenantId) {
|
||||||
|
PaymentResponse response = new PaymentResponse();
|
||||||
|
response.setSuccess(true);
|
||||||
|
response.setOrderNo(refund.getOutTradeNo());
|
||||||
|
response.setTransactionId(refund.getRefundId());
|
||||||
|
response.setPaymentType(PaymentType.WECHAT_NATIVE);
|
||||||
|
response.setTenantId(tenantId);
|
||||||
|
|
||||||
|
// 转换退款状态
|
||||||
|
PaymentStatus paymentStatus = convertWechatRefundStatus(refund.getStatus());
|
||||||
|
response.setPaymentStatus(paymentStatus);
|
||||||
|
|
||||||
|
// 设置退款金额
|
||||||
|
if (refund.getAmount() != null && refund.getAmount().getRefund() != null) {
|
||||||
|
BigDecimal refundAmount = new BigDecimal(refund.getAmount().getRefund()).divide(new BigDecimal("100"));
|
||||||
|
response.setAmount(refundAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换微信退款状态
|
||||||
|
*/
|
||||||
|
private PaymentStatus convertWechatRefundStatus(Refund.Status refundStatus) {
|
||||||
|
if (refundStatus == null) {
|
||||||
|
return PaymentStatus.REFUNDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (refundStatus) {
|
||||||
|
case SUCCESS:
|
||||||
|
return PaymentStatus.REFUNDED;
|
||||||
|
case CLOSED:
|
||||||
|
return PaymentStatus.REFUND_FAILED;
|
||||||
|
case PROCESSING:
|
||||||
|
return PaymentStatus.REFUNDING;
|
||||||
|
case ABNORMAL:
|
||||||
|
return PaymentStatus.REFUND_FAILED;
|
||||||
|
default:
|
||||||
|
return PaymentStatus.REFUNDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import com.gxwebsoft.shop.entity.ShopOrder;
|
|||||||
import com.gxwebsoft.shop.param.ShopOrderParam;
|
import com.gxwebsoft.shop.param.ShopOrderParam;
|
||||||
import com.gxwebsoft.shop.dto.OrderCreateRequest;
|
import com.gxwebsoft.shop.dto.OrderCreateRequest;
|
||||||
import com.gxwebsoft.shop.dto.UpdatePaymentStatusRequest;
|
import com.gxwebsoft.shop.dto.UpdatePaymentStatusRequest;
|
||||||
|
import com.gxwebsoft.payment.service.PaymentService;
|
||||||
|
import com.gxwebsoft.payment.dto.PaymentResponse;
|
||||||
|
import com.gxwebsoft.payment.enums.PaymentType;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
@@ -95,6 +98,8 @@ public class ShopOrderController extends BaseController {
|
|||||||
private ShopUserAddressService shopUserAddressService;
|
private ShopUserAddressService shopUserAddressService;
|
||||||
@Resource
|
@Resource
|
||||||
private ShopOrderDeliveryService shopOrderDeliveryService;
|
private ShopOrderDeliveryService shopOrderDeliveryService;
|
||||||
|
@Resource
|
||||||
|
private PaymentService paymentService;
|
||||||
|
|
||||||
@Operation(summary = "分页查询订单")
|
@Operation(summary = "分页查询订单")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@@ -192,6 +197,103 @@ public class ShopOrderController extends BaseController {
|
|||||||
shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, shopOrder);
|
shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, shopOrder);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// 退款操作
|
||||||
|
if(shopOrder.getOrderStatus().equals(6)){
|
||||||
|
// 当订单状态更改为6(已退款)时,执行退款操作
|
||||||
|
try {
|
||||||
|
// 1. 验证订单是否可以退款
|
||||||
|
if (shopOrderNow == null) {
|
||||||
|
return fail("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查订单是否已支付
|
||||||
|
if (shopOrderNow.getPayStatus() == null || !shopOrderNow.getPayStatus().equals(1)) {
|
||||||
|
return fail("订单未支付,无法退款");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已经退款过了
|
||||||
|
if (StrUtil.isNotBlank(shopOrderNow.getRefundOrder())) {
|
||||||
|
logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), shopOrderNow.getRefundOrder());
|
||||||
|
return fail("订单已退款,请勿重复操作");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 生成退款单号
|
||||||
|
String refundNo = "RF" + IdUtil.getSnowflakeNextId();
|
||||||
|
|
||||||
|
// 3. 确定退款金额(默认全额退款)
|
||||||
|
BigDecimal refundAmount = shopOrder.getRefundMoney();
|
||||||
|
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
// 如果没有指定退款金额,使用订单实付金额
|
||||||
|
refundAmount = shopOrderNow.getTotalPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证退款金额不能大于订单金额
|
||||||
|
if (refundAmount.compareTo(shopOrderNow.getTotalPrice()) > 0) {
|
||||||
|
return fail("退款金额不能大于订单金额");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 确定支付类型(默认为微信Native支付)
|
||||||
|
PaymentType paymentType = PaymentType.WECHAT_NATIVE;
|
||||||
|
if (shopOrderNow.getPayType() != null) {
|
||||||
|
// 根据订单的支付类型确定
|
||||||
|
paymentType = PaymentType.fromCode(shopOrderNow.getPayType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 调用统一支付服务的退款接口
|
||||||
|
logger.info("开始处理订单退款 - 订单号: {}, 退款单号: {}, 退款金额: {}, 支付方式: {}",
|
||||||
|
shopOrderNow.getOrderNo(), refundNo, refundAmount, paymentType);
|
||||||
|
|
||||||
|
PaymentResponse refundResponse = paymentService.refund(
|
||||||
|
shopOrderNow.getOrderNo(), // 原订单号
|
||||||
|
refundNo, // 退款单号
|
||||||
|
shopOrderNow.getTotalPrice(), // 订单总金额
|
||||||
|
refundAmount, // 退款金额
|
||||||
|
shopOrder.getRefundReason() != null ? shopOrder.getRefundReason() : "用户申请退款", // 退款原因
|
||||||
|
paymentType, // 支付方式
|
||||||
|
shopOrderNow.getTenantId() // 租户ID
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6. 处理退款结果
|
||||||
|
if (refundResponse.getSuccess()) {
|
||||||
|
// 退款成功,更新订单信息
|
||||||
|
shopOrder.setRefundOrder(refundNo);
|
||||||
|
shopOrder.setRefundMoney(refundAmount);
|
||||||
|
shopOrder.setRefundTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
// 根据退款状态决定订单状态
|
||||||
|
// 如果微信返回退款处理中,则设置订单状态为5(退款处理中)
|
||||||
|
// 如果微信返回退款成功,则保持状态为6(退款成功)
|
||||||
|
if (refundResponse.getPaymentStatus() != null) {
|
||||||
|
switch (refundResponse.getPaymentStatus()) {
|
||||||
|
case REFUNDING:
|
||||||
|
shopOrder.setOrderStatus(5); // 退款处理中
|
||||||
|
logger.info("订单退款处理中,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo);
|
||||||
|
break;
|
||||||
|
case REFUNDED:
|
||||||
|
shopOrder.setOrderStatus(6); // 退款成功
|
||||||
|
logger.info("订单退款成功,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo);
|
||||||
|
break;
|
||||||
|
case REFUND_FAILED:
|
||||||
|
logger.error("订单退款失败,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo);
|
||||||
|
return fail("退款失败,请联系管理员");
|
||||||
|
default:
|
||||||
|
shopOrder.setOrderStatus(5); // 默认为退款处理中
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("订单退款请求成功 - 订单号: {}, 退款单号: {}, 微信退款单号: {}",
|
||||||
|
shopOrderNow.getOrderNo(), refundNo, refundResponse.getTransactionId());
|
||||||
|
} else {
|
||||||
|
// 退款失败
|
||||||
|
logger.error("订单退款失败 - 订单号: {}, 错误: {}", shopOrderNow.getOrderNo(), refundResponse.getErrorMessage());
|
||||||
|
return fail("退款失败: " + refundResponse.getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理订单退款异常 - 订单号: {}, 错误: {}", shopOrderNow.getOrderNo(), e.getMessage(), e);
|
||||||
|
return fail("退款处理异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
if (shopOrderService.updateById(shopOrder)) {
|
if (shopOrderService.updateById(shopOrder)) {
|
||||||
return success("修改成功");
|
return success("修改成功");
|
||||||
}
|
}
|
||||||
|
|||||||
130
src/main/java/com/gxwebsoft/shop/task/RefundStatusQueryTask.java
Normal file
130
src/main/java/com/gxwebsoft/shop/task/RefundStatusQueryTask.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package com.gxwebsoft.shop.task;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
|
import com.gxwebsoft.payment.dto.PaymentResponse;
|
||||||
|
import com.gxwebsoft.payment.enums.PaymentType;
|
||||||
|
import com.gxwebsoft.payment.exception.PaymentException;
|
||||||
|
import com.gxwebsoft.payment.service.PaymentService;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款状态查询定时任务
|
||||||
|
* 定期查询处理中的退款订单状态,并更新订单状态
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2025-01-26
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@ConditionalOnProperty(prefix = "shop.order.refund-query", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||||
|
public class RefundStatusQueryTask {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShopOrderService shopOrderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PaymentService paymentService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询退款状态并更新订单
|
||||||
|
* 生产环境:每10分钟执行一次
|
||||||
|
* 开发环境:每2分钟执行一次(便于测试)
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "${shop.order.refund-query.cron:0 0/10 * * * ?}")
|
||||||
|
@IgnoreTenant("定时任务需要处理所有租户的退款订单")
|
||||||
|
public void queryRefundStatusAndUpdateOrders() {
|
||||||
|
log.info("开始执行退款状态查询任务...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
int totalProcessedCount = 0;
|
||||||
|
|
||||||
|
// 查询所有退款处理中的订单(状态为5)
|
||||||
|
List<ShopOrder> refundProcessingOrders = shopOrderService.getRefundProcessingOrders();
|
||||||
|
|
||||||
|
if (refundProcessingOrders.isEmpty()) {
|
||||||
|
log.info("没有找到退款处理中的订单");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("找到 {} 个退款处理中的订单,开始查询退款状态...", refundProcessingOrders.size());
|
||||||
|
|
||||||
|
int successCount = 0;
|
||||||
|
int failedCount = 0;
|
||||||
|
|
||||||
|
for (ShopOrder order : refundProcessingOrders) {
|
||||||
|
try {
|
||||||
|
// 查询退款状态
|
||||||
|
PaymentResponse response = paymentService.queryRefund(
|
||||||
|
order.getRefundOrder(), // 退款单号
|
||||||
|
PaymentType.WECHAT_NATIVE, // 支付类型(这里假设是微信支付)
|
||||||
|
order.getTenantId() // 租户ID
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
// 根据退款状态更新订单
|
||||||
|
if (response.getSuccess()) {
|
||||||
|
// 退款成功
|
||||||
|
order.setOrderStatus(6); // 退款成功
|
||||||
|
shopOrderService.updateById(order);
|
||||||
|
log.info("订单 {} 退款成功,更新订单状态为退款成功", order.getOrderNo());
|
||||||
|
successCount++;
|
||||||
|
} else if (response.getPaymentStatus() != null) {
|
||||||
|
// 根据具体的退款状态处理
|
||||||
|
switch (response.getPaymentStatus()) {
|
||||||
|
case REFUND_FAILED:
|
||||||
|
// 退款失败
|
||||||
|
order.setOrderStatus(5); // 退款被拒绝(保持原状态或根据业务需要调整)
|
||||||
|
shopOrderService.updateById(order);
|
||||||
|
log.info("订单 {} 退款失败,更新订单状态", order.getOrderNo());
|
||||||
|
failedCount++;
|
||||||
|
break;
|
||||||
|
case REFUNDING:
|
||||||
|
// 仍在退款中,无需处理
|
||||||
|
log.debug("订单 {} 仍在退款中", order.getOrderNo());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.warn("订单 {} 未知的退款状态: {}", order.getOrderNo(), response.getPaymentStatus());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("订单 {} 退款状态查询返回空结果", order.getOrderNo());
|
||||||
|
}
|
||||||
|
} catch (PaymentException e) {
|
||||||
|
log.error("查询订单 {} 退款状态时发生支付异常: {}", order.getOrderNo(), e.getMessage(), e);
|
||||||
|
failedCount++;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("查询订单 {} 退款状态时发生系统异常: {}", order.getOrderNo(), e.getMessage(), e);
|
||||||
|
failedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
long duration = endTime - startTime;
|
||||||
|
|
||||||
|
log.info("退款状态查询任务完成,总处理数量: {},成功: {},失败: {},耗时: {}ms",
|
||||||
|
refundProcessingOrders.size(), successCount, failedCount, duration);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("退款状态查询任务执行失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动触发退款状态查询任务(用于测试)
|
||||||
|
*/
|
||||||
|
@IgnoreTenant("手动触发的定时任务需要处理所有租户的退款订单")
|
||||||
|
public void manualQueryRefundStatus() {
|
||||||
|
log.info("手动触发退款状态查询任务...");
|
||||||
|
queryRefundStatusAndUpdateOrders();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user