分账逻辑调整

This commit is contained in:
Jack 2025-09-08 17:58:18 +08:00
parent 235dfa19a8
commit c5d5c9ee5b
3 changed files with 142 additions and 105 deletions

View File

@ -57,6 +57,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -1646,17 +1647,14 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 6. 获取订单分账相关参数
String merchantNo = shopOrderLkl.getLkl_merchant_no();
// 分账金额 = 应付总金额-运费支付时已计算好
Integer splitAmount = shopOrderLkl.getSplit_amt();
splitAmount = CheckUtil.isEmpty(splitAmount) ? 0 : splitAmount;
Integer shoppingFee = shopOrderLkl.getShopping_fee();
shoppingFee = CheckUtil.isEmpty(shoppingFee) ? 0 : shoppingFee;
// 商家分账比例
BigDecimal splitRatioMch = shopOrderLkl.getSplit_ratio();
// 7. 分账金额校验
if (splitAmount <= 0) {
String errorMsg = String.format("店铺[%s]订单[%s]分账金额[%d]异常,跳过分账",
String errorMsg = String.format("店铺[%s]订单[%s]分账金额[%d]低于1分钱跳过分账",
shopOrderLkl.getStore_id(), orderId, splitAmount);
log.error(errorMsg);
errorMessages.append(errorMsg).append("; ");
@ -1666,9 +1664,8 @@ public class LakalaApiServiceImpl implements LakalaApiService {
continue;
}
// 8. 获取分账接收方信息
// 获取分账平台接收方信息
LklLedgerMerReceiverBind platform = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo);
List<LklLedgerMerReceiverBind> distributors = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo);
if (platform == null) {
String errorMsg = String.format("店铺[%s]未绑定平台方接收账户,跳过分账", shopOrderLkl.getStore_id());
@ -1677,17 +1674,140 @@ public class LakalaApiServiceImpl implements LakalaApiService {
continue;
}
// 9. 判断是否可以分账商家比例不超过100%
boolean canSplit = splitRatioMch != null && splitRatioMch.compareTo(new BigDecimal(100)) <= 0;
if (!canSplit) {
String errorMsg = String.format("店铺[%s]分账比例[%s]异常,无法分账",
// 8. 构建分账接收方列表
List<V3SacsSeparateRecvDatas> recvDatas = new ArrayList<>();
// 9. 获取商家分账比例并校验
BigDecimal splitRatioMch = shopOrderLkl.getSplit_ratio();
// 判断商家分账比例是否有效必须在(0, 100]范围内
boolean canSplitForMch = splitRatioMch != null
&& splitRatioMch.compareTo(BigDecimal.ZERO) > 0
&& splitRatioMch.compareTo(new BigDecimal(100)) <= 0;
if (!canSplitForMch) {
String errorMsg = String.format("店铺[%s]商家分账比例[%s]不在(0-100]范围内,无法分账",
shopOrderLkl.getStore_id(), splitRatioMch);
log.error(errorMsg);
errorMessages.append(errorMsg).append("; ");
continue;
} else {
log.info("店铺[%s]商家分账比例[%s]在(0-100]范围内,可以分账",
shopOrderLkl.getStore_id(), splitRatioMch);
// 商家分账
BigDecimal mchRatio = splitRatioMch.divide(new BigDecimal(100)); // 比如94/100
Integer mchSplitCent = new BigDecimal(splitAmount).multiply(mchRatio).intValue();
if (mchSplitCent > 0) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvMerchantNo(merchantNo);
receiver.setSeparateValue(mchSplitCent.toString());
recvDatas.add(receiver);
log.debug("商家分账:金额={}分,商户号={}", mchSplitCent, merchantNo);
}
}
// 10. 构建分账请求对象
// 10. 计算平台和代理商分账金额
Integer pdSplitAmount = 0;
// 计算平台+代理商的总比例=100-商家分账比例
BigDecimal splitRatioNoMch = new BigDecimal(100).subtract(splitRatioMch);
// 判断平台和代理商分账比例是否有效必须在[0, 100)范围内
boolean canSplitForNoMch = splitRatioNoMch != null
&& splitRatioNoMch.compareTo(BigDecimal.ZERO) > 0
&& splitRatioNoMch.compareTo(new BigDecimal(100)) < 0;
if (!canSplitForNoMch) {
String errorMsg = String.format("店铺[%s]平台和代理商分账比例[%s]不在[0-100)范围内,无法分账",
shopOrderLkl.getStore_id(), splitRatioNoMch);
log.warn(errorMsg);
} else {
log.info("店铺[%s]平台和代理商分账比例[%s]在[0-100)范围内,可以分账",
shopOrderLkl.getStore_id(), splitRatioNoMch);
// 分账代理商接收方信息
List<LklLedgerMerReceiverBind> distributors = Collections.emptyList();
// 判断要不要分账给平台和代理商分账金额太低或者没有平台代理商就不用分账
// 计算平台和代理商分账金额小于1分钱不进行平台和代理商分账
BigDecimal notMchRatio = splitRatioNoMch.divide(new BigDecimal(100)); // 比如6/100
// 平台方和代理商总计分账金额
pdSplitAmount = notMchRatio.multiply(new BigDecimal(splitAmount)).intValue();
if (pdSplitAmount > 1) {
// 11. 判断是否能分账给代理商
// 如果有平台和代理商同时存在平台分账剩余资金20%代理商分账剩余资金80%
// 能分账给代理商的条件总分账金额*0.2要大于1分钱
boolean canSplitForDistributors = pdSplitAmount * 0.2 > 1;
// 12. 根据是否能分账给代理商决定分账模式
if (canSplitForDistributors) {
// 获取分账代理商接收方信息
distributors = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo);
// 根据是否有代理商决定分账模式
if (!CollectionUtils.isEmpty(distributors)) {
// 平台+代理商分账模式
log.debug("订单[{}]采用平台+代理商分账模式", orderId);
// 平台收取剩余资金20%手续费
Integer platformValue = pdSplitAmount * 20 / 100;
if (platformValue >= 1) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(platform.getReceiver_no());
receiver.setSeparateValue(platformValue.toString());
recvDatas.add(receiver);
log.debug("平台分账:金额={}分,接收方={}", platformValue, platform.getReceiver_no());
}
// 代理商分账扣除平台20%后的剩余比例
Integer distributorValue = pdSplitAmount - platformValue;
if (distributorValue >= 1) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(distributors.get(0).getReceiver_no());
receiver.setSeparateValue(distributorValue.toString());
recvDatas.add(receiver);
log.debug("代理商分账:金额={}分,接收方={}", distributorValue, distributors.get(0).getReceiver_no());
}
}
} else {
// 仅平台分账模式
log.debug("订单[{}]采用仅平台分账模式", orderId);
if (pdSplitAmount > 1) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(platform.getReceiver_no());
receiver.setSeparateValue(pdSplitAmount.toString());
recvDatas.add(receiver);
log.debug("平台分账:金额={}分,接收方={}", pdSplitAmount, platform.getReceiver_no());
}
}
// 13. 记录详细的分账信息便于部署后排查问题
if (log.isDebugEnabled()) {
StringBuilder detailLog = new StringBuilder();
detailLog.append("详细分账信息:");
detailLog.append("订单ID=").append(shopOrderLkl.getOrder_id()).append(", ");
detailLog.append("商户号=").append(merchantNo).append(", ");
detailLog.append("分账流水号=").append(shopOrderLkl.getLkl_split_log_no()).append(", ");
detailLog.append("外部分账单号=").append(shopOrderLkl.getOut_separate_no()).append(", ");
detailLog.append("分账总金额=").append(splitAmount).append("分, ");
detailLog.append("商家分账比例=").append(splitRatioMch).append("%, ");
detailLog.append("平台接收方=").append(platform.getReceiver_no()).append(", ");
if (canSplitForDistributors && !CollectionUtils.isEmpty(distributors)) {
detailLog.append("代理商接收方=[");
for (int i = 0; i < distributors.size(); i++) {
if (i > 0) detailLog.append(",");
detailLog.append(distributors.get(i).getReceiver_no());
}
detailLog.append("], ");
}
detailLog.append("分账接收方数量=").append(recvDatas.size());
log.debug(detailLog.toString());
}
}
}
// 14. 构建分账请求对象
V3SacsSeparateRequest request = new V3SacsSeparateRequest();
request.setMerchantNo(merchantNo);
request.setLogNo(shopOrderLkl.getLkl_split_log_no()); // 合单和非合单的流水号保存在此字段
@ -1695,101 +1815,17 @@ public class LakalaApiServiceImpl implements LakalaApiService {
request.setOutSeparateNo(shopOrderLkl.getOut_separate_no());
request.setTotalAmt(splitAmount.toString());
request.setLklOrgNo(orgCode);
request.setCalType("0");
request.setCalType("0"); // 0- 按照指定金额1- 按照指定比例默认 0
request.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify");
// 11. 构建分账接收方列表
List<V3SacsSeparateRecvDatas> recvDatas = new ArrayList<>();
Integer totalSeparateValue = 0;// 平台方和代理商总计分账金额
// 12. 处理分账逻辑如果可以分账
if (canSplit) {
// 计算平台+代理商的总比例
BigDecimal splitRatioNoMch = new BigDecimal(100).subtract(splitRatioMch);
// 13. 根据是否有代理商决定分账模式
if (!CollectionUtils.isEmpty(distributors) && splitRatioNoMch.compareTo(BigDecimal.ONE) > 0) {
// 平台+代理商分账模式
log.debug("订单[{}]采用平台+代理商分账模式", orderId);
// 平台收取1%手续费
BigDecimal platformValue = CommonUtil.DecimalRoundHalfDown(
new BigDecimal(splitAmount).multiply(new BigDecimal("0.01")));
if (platformValue.compareTo(BigDecimal.ZERO) > 0) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(platform.getReceiver_no());
receiver.setSeparateValue(platformValue.toString());
recvDatas.add(receiver);
totalSeparateValue += platformValue.intValue();
log.debug("平台分账:金额={}分,接收方={}", platformValue, platform.getReceiver_no());
}
// 代理商分账扣除平台1%后的剩余比例
BigDecimal distributorRatio = splitRatioNoMch.subtract(new BigDecimal("1")).divide(new BigDecimal(100));
BigDecimal distributorValue = CommonUtil.DecimalRoundHalfDown(
new BigDecimal(splitAmount).multiply(distributorRatio));
if (distributorValue.compareTo(BigDecimal.ZERO) > 0 && !distributors.isEmpty()) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(distributors.get(0).getReceiver_no());
receiver.setSeparateValue(distributorValue.toString());
recvDatas.add(receiver);
totalSeparateValue += distributorValue.intValue();
log.debug("代理商分账:金额={}分,接收方={}", distributorValue, distributors.get(0).getReceiver_no());
}
} else {
// 仅平台分账模式
log.debug("订单[{}]采用仅平台分账模式", orderId);
BigDecimal platformRatio = splitRatioNoMch.divide(new BigDecimal(100));
BigDecimal platformValue = new BigDecimal(splitAmount).multiply(platformRatio);
if (platformValue.compareTo(BigDecimal.ZERO) > 0) {
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
receiver.setRecvNo(platform.getReceiver_no());
receiver.setSeparateValue(platformValue.toString());
recvDatas.add(receiver);
totalSeparateValue += platformValue.intValue();
log.debug("平台分账:金额={}分,接收方={}", platformValue, platform.getReceiver_no());
}
}
}
// 14. 设置分账接收方列表
// 15. 设置分账接收方列表
request.setRecvDatas(recvDatas);
log.info("分账请求参数: 订单={}, 商户={}, 金额={}分, 分账接收方数量={}",
orderId, merchantNo, splitAmount, recvDatas.size());
// 记录详细的分账信息便于部署后排查问题
if (log.isDebugEnabled()) {
StringBuilder detailLog = new StringBuilder();
detailLog.append("详细分账信息:");
detailLog.append("订单ID=").append(shopOrderLkl.getOrder_id()).append(", ");
detailLog.append("商户号=").append(merchantNo).append(", ");
detailLog.append("分账流水号=").append(shopOrderLkl.getLkl_split_log_no()).append(", ");
detailLog.append("外部分账单号=").append(shopOrderLkl.getOut_separate_no()).append(", ");
detailLog.append("分账总金额=").append(splitAmount).append("分, ");
detailLog.append("运费=").append(shoppingFee).append("分, ");
detailLog.append("商家分账比例=").append(splitRatioMch).append("%, ");
detailLog.append("平台接收方=").append(platform.getReceiver_no()).append(", ");
if (!CollectionUtils.isEmpty(distributors)) {
detailLog.append("代理商接收方=[");
for (int i = 0; i < distributors.size(); i++) {
if (i > 0) detailLog.append(",");
detailLog.append(distributors.get(i).getReceiver_no());
}
detailLog.append("], ");
}
detailLog.append("分账接收方数量=").append(recvDatas.size());
log.debug(detailLog.toString());
}
log.debug("分账请求详细参数: {}", JSONUtil.toJsonStr(request));
// 15. 发送分账请求
// 16. 发送分账请求
log.info("向拉卡拉发送分账请求:订单={}, 商户={}, 分账流水号={}",
orderId, merchantNo, shopOrderLkl.getLkl_split_log_no());
String response = LKLSDK.httpPost(request);
@ -1803,7 +1839,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
log.debug("分账响应结果: {}", response);
// 16. 解析响应结果
// 17. 解析响应结果
JSONObject respJson = JSONUtil.parseObj(response);
if (respJson == null || !lklSacsSuccessCode.equals(respJson.getStr("code")) || respJson.getJSONObject("resp_data") == null) {
String errorMsg = String.format("拉卡拉返回格式异常,订单=%s商户=%s分账流水号=%s响应=%srespJson=%s",
@ -1813,7 +1849,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
continue;
}
// 17. 保存分账记录
// 18. 保存分账记录
JSONObject respData = respJson.getJSONObject("resp_data");
LklOrderSeparate lklOrderSeparate = new LklOrderSeparate();
lklOrderSeparate.setSeparate_no(respData.getStr("separate_no"));
@ -1827,7 +1863,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
lklOrderSeparate.setLkl_org_no(request.getLklOrgNo());
lklOrderSeparate.setRecv_datas(JSONUtil.toJsonStr(request.getRecvDatas()));
lklOrderSeparate.setStatus(respData.getStr("status"));
lklOrderSeparate.setTotal_separate_value(totalSeparateValue);
lklOrderSeparate.setTotal_separate_value(pdSplitAmount);
try {
if (lklOrderSeparateService.addOrUpdateByReceiverNo(lklOrderSeparate)) {
@ -1852,7 +1888,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
}
}
// 18. 返回最终处理结果
// 19. 返回最终处理结果
log.info("订单[{}]分账处理完成:总订单数={},成功处理数={}", orderId, totalCount, successCount);
if (successCount == 0) {
String result = "分账全部失败: " + errorMessages;

View File

@ -41,7 +41,7 @@ public class RedisKeyExpiredListener implements MessageListener {
String patternStr = pattern != null ? new String(pattern) : "无模式";
// 基本日志记录
log.info("[Redis过期监听] 接收到Redis键过期事件. 过期键: {}, 订阅模式: {}", expiredKey, patternStr);
log.debug("[Redis过期监听] 接收到Redis键过期事件. 过期键: {}, 订阅模式: {}", expiredKey, patternStr);
// 参数验证
if (StrUtil.isBlank(expiredKey)) {
@ -68,7 +68,7 @@ public class RedisKeyExpiredListener implements MessageListener {
log.error("[Redis过期监听] 订单超时事件处理失败. 店铺ID: {}, 订单号: {}", args[0], args[1]);
}
} else {
log.debug("[Redis过期监听] 忽略非订单超时事件. 过期键: {}", expiredKey);
//log.debug("[Redis过期监听] 忽略非订单超时事件. 过期键: {}", expiredKey);
}
}

View File

@ -488,6 +488,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
ShopStoreSfOrder shopStoreSfOrder = JSONUtil.toBean(sfExpressApiRes.get("result").toString(), ShopStoreSfOrder.class);
shopStoreSfOrder.setDev_id(sfCreateOrderReq.getDev_id());
shopStoreSfOrder.setOrder_status(SFExpressConstant.Cons_CreatedOrder);
shopStoreSfOrder.setShop_id(sfCreateOrderReq.getShop_id());
shopStoreSfOrder.setStatus_desc("已创建顺丰同城订单");
// 下单成功保存顺丰同城订单记录到 shop_store_sf_order 表里