订单退款

localizer
ljw 2025-09-30 16:48:27 +08:00
parent 89b1679992
commit c6f6c8c6a5
5 changed files with 214 additions and 2 deletions

View File

@ -15,6 +15,7 @@ public enum MoneyChangeCategory implements DictStr {
PinTaiDaKuan("PinTaiDaKuan", "平台打款"),
DingDanTiaoZhang("DingDanTiaoZhang", "订单调账"),
DingDanKouKuan("DingDanKouKuan", "订单扣款"),
DingDanTuiKuan("DingDanTuiKuan", "订单退款"),
;
private final String val;
private final String txt;

View File

@ -23,6 +23,7 @@ import com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity;
import com.njzscloud.supervisory.order.pojo.param.PaymentItemParam;
import com.njzscloud.supervisory.order.pojo.param.PaymentParam;
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
import com.njzscloud.supervisory.order.pojo.dto.RefundRequestDto;
import com.njzscloud.supervisory.order.service.OrderExpenseItemsService;
import com.njzscloud.supervisory.order.service.OrderInfoService;
import com.njzscloud.supervisory.order.service.WechatPayService;
@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
/**
*
@ -83,8 +85,8 @@ public class PaymentController {
// 校验支付方式
if (paymentParam.getPaymentCategory() == null ||
!("wx".equals(paymentParam.getPaymentCategory()) ||
"company".equals(paymentParam.getPaymentCategory()))) {
!(PaymentWay.WX.getVal().equals(paymentParam.getPaymentCategory()) ||
PaymentWay.COMPANY.getVal().equals(paymentParam.getPaymentCategory()))) {
throw Exceptions.clierr("支付方式不合法");
}
@ -301,6 +303,154 @@ public class PaymentController {
}
}
/**
* 退 - 退退
*/
@PostMapping("/refund")
public R<?> refund(@RequestBody RefundRequestDto refundRequest) {
try {
// 验证订单存在性
OrderInfoEntity orderInfo = orderInfoService.getById(refundRequest.getOrderId());
if (orderInfo == null) {
throw Exceptions.clierr("订单不存在");
}
// 验证订单状态
if (!PaymentStatus.YiZhiFu.getVal().equals(orderInfo.getPaymentStatus().getVal())) {
throw Exceptions.clierr("订单未支付,无法退款");
}
// 获取支付上下文
PaymentContextResult ctx = orderInfoService.paymentContext(refundRequest.getOrderId());
if (ctx == null) {
throw Exceptions.clierr("获取支付上下文失败");
}
// 根据支付方式处理退款
if (PaymentWay.WX.getVal().equals(ctx.getPaymentCategory())) {
// 微信退款
// return processWechatRefund(refundRequest, orderInfo, ctx);
return R.success("退款成功");
} else if (PaymentWay.COMPANY.getVal().equals(ctx.getPaymentCategory())) {
// 公司退款
return processCompanyRefund(refundRequest, ctx);
} else {
throw Exceptions.clierr("不支持的支付方式");
}
} catch (Exception e) {
log.error("退款处理异常订单ID{}", refundRequest.getOrderId(), e);
return R.failed(ExceptionMsg.CLI_ERR_MSG, "退款处理失败:" + e.getMessage());
}
}
/**
* 退
*/
private R<?> processWechatRefund(RefundRequestDto refundRequest, OrderInfoEntity orderInfo, PaymentContextResult ctx) {
if (refundRequest.getWechatRefundParams() == null) {
throw Exceptions.clierr("微信退款参数不能为空");
}
// 构建微信退款请求
WechatPayRefundDto refundDto = new WechatPayRefundDto();
// refundDto.setOutTradeNo(orderInfo.getOutTradeNo());
// refundDto.setOutRefundNo(refundRequest.getWechatRefundParams().getOutRefundNo());
// refundDto.setReason(refundRequest.getReason());
// refundDto.setRefund(orderInfo.getSettleMoney().multiply(new BigDecimal("100")).longValue()); // 转换为分
// refundDto.setTotal(orderInfo.getSettleMoney().multiply(new BigDecimal("100")).longValue()); // 转换为分
// refundDto.setFundsAccount(refundRequest.getWechatRefundParams().getFundsAccount());
// refundDto.setNotifyUrl(refundRequest.getWechatRefundParams().getNotifyUrl());
// 调用微信退款接口
WechatPayRefundResponseDto response = wechatPayService.refund(refundDto);
// 更新订单状态为已退款
orderInfoService.lambdaUpdate()
.eq(OrderInfoEntity::getId, refundRequest.getOrderId())
.set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan)
.update();
log.info("微信退款成功订单ID{},退款单号:{}", refundRequest.getOrderId(), refundDto.getOutRefundNo());
return R.success(response);
}
/**
* 退
*/
private R<?> processCompanyRefund(RefundRequestDto refundRequest, PaymentContextResult ctx) {
if (refundRequest.getRefundAmount() == null) {
throw Exceptions.clierr("公司退款金额不能为空");
}
if (refundRequest.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw Exceptions.clierr("退款金额必须大于0");
}
List<OrderExpenseItemsEntity> entityList = orderExpenseItemsService.list(Wrappers.<OrderExpenseItemsEntity>lambdaQuery()
.eq(OrderExpenseItemsEntity::getDeleted, Boolean.FALSE)
.eq(OrderExpenseItemsEntity::getOrderId, refundRequest.getOrderId()));
BigDecimal dbTotalAmount = BigDecimal.ZERO;
if (null == entityList || entityList.size() <= 0) {
throw Exceptions.clierr("结算清单不存在");
}
for (OrderExpenseItemsEntity it : entityList) {
BigDecimal dbSettle = it.getSettleMoney();
// 当dbSettle为null时按0计算
if (dbSettle == null) {
dbSettle = BigDecimal.ZERO;
}
// 累加数据库中的结算金额
dbTotalAmount = dbTotalAmount.add(dbSettle);
}
if (refundRequest.getRefundAmount().compareTo(dbTotalAmount) > 0) {
throw Exceptions.clierr("退款金额不能超过订单已结算金额");
}
// 恢复公司账户余额
restoreCompanyBalance(ctx, refundRequest.getRefundAmount(), refundRequest.getOrderId(), refundRequest.getReason());
// 更新订单状态为已退款
orderInfoService.lambdaUpdate()
.eq(OrderInfoEntity::getId, refundRequest.getOrderId())
.set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan)
.update();
log.info("公司退款成功订单ID{},退款金额:{}", refundRequest.getOrderId(), refundRequest.getRefundAmount());
return R.success("退款成功");
}
/**
*
*/
private void restoreCompanyBalance(PaymentContextResult ctx, BigDecimal refundAmount, Long orderId, String reason) {
if (ctx.getCompanyUserId() == null || ctx.getCompanyAccountId() == null) {
throw Exceptions.clierr("公司账户信息不完整,无法退款");
}
// 恢复公司账户余额
BigDecimal oldBalance = ctx.getCompanyBalance();
BigDecimal newBalance = oldBalance.add(refundAmount);
MoneyAccountEntity companyAccount = new MoneyAccountEntity()
.setId(ctx.getCompanyAccountId())
.setMoney(newBalance);
moneyAccountService.updateById(companyAccount);
// 记录资金明细(这里需要实现资金明细记录功能)
MoneyChangeDetailEntity changeDetail = new MoneyChangeDetailEntity()
.setCompanyId(ctx.getTransCompanyId())
.setOrderId(orderId)
.setMoneyAccountId(companyAccount.getId())
.setOldMoney(oldBalance)
.setDelta(refundAmount) // 扣减为负数
.setNewMoney(newBalance)
.setMoneyChangeCategory(MoneyChangeCategory.DingDanTuiKuan)
.setMemo(reason);
moneyChangeDetailService.save(changeDetail);
log.info("公司账户余额恢复成功账户ID{},恢复金额:{}", ctx.getCompanyAccountId(), refundAmount);
}
/**
* ID
*/

View File

@ -0,0 +1,55 @@
package com.njzscloud.supervisory.order.pojo.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
/**
* 退DTO
*/
@Data
public class RefundRequestDto {
/**
* ID
*/
@NotNull(message = "订单ID不能为空")
private Long orderId;
/**
* 退退使退
*/
private BigDecimal refundAmount;
/**
* 退
*/
@Size(max = 200, message = "退款原因不能超过200个字符")
private String reason;
/**
* 退退使
*/
private WechatRefundParams wechatRefundParams;
@Data
public static class WechatRefundParams {
/**
* 退
*/
@Size(max = 64, message = "商户退款单号不能超过64个字符")
private String outRefundNo;
/**
* 退
*/
private String fundsAccount;
/**
* 退
*/
private String notifyUrl;
}
}

View File

@ -25,6 +25,10 @@ public class PaymentContextResult {
private String wechatOpenid;
private String paymentCategory;
private String outTradeNo;
}

View File

@ -206,6 +206,8 @@
a.id AS orderId,
a.trans_company_id AS transCompanyId,
a.sn,
a.payment_category,
a.out_trade_no,
bc.user_id AS companyUserId,
ma1.id AS companyAccountId,
ma1.money AS companyBalance,