feat:独立分账

This commit is contained in:
suzhuhong
2026-04-16 11:03:52 +08:00
parent 33f47db7e7
commit d90f8eb2a8
11 changed files with 371 additions and 15 deletions

View File

@@ -394,10 +394,12 @@ public enum ErrorCodeEnum {
TRADE_EXIST(1621006, "部分交易流水号已被使用,请检查!", null),
NOT_COMPLETE_TRANS_SHEET(1621007, "存在关联未分账完成的分账单,请完成之后再操作!", null),
PENDING_TRANS_AMOUNT(1621008, "分账金额不能大于待分账金额!", null),
TRANSFER_ERROR(1621009, "分账异常", null),
TRANSFER_ERROR(1621009, "分账异常{0}", null),
TRANSFER_ING(1621010, "分账中,请勿重复分账!", null),
WITHDRAW_APPLY_NOT_EXIST(1621011, "提现申请单不存在!", null),
WITHDRAW_ING(1621012, "提现中,请勿重复分账!", null),
WALLET_BALANCE_INSUFFICIENT(1621013, "钱包余额不足!", null),

View File

@@ -0,0 +1,240 @@
package com.cool.store.utils;
import java.math.BigDecimal;
/**
* @Auther zx_szh
* @Date 2026/4/16 10:03
* @Version 1.0
*/
public class BigDecimalUtils {
/**
* 比较两个BigDecimal是否相等忽略精度差异
*
* @param a 第一个值
* @param b 第二个值
* @return 如果相等返回true
*/
public static boolean equals(BigDecimal a, BigDecimal b) {
if (a == b) return true;
if (a == null || b == null) return false;
return a.compareTo(b) == 0;
}
/**
* 比较两个BigDecimal是否相等指定精度
*
* @param a 第一个值
* @param b 第二个值
* @param scale 保留小数位数
* @param roundingMode 舍入模式
* @return 如果相等返回true
*/
public static boolean equals(BigDecimal a, BigDecimal b, int scale, int roundingMode) {
if (a == b) return true;
if (a == null || b == null) return false;
return a.setScale(scale, roundingMode).compareTo(b.setScale(scale, roundingMode)) == 0;
}
/**
* 判断a是否大于b
*
* @param a 第一个值
* @param b 第二个值
* @return 如果a大于b返回true
*/
public static boolean greaterThan(BigDecimal a, BigDecimal b) {
if (a == null || b == null) return false;
return a.compareTo(b) > 0;
}
/**
* 判断a是否大于或等于b
*
* @param a 第一个值
* @param b 第二个值
* @return 如果a大于或等于b返回true
*/
public static boolean greaterThanOrEqual(BigDecimal a, BigDecimal b) {
if (a == null || b == null) return false;
return a.compareTo(b) >= 0;
}
/**
* 判断a是否小于b
*
* @param a 第一个值
* @param b 第二个值
* @return 如果a小于b返回true
*/
public static boolean lessThan(BigDecimal a, BigDecimal b) {
if (a == null || b == null) return false;
return a.compareTo(b) < 0;
}
/**
* 判断a是否小于或等于b
*
* @param a 第一个值
* @param b 第二个值
* @return 如果a小于或等于b返回true
*/
public static boolean lessThanOrEqual(BigDecimal a, BigDecimal b) {
if (a == null || b == null) return false;
return a.compareTo(b) <= 0;
}
/**
* 判断value是否在min和max之间包含边界
*
* @param value 要判断的值
* @param min 最小值
* @param max 最大值
* @return 如果在范围内返回true
*/
public static boolean between(BigDecimal value, BigDecimal min, BigDecimal max) {
if (value == null || min == null || max == null) return false;
return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
}
/**
* 判断value是否在min和max之间不包含边界
*
* @param value 要判断的值
* @param min 最小值
* @param max 最大值
* @return 如果在范围内返回true
*/
public static boolean betweenExclusive(BigDecimal value, BigDecimal min, BigDecimal max) {
if (value == null || min == null || max == null) return false;
return value.compareTo(min) > 0 && value.compareTo(max) < 0;
}
/**
* 判断value是否在min和max之间左闭右开
*
* @param value 要判断的值
* @param min 最小值(包含)
* @param max 最大值(不包含)
* @return 如果在范围内返回true
*/
public static boolean betweenLeftClosedRightOpen(BigDecimal value, BigDecimal min, BigDecimal max) {
if (value == null || min == null || max == null) return false;
return value.compareTo(min) >= 0 && value.compareTo(max) < 0;
}
/**
* 判断value是否在min和max之间左开右闭
*
* @param value 要判断的值
* @param min 最小值(不包含)
* @param max 最大值(包含)
* @return 如果在范围内返回true
*/
public static boolean betweenLeftOpenRightClosed(BigDecimal value, BigDecimal min, BigDecimal max) {
if (value == null || min == null || max == null) return false;
return value.compareTo(min) > 0 && value.compareTo(max) <= 0;
}
/**
* 判断value是否大于0
*
* @param value 要判断的值
* @return 如果大于0返回true
*/
public static boolean isPositive(BigDecimal value) {
return greaterThan(value, BigDecimal.ZERO);
}
/**
* 判断value是否大于或等于0
*
* @param value 要判断的值
* @return 如果大于或等于0返回true
*/
public static boolean isNonNegative(BigDecimal value) {
return greaterThanOrEqual(value, BigDecimal.ZERO);
}
/**
* 判断value是否小于0
*
* @param value 要判断的值
* @return 如果小于0返回true
*/
public static boolean isNegative(BigDecimal value) {
return lessThan(value, BigDecimal.ZERO);
}
/**
* 判断value是否小于或等于0
*
* @param value 要判断的值
* @return 如果小于或等于0返回true
*/
public static boolean isNonPositive(BigDecimal value) {
return lessThanOrEqual(value, BigDecimal.ZERO);
}
/**
* 判断value是否等于0
*
* @param value 要判断的值
* @return 如果等于0返回true
*/
public static boolean isZero(BigDecimal value) {
return equals(value, BigDecimal.ZERO);
}
/**
* 判断value是否非零
*
* @param value 要判断的值
* @return 如果不等于0返回true
*/
public static boolean isNotZero(BigDecimal value) {
return !isZero(value);
}
/**
* 判断两个BigDecimal中的较大值
*
* @param a 第一个值
* @param b 第二个值
* @return 较大值如果都为null返回null
*/
public static BigDecimal max(BigDecimal a, BigDecimal b) {
if (a == null) return b;
if (b == null) return a;
return a.compareTo(b) >= 0 ? a : b;
}
/**
* 判断两个BigDecimal中的较小值
*
* @param a 第一个值
* @param b 第二个值
* @return 较小值如果都为null返回null
*/
public static BigDecimal min(BigDecimal a, BigDecimal b) {
if (a == null) return b;
if (b == null) return a;
return a.compareTo(b) <= 0 ? a : b;
}
/**
* 安全比较处理null情况
*
* @param a 第一个值
* @param b 第二个值
* @return 比较结果a>b返回1a=b返回0a<b返回-1
*/
public static int safeCompare(BigDecimal a, BigDecimal b) {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return a.compareTo(b);
}
}

View File

@@ -67,6 +67,17 @@ public class WalletTradeDAO {
return walletTradeMapper.getPayingOrderBatchCode(module, type);
}
/**
* 查询支付中的 单次支付的数据 非批量
* @param module
* @param type
* @return
*/
public List<WalletTradeDO> getPayingOrder(String module, Integer type) {
return walletTradeMapper.getPayingOrder(module, type);
}
public List<WalletTradeDO> transferTradeList(String module, Integer type) {
return walletTradeMapper.transferTradeList(module, type);
}

View File

@@ -21,5 +21,7 @@ public interface WalletTradeMapper extends Mapper<WalletTradeDO> {
*/
List<String> getPayingOrderBatchCode(@Param("module") String module, @Param("type") Integer type);
List<WalletTradeDO> getPayingOrder(@Param("module") String module, @Param("type") Integer type);
List<WalletTradeDO> transferTradeList(@Param("module") String module, @Param("type") Integer type);
}

View File

@@ -72,6 +72,12 @@
GROUP BY batch_code
</select>
<select id="getPayingOrder" resultMap="BaseResultMap">
SELECT *
FROM zxjp_wallet_trade
WHERE pay_status = 3 AND module = #{module} AND type = #{type} AND batch_code IS NULL
</select>
<select id="transferTradeList" resultMap="BaseResultMap">
SELECT *
FROM zxjp_wallet_trade

View File

@@ -41,7 +41,7 @@ public class WalletTradeDO {
private String tradeId;
/**
* 交易类型1转账
* 交易类型1转账 2-提现
*/
private Integer type;

View File

@@ -252,7 +252,7 @@ public class SplitOrderServiceImpl implements SplitOrderService {
//分账失败 释放锁
redisUtil.unlock(lockKey);
log.info("confirmSplitOrder:{}",e.getMessage());
throw new ServiceException(ErrorCodeEnum.TRANSFER_ERROR);
throw new ServiceException(ErrorCodeEnum.ERROR_MESSAGE);
}
existing.setConfirmer(userInfo.getUserId());
existing.setStatus(accountTransferDTO.getTradeStatus());

View File

@@ -2,6 +2,7 @@ package com.cool.store.service.store;
import com.cool.store.entity.order.PreAllocationRecordDO;
import com.cool.store.entity.order.StoreOrderDO;
import com.cool.store.entity.wallet.WalletTradeDO;
import com.cool.store.request.store.PreAllocationQueryShopRequest;
import com.cool.store.request.store.PreAllocationSaveRequest;
import com.cool.store.request.store.TransRequest;
@@ -29,4 +30,11 @@ public interface PreAllocationRecordService {
Boolean trans(TransRequest transRequest);
/**
* 分账状态刷新
* @param walletTradeDO
* @return
*/
Boolean transStatusRefresh(WalletTradeDO walletTradeDO);
}

View File

@@ -1,6 +1,7 @@
package com.cool.store.service.store.impl;
import cn.hutool.core.collection.CollStreamUtil;
import com.alibaba.excel.converters.bigdecimal.BigDecimalNumberConverter;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.RedisConstant;
import com.cool.store.dao.FranchiseFeeDAO;
@@ -18,6 +19,7 @@ import com.cool.store.dto.fees.WalletAllocationDTO;
import com.cool.store.dto.wallet.AccountInfoDTO;
import com.cool.store.dto.wallet.AccountTransferDTO;
import com.cool.store.dto.wallet.BatchTransferDTO;
import com.cool.store.dto.wallet.TradeRecordDTO;
import com.cool.store.entity.FranchiseFeeDO;
import com.cool.store.entity.LineInfoDO;
import com.cool.store.entity.ShopInfoDO;
@@ -38,14 +40,12 @@ import com.cool.store.enums.fees.WalletFeeItemEnum;
import com.cool.store.enums.order.StoreOrderStatusEnum;
import com.cool.store.enums.point.ShopSubStageEnum;
import com.cool.store.enums.point.ShopSubStageStatusEnum;
import com.cool.store.enums.wallet.TradeTypeEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.FranchiseFeeMapper;
import com.cool.store.request.store.PreAllocationSaveRequest;
import com.cool.store.request.store.TransRequest;
import com.cool.store.request.wallet.BatchTransferQueryRequest;
import com.cool.store.request.wallet.BatchTransferRequest;
import com.cool.store.request.wallet.OutStoreIdRequest;
import com.cool.store.request.wallet.TransferRequest;
import com.cool.store.request.wallet.*;
import com.cool.store.request.xgj.PushFranchiseFeeRequest;
import com.cool.store.request.xgj.ReceiptRequest;
import com.cool.store.service.PushService;
@@ -54,10 +54,7 @@ import com.cool.store.service.fees.WalletPayInfoService;
import com.cool.store.service.order.MiniStoreOrderService;
import com.cool.store.service.store.PreAllocationRecordService;
import com.cool.store.service.wallet.WalletApiService;
import com.cool.store.utils.BeanUtil;
import com.cool.store.utils.GenerateNoUtil;
import com.cool.store.utils.RedisUtil;
import com.cool.store.utils.StringUtil;
import com.cool.store.utils.*;
import com.cool.store.vo.order.MiniStoreOrderDetailVO;
import com.cool.store.vo.order.PreAllocationRecordVO;
import com.sun.xml.bind.v2.TODO;
@@ -78,6 +75,7 @@ import java.util.stream.Stream;
import static com.cool.store.enums.order.StoreOrderStatusEnum.PAY_FAIL;
import static com.cool.store.enums.wallet.WalletTradeModuleEnum.STANDARD_STORE;
import static com.cool.store.enums.wallet.WalletTradeModuleEnum.TRANSFER;
/**
* @Auther zx_szh
@@ -740,21 +738,76 @@ public class PreAllocationRecordServiceImpl implements PreAllocationRecordServic
if (CollectionUtils.isEmpty(accountInfoList)){
throw new ServiceException(ErrorCodeEnum.NOT_EXIST_WANG_SHANG_ACCOUNT);
}
//校验金额
AccountInfoDTO accountInfoDTO = accountInfoList.get(0);
String totalAmount = accountInfoDTO.getTotalAmount();
Boolean amountSupport = BigDecimalUtils.greaterThanOrEqual(new BigDecimal(totalAmount), record.getPayAmount());
if (!amountSupport){
throw new ServiceException(ErrorCodeEnum.WALLET_BALANCE_INSUFFICIENT);
}
AccountTransferDTO accountTransferDTO ;
try {
AccountInfoDTO accountInfoDTO = accountInfoList.get(0);
accountTransferDTO = walletPayInfoService.accountPay(record.getExpenseType(), record.getPayAmount(),
record.getPayeeCode(), accountInfoDTO.getAccountNo(), record.getPayNo(), transRequest.getRemark());
}catch (Exception e){
//分账失败 释放锁
redisUtil.unlock(lockKey);
log.info("confirmSplitOrder:{}",e.getMessage());
//todo 改为明确的提示
throw new ServiceException(ErrorCodeEnum.TRANSFER_ERROR);
log.error("confirmSplitOrder:{}",e.getMessage());
throw new ServiceException(ErrorCodeEnum.ERROR_MESSAGE);
}
record.setAllocationStatus(AllocationPayStatusEnum.PAYING.getStatus());
//先改为分账中
preAllocationRecordDAO.updateByPrimaryKeySelective(record);
WalletTradeDO walletTradeDO = new WalletTradeDO();
walletTradeDO.setPayStatus(accountTransferDTO.getTradeStatus());
walletTradeDO.setModule(TRANSFER.getModule());
walletTradeDO.setRemark(transRequest.getRemark());
walletTradeDO.setPayAmount(new BigDecimal(accountTransferDTO.getAmount()));
walletTradeDO.setPayTime(new Date());
walletTradeDO.setPayNo(record.getPayNo());
walletTradeDO.setTradeId(String.valueOf(accountTransferDTO.getTradeId()));
walletTradeDO.setType(TradeTypeEnum.ADD_BY_HAND.getStatus());
walletTradeDAO.insertSelective(walletTradeDO);
return null;
}
@Override
public Boolean transStatusRefresh(WalletTradeDO walletTradeDO) {
BillDetailRequest request = new BillDetailRequest();
request.setTradeId(Long.valueOf(walletTradeDO.getTradeId()));
TradeRecordDTO billDetail = walletApiService.getBillDetail(request);
walletTradeDO.setPayStatus(billDetail.getTradeStatus());
walletTradeDAO.updateByPrimaryKeySelective(walletTradeDO);
PreAllocationRecordDO preAllocationRecordDO = preAllocationRecordDAO.queryPageByPayNo(walletTradeDO.getPayNo());
preAllocationRecordDO.setAllocationStatus(billDetail.getTradeStatus());
preAllocationRecordDO.setUpdateTime(new Date());
preAllocationRecordDAO.updateByPrimaryKeySelective(preAllocationRecordDO);
if (AllocationPayStatusEnum.PAID.getStatus().equals(walletTradeDO.getPayStatus())) {
ShopInfoDO shopInfo = shopInfoDAO.getShopInfo(preAllocationRecordDO.getShopId());
//订单状态更新
StoreOrderDO storeOrder = storeOrderDAO.getById(preAllocationRecordDO.getOrderId());
//部分支付
StoreOrderDO updateOrder = StoreOrderDO.builder()
.id(storeOrder.getId())
.status(StoreOrderStatusEnum.PART_OF_WAIT_PAY.getCode())
.updateTime(new Date())
.build();
//当前缴纳金额 = 本次缴纳前已缴纳金额+本次缴纳金额
BigDecimal currentAmount = new BigDecimal(billDetail.getAmount());
BigDecimal paidAmount = storeOrder.getPaidAmount().add(currentAmount);
BigDecimal unpaidAmount = storeOrder.getTotalAmount().subtract(paidAmount);
//支付成功 修改金额,支付中 回调的时候习惯
updateOrder.setPaidAmount(paidAmount);
updateOrder.setUnpaidAmount(unpaidAmount);
updateOrder.setPayTime(new Date());
if (BigDecimalUtils.equals(unpaidAmount,new BigDecimal(0))){
//剩余缴纳金额是0 缴费完成
updateOrder.setStatus( StoreOrderStatusEnum.PAID.getCode());
}
storeOrderDAO.updateSelective(updateOrder);
}
return Boolean.TRUE;
}
}

View File

@@ -2,6 +2,7 @@ package com.cool.store.controller.webb;
import com.cool.store.request.store.PreAllocationQueryShopRequest;
import com.cool.store.request.store.PreAllocationSaveRequest;
import com.cool.store.request.store.TransRequest;
import com.cool.store.response.ResponseResult;
import com.cool.store.service.store.PreAllocationRecordService;
import com.cool.store.vo.order.PreAllocationRecordVO;
@@ -33,6 +34,12 @@ public class PreAllocationRecordController {
return ResponseResult.success(preAllocationRecordService.saveBatch(req));
}
@ApiOperation("单费用类型独立分账")
@PostMapping("/trans")
public ResponseResult<Boolean> queryByShop(@RequestBody @Valid TransRequest request ){
return ResponseResult.success(preAllocationRecordService.trans(request));
}
@ApiOperation("按门店查询预分账明细")
@GetMapping("/queryByShop")
public ResponseResult<List<PreAllocationRecordVO>> queryByShop(@RequestParam(value = "shopId" ,required = true) Long shopId){

View File

@@ -676,6 +676,33 @@ public class XxlJobHandler {
pageNum++;
}
log.info("------end batchTransferStandardStore------");
//开始单个分账状态查询
transferStandardStore();
}
public void transferStandardStore() {
log.info("------start transferStandardStore------");
boolean hasNext = true;
int pageNum = 1;
int pageSize = CommonConstants.BATCH_SIZE;
while (hasNext) {
PageHelper.startPage(pageNum, pageSize);
List<WalletTradeDO> list = walletTradeDAO.getPayingOrder(WalletTradeModuleEnum.STANDARD_STORE.getModule(), 1);
hasNext = list.size() >= pageSize;
if (CollectionUtils.isEmpty(list)) {
break;
}
for (WalletTradeDO walletTradeDO : list) {
try {
preAllocationRecordService.transStatusRefresh(walletTradeDO);
} catch (Exception e) {
log.error("钱包单费用类型转账分账支付状态查询失败, walletTradeDO:{}", walletTradeDO, e);
}
}
pageNum++;
}
log.info("------end transferStandardStore------");
}
@XxlJob("transfer")