mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-08-10 00:03:44 +08:00
bugfix balance refund duplicate risk
This commit is contained in:
@@ -482,6 +482,13 @@ public interface OrderBasicInfoService{
|
||||
*/
|
||||
BigDecimal getOrderRefundedAmount(String orderCode);
|
||||
|
||||
/**
|
||||
* 计算余额支付订单本次允许退款金额
|
||||
*/
|
||||
Map<String, BigDecimal> calculateAllowableBalanceRefund(String orderCode, BigDecimal refundAmount,
|
||||
BigDecimal principalPay, BigDecimal giftPay,
|
||||
BigDecimal returnPrincipal, BigDecimal returnGift);
|
||||
|
||||
/**
|
||||
* 通过orderCode获取退款明细列表
|
||||
* @param orderCode
|
||||
|
||||
@@ -35,6 +35,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
@@ -213,6 +214,7 @@ public class MemberBasicInfoServiceImpl implements MemberBasicInfoService {
|
||||
* 接收的金额都是正数,通过操作类型判断 充值还是扣减
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateMemberBalance(UpdateMemberBalanceDTO dto) {
|
||||
String memberId = dto.getMemberId(); // 会员id
|
||||
BigDecimal updateGiftBalance = dto.getUpdateGiftBalance() != null ? dto.getUpdateGiftBalance() : BigDecimal.ZERO; // 更新赠送金额
|
||||
@@ -412,6 +414,7 @@ public class MemberBasicInfoServiceImpl implements MemberBasicInfoService {
|
||||
i = memberBasicInfoMapper.updateMemberBalance(memberId, walletCode, newPrincipalBalance, newGiftBalance, walletInfo.getVersion());
|
||||
if (i == 0) {
|
||||
log.info("修改用户余额-修改余额失败, memberId:{}", memberId);
|
||||
throw new BusinessException("", "修改用户余额失败,请重试");
|
||||
}
|
||||
}
|
||||
// 插入 member_wallet_log 表
|
||||
|
||||
@@ -1584,32 +1584,55 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
public void balancePaymentOrderRefundV2(OrderBasicInfo orderBasicInfo) {
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
BigDecimal refundAmount = orderBasicInfo.getRefundAmount();
|
||||
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("balancePaymentOrderRefundV2-退款金额小于等于0,orderCode:{}, 退款金额:{}", orderCode, refundAmount);
|
||||
return;
|
||||
}
|
||||
// 订单消费金额
|
||||
BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
|
||||
BigDecimal orderAmount = orderBasicInfo.getOrderAmount() == null ? BigDecimal.ZERO : orderBasicInfo.getOrderAmount();
|
||||
// 查支付记录
|
||||
List<OrderPayRecord> payRecordList = orderPayRecordService.getOrderPayRecordList(orderCode);
|
||||
if (CollectionUtils.isEmpty(payRecordList)) {
|
||||
logger.info("balancePaymentOrderRefundV2-订单支付记录为空,orderCode:{}", orderCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Map<String, OrderPayRecord> payRecordMap = payRecordList.stream()
|
||||
// .collect(Collectors.toMap(OrderPayRecord::getPayMode, Function.identity(), (k1, k2) -> k1));
|
||||
Map<String, List<OrderPayRecord>> payRecordMap = payRecordList.stream().collect(Collectors.groupingBy(OrderPayRecord::getPayMode));
|
||||
// 获取本金支付的记录
|
||||
List<OrderPayRecord> orderPayRecordList = payRecordMap.get(Constants.ONE);
|
||||
BigDecimal principalPay = orderPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
List<OrderPayRecord> principalPayRecordList = payRecordMap.get(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue());
|
||||
BigDecimal principalPay = null;
|
||||
if (CollectionUtils.isNotEmpty(principalPayRecordList)) {
|
||||
principalPay = principalPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
// 取出本金支付金额
|
||||
// BigDecimal principalPay = null;
|
||||
|
||||
// if (principalPayRecord != null) {
|
||||
// principalPay = principalPayRecord.getPayAmount();
|
||||
// }
|
||||
// 获取赠送金支付的记录
|
||||
List<OrderPayRecord> giftPayRecordList = payRecordMap.get(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue());
|
||||
BigDecimal giftPay = null;
|
||||
if (CollectionUtils.isNotEmpty(giftPayRecordList)) {
|
||||
giftPay = giftPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
// 计算需要退回的金额
|
||||
Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, null, orderAmount);
|
||||
Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, giftPay, orderAmount);
|
||||
logger.info("结算订单:{}, 剩余金额退回余额, 订单消费金额:{}, 本金支付金额:{}, 赠送支付金额:{}, 退回金额map:{}",
|
||||
orderCode, orderAmount, principalPay, null, JSON.toJSONString(returnAmountMap));
|
||||
orderCode, orderAmount, principalPay, giftPay, JSON.toJSONString(returnAmountMap));
|
||||
|
||||
// 需要退回本金的金额
|
||||
BigDecimal returnPrincipal = returnAmountMap.get("returnPrincipal");
|
||||
// 需要退回赠送金的金额
|
||||
BigDecimal returnGift = returnAmountMap.get("returnGift");
|
||||
|
||||
Map<String, BigDecimal> allowableRefundMap = calculateAllowableBalanceRefund(orderCode, refundAmount,
|
||||
principalPay, giftPay, returnPrincipal, returnGift);
|
||||
returnPrincipal = allowableRefundMap.get("returnPrincipal");
|
||||
returnGift = allowableRefundMap.get("returnGift");
|
||||
if (returnPrincipal.compareTo(BigDecimal.ZERO) <= 0 && returnGift.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("balancePaymentOrderRefundV2-订单:{}本次可退金额为0,不执行退款", orderCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新会员钱包/余额退回到钱包
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
@@ -1617,6 +1640,7 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
.type(MemberWalletEnum.TYPE_IN.getValue()) // 进账
|
||||
.subType(MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue()) // 订单结算退款
|
||||
.updatePrincipalBalance(returnPrincipal)
|
||||
.updateGiftBalance(returnGift)
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
@@ -5618,6 +5642,102 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
return orderBasicInfoMapper.queryRepayOrder(memberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算余额支付订单本次允许退款金额
|
||||
*/
|
||||
@Override
|
||||
public Map<String, BigDecimal> calculateAllowableBalanceRefund(String orderCode, BigDecimal refundAmount,
|
||||
BigDecimal principalPay, BigDecimal giftPay,
|
||||
BigDecimal returnPrincipal, BigDecimal returnGift) {
|
||||
Map<String, BigDecimal> resultMap = Maps.newHashMap();
|
||||
resultMap.put("returnPrincipal", BigDecimal.ZERO);
|
||||
resultMap.put("returnGift", BigDecimal.ZERO);
|
||||
|
||||
principalPay = positiveOrZero(principalPay);
|
||||
giftPay = positiveOrZero(giftPay);
|
||||
returnPrincipal = positiveOrZero(returnPrincipal);
|
||||
returnGift = positiveOrZero(returnGift);
|
||||
|
||||
BigDecimal balancePayTotal = principalPay.add(giftPay);
|
||||
BigDecimal targetRefundAmount = positiveOrZero(refundAmount);
|
||||
if (StringUtils.isBlank(orderCode) || balancePayTotal.compareTo(BigDecimal.ZERO) <= 0
|
||||
|| targetRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("余额退款额度裁剪-参数不足,orderCode:{}, 目标退款金额:{}, 余额支付总额:{}",
|
||||
orderCode, targetRefundAmount, balancePayTotal);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
if (targetRefundAmount.compareTo(balancePayTotal) > 0) {
|
||||
logger.info("余额退款额度裁剪-orderCode:{}, 目标退款金额:{}大于余额支付总额:{},按余额支付总额限制",
|
||||
orderCode, targetRefundAmount, balancePayTotal);
|
||||
targetRefundAmount = balancePayTotal;
|
||||
}
|
||||
|
||||
Map<String, BigDecimal> refundedAmountMap = getBalanceRefundedAmountByCategory(orderCode);
|
||||
BigDecimal refundedPrincipal = refundedAmountMap.get("refundedPrincipal");
|
||||
BigDecimal refundedGift = refundedAmountMap.get("refundedGift");
|
||||
BigDecimal refundedTotal = refundedPrincipal.add(refundedGift);
|
||||
BigDecimal remainingTargetAmount = targetRefundAmount.subtract(refundedTotal);
|
||||
if (remainingTargetAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("余额退款额度裁剪-orderCode:{}, 目标退款金额:{}, 已退总额:{},不再退款",
|
||||
orderCode, targetRefundAmount, refundedTotal);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
BigDecimal remainingPrincipalPay = positiveOrZero(principalPay.subtract(refundedPrincipal));
|
||||
BigDecimal remainingGiftPay = positiveOrZero(giftPay.subtract(refundedGift));
|
||||
|
||||
BigDecimal allowedPrincipal = min(returnPrincipal, remainingPrincipalPay, remainingTargetAmount);
|
||||
remainingTargetAmount = remainingTargetAmount.subtract(allowedPrincipal);
|
||||
BigDecimal allowedGift = min(returnGift, remainingGiftPay, remainingTargetAmount);
|
||||
|
||||
resultMap.put("returnPrincipal", allowedPrincipal);
|
||||
resultMap.put("returnGift", allowedGift);
|
||||
|
||||
logger.info("余额退款额度裁剪-orderCode:{}, 目标退款金额:{}, 支付本金:{}, 支付赠送:{}, 已退本金:{}, 已退赠送:{}, 原计算本金:{}, 原计算赠送:{}, 本次本金:{}, 本次赠送:{}",
|
||||
orderCode, targetRefundAmount, principalPay, giftPay, refundedPrincipal, refundedGift,
|
||||
returnPrincipal, returnGift, allowedPrincipal, allowedGift);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
private Map<String, BigDecimal> getBalanceRefundedAmountByCategory(String orderCode) {
|
||||
Map<String, BigDecimal> resultMap = Maps.newHashMap();
|
||||
BigDecimal refundedPrincipal = BigDecimal.ZERO;
|
||||
BigDecimal refundedGift = BigDecimal.ZERO;
|
||||
|
||||
List<MemberWalletLog> walletLogs = memberWalletLogService.getOrderRecord(orderCode, MemberWalletEnum.TYPE_IN.getValue());
|
||||
if (CollectionUtils.isNotEmpty(walletLogs)) {
|
||||
for (MemberWalletLog walletLog : walletLogs) {
|
||||
String subType = walletLog.getSubType();
|
||||
if (StringUtils.isNotBlank(subType)
|
||||
&& !StringUtils.equals(subType, MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue())) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal amount = walletLog.getAmount() == null ? BigDecimal.ZERO : walletLog.getAmount().abs();
|
||||
if (StringUtils.equals(Constants.TWO, walletLog.getCategory())) {
|
||||
refundedGift = refundedGift.add(amount);
|
||||
} else {
|
||||
refundedPrincipal = refundedPrincipal.add(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultMap.put("refundedPrincipal", refundedPrincipal);
|
||||
resultMap.put("refundedGift", refundedGift);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
private BigDecimal positiveOrZero(BigDecimal amount) {
|
||||
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
private BigDecimal min(BigDecimal first, BigDecimal second, BigDecimal third) {
|
||||
return first.min(second).min(third);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单已退款金额
|
||||
*/
|
||||
@@ -6735,4 +6855,3 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1076,6 +1076,7 @@ public class DelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
}
|
||||
|
||||
BigDecimal discountAmount = orderBasicInfo.getDiscountAmount() == null ? BigDecimal.ZERO : orderBasicInfo.getDiscountAmount();
|
||||
BigDecimal insuranceAmount = orderBasicInfo.getInsuranceAmount() == null ? BigDecimal.ZERO : orderBasicInfo.getInsuranceAmount();
|
||||
|
||||
// 计算需要退回的金额
|
||||
Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, giftPay, orderAmount, discountAmount);
|
||||
@@ -1102,14 +1103,14 @@ public class DelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
.build();
|
||||
|
||||
// 判断消费金额,如果消费金额 - 折扣金额小于 1 元,则将保险费也进行退回
|
||||
if (orderBasicInfo.getOrderAmount().subtract(orderBasicInfo.getDiscountAmount()).compareTo(BigDecimal.ONE) < 0) {
|
||||
if (orderAmount.subtract(discountAmount).compareTo(BigDecimal.ONE) < 0) {
|
||||
// 判断是否需要退保险费用
|
||||
boolean checkResult = orderBasicInfoService.checkRefundInsuranceAmount(orderBasicInfo);
|
||||
if (checkResult) {
|
||||
// 退保险, 使用 calculateBalanceRefund 重新计算完整的退款金额(电费退款+保险费退款)
|
||||
// 注意:不能在 calculateReturnAmount 的基础上叠加保险费,因为 calculateReturnAmount 不感知保险费,
|
||||
// 算出的退款金额中已包含保险费部分,叠加会导致保险费被重复退还
|
||||
Map<String, BigDecimal> refundMap = calculateBalanceRefund(principalPay, giftPay, orderBasicInfo.getOrderAmount(), orderBasicInfo.getDiscountAmount(), orderBasicInfo.getInsuranceAmount());
|
||||
Map<String, BigDecimal> refundMap = calculateBalanceRefund(principalPay, giftPay, orderAmount, discountAmount, insuranceAmount);
|
||||
BigDecimal refundPrincipal = refundMap.get("returnPrincipal");
|
||||
BigDecimal refundGift = refundMap.get("returnGift");
|
||||
BigDecimal returnPrincipalForInsurance = refundMap.get("returnPrincipalForInsurance");
|
||||
@@ -1121,6 +1122,16 @@ public class DelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
updateMemberBalanceDTO.setUpdateGiftBalance(refundGift.add(returnGiftForInsurance));
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, BigDecimal> allowableRefundMap = orderBasicInfoService.calculateAllowableBalanceRefund(orderCode, refundAmount,
|
||||
principalPay, giftPay, updateMemberBalanceDTO.getUpdatePrincipalBalance(), updateMemberBalanceDTO.getUpdateGiftBalance());
|
||||
updateMemberBalanceDTO.setUpdatePrincipalBalance(allowableRefundMap.get("returnPrincipal"));
|
||||
updateMemberBalanceDTO.setUpdateGiftBalance(allowableRefundMap.get("returnGift"));
|
||||
if (updateMemberBalanceDTO.getUpdatePrincipalBalance().compareTo(BigDecimal.ZERO) <= 0
|
||||
&& updateMemberBalanceDTO.getUpdateGiftBalance().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("balancePaymentOrderRefund-订单:{}本次可退金额为0,不执行退款", orderCode);
|
||||
return;
|
||||
}
|
||||
// 统一退款
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
|
||||
@@ -755,6 +755,11 @@ public class NotDelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-余额支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
|
||||
BigDecimal refundAmount = orderBasicInfo.getRefundAmount();
|
||||
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("balancePaymentOrderRefund-退款金额小于等于0,orderCode:{}, 退款金额:{}", orderBasicInfo.getOrderCode(), refundAmount);
|
||||
return;
|
||||
}
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
// 订单消费金额
|
||||
@@ -801,6 +806,16 @@ public class NotDelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
.updateGiftBalance(returnGift)
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
|
||||
Map<String, BigDecimal> allowableRefundMap = orderBasicInfoService.calculateAllowableBalanceRefund(orderCode, refundAmount,
|
||||
principalPay, giftPay, updateMemberBalanceDTO.getUpdatePrincipalBalance(), updateMemberBalanceDTO.getUpdateGiftBalance());
|
||||
updateMemberBalanceDTO.setUpdatePrincipalBalance(allowableRefundMap.get("returnPrincipal"));
|
||||
updateMemberBalanceDTO.setUpdateGiftBalance(allowableRefundMap.get("returnGift"));
|
||||
if (updateMemberBalanceDTO.getUpdatePrincipalBalance().compareTo(BigDecimal.ZERO) <= 0
|
||||
&& updateMemberBalanceDTO.getUpdateGiftBalance().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
logger.info("balancePaymentOrderRefund-订单:{}本次可退金额为0,不执行退款", orderCode);
|
||||
return;
|
||||
}
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 更新order_pay_record, 解冻部分
|
||||
|
||||
Reference in New Issue
Block a user