优化退款和分账流程,

This commit is contained in:
Jack 2025-09-20 17:54:45 +08:00
parent cf01dcd472
commit 1c5334ff86
6 changed files with 98 additions and 48 deletions

View File

@ -61,8 +61,8 @@ public class LklSeparateDTO {
// 测试基于可分账金额的分账算法正常情况
System.out.println("\n=== 基于可分账金额的分账算法测试(正常情况) ===");
LklSeparateDTO dto2 = new LklSeparateDTO();
dto2.setTotalSeparateAmount(2); // 分账总额 1000分
dto2.setShippingFee(1); // 配送费 100分
dto2.setTotalSeparateAmount(1); // 分账总额 1000分
dto2.setShippingFee(0); // 配送费 100分
// dto2.setRefCanSeparateAmount(null);
dto2.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025
dto2.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.857 (会产生小数)

View File

@ -74,4 +74,12 @@ public interface LklOrderSeparateService extends IBaseService<LklOrderSeparate>
*/
List<LklOrderSeparate> getUnSuccessSeparateList(Date beginDate, Date endDate, Integer page, Integer size);
/**
* 判断订单是否已经分账
* 记录不存在或 status final_status 不是 SUCCESS 都表示没有分账
*
* @param orderId
* @return
*/
Boolean isOrderSeparated(String orderId);
}

View File

@ -2320,7 +2320,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
log.debug("[分账操作] 分账参数计算结果:{}", lklSeparateDTO);
// 更新分账计算结果
shopOrderLkl.setSeparate_remark(lklSeparateDTO.toString());
shopOrderLkl.setSeparate_remark(lklSeparateDTO.toString()); // 写入分账具体情况
if (CheckUtil.isEmpty(canSeparateAmt)) {
shopOrderLkl.setSplit_amt(lklSeparateDTO.getCanSeparateAmount());
canSeparateAmt = lklSeparateDTO.getCanSeparateAmount();
@ -2406,7 +2406,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
if (StrUtil.isBlank(response)) {
String errorMsg = String.format("[分账操作] 拉卡拉无响应,订单=%s商户=%s分账流水号=%s",
orderId, merchantNo, shopOrderLkl.getLkl_receive_log_no());
shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, errorMsg);
shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, "");
log.error(errorMsg);
return Pair.of(false, "拉卡拉无响应");
}
@ -2418,7 +2418,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
if (respJson == null || !lklSacsSuccessCode.equals(respJson.getStr("code")) || respJson.getJSONObject("resp_data") == null) {
String errorMsg = String.format("[分账操作] 拉卡拉返回格式异常,订单=%s商户=%s分账流水号=%s响应=%s",
orderId, merchantNo, shopOrderLkl.getLkl_receive_log_no(), response);
shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, errorMsg);
shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, "");
log.error(errorMsg);
return Pair.of(false, "拉卡拉分账异常:[" + (respJson != null ? respJson.getStr("code") : "未知错误码") + "]" +
(respJson != null ? respJson.getStr("msg") : "未知错误"));
@ -2473,20 +2473,22 @@ public class LakalaApiServiceImpl implements LakalaApiService {
* </p>
*
* @param request
* @param merchantNo 分账方商户号非空表示是事后补偿
* @param separateNo 分账指令流水号非空表示是事后补偿
* <ul>
* <li>成功: {"code": "SUCCESS", "message": "操作成功"}</li>
* <li>失败: {"code": "FAIL", "message": "错误信息"}</li>
* </ul>
* @param merchantNoParam 分账方商户号非空表示是事后补偿
* @param separateNoParam 分账指令流水号非空表示是事后补偿
* <ul>
* <li>成功: {"code": "SUCCESS", "message": "操作成功"}</li>
* <li>失败: {"code": "FAIL", "message": "错误信息"}</li>
* </ul>
*/
@Override
public JSONObject sacsSeparateNotify(HttpServletRequest request, String merchantNo, String separateNo) {
public JSONObject sacsSeparateNotify(HttpServletRequest request, String merchantNoParam, String separateNoParam) {
String channel = "";
try {
JSONObject paramsJson = null;
if (!StrUtil.hasBlank(merchantNo, separateNo)) {
log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果查询补偿");
paramsJson = sacsQuery(merchantNo, separateNo);
if (!StrUtil.hasBlank(merchantNoParam, separateNoParam)) {
channel = "(补偿)";
log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果通知异步回调 merchantNoParam={},separateNoParam={}" + channel, merchantNoParam, separateNoParam);
paramsJson = sacsQuery(merchantNoParam, separateNoParam);
} else {
log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果通知异步回调");
@ -2504,9 +2506,8 @@ public class LakalaApiServiceImpl implements LakalaApiService {
paramsJson = JSONUtil.parseObj(signCheckResult.getSecond());
}
if (paramsJson == null) {
String errorMsg = "分账通知参数解析失败: 请求参数为空";
String errorMsg = "分账通知参数解析失败: 请求参数为空" + channel;
log.error("[拉卡拉分账通知] {}", errorMsg);
return JSONUtil.createObj()
.put("code", "FAIL")
@ -2515,7 +2516,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 3. 提取关键参数并校验 - 确保必要参数完整
String logNo = paramsJson.getStr("log_no"); // 合单订单是子单确认收货流水号非合单时是主单确认收货流水号
String separateNoNew = paramsJson.getStr("separate_no");
String separateNo = paramsJson.getStr("separate_no");
String outSeparateNo = paramsJson.getStr("out_separate_no");
String status = paramsJson.getStr("status");
String finalStatus = paramsJson.getStr("final_status");
@ -2523,12 +2524,12 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 必要参数
List<String> missingParams = new ArrayList<>();
if (StrUtil.isBlank(outSeparateNo)) missingParams.add("outSeparateNo");
if (StrUtil.isBlank(separateNoNew)) missingParams.add("separateNo");
if (StrUtil.isBlank(separateNo)) missingParams.add("separateNo");
if (StrUtil.isBlank(status)) missingParams.add("status");
if (!missingParams.isEmpty()) {
String errorMsg = "分账通知缺少必要参数: " + String.join(", ", missingParams);
log.error("[拉卡拉分账通知] {},参数详情: {}", errorMsg, paramsJson);
String errorMsg = "分账通知缺少必要参数" + channel + String.join(", ", missingParams);
log.error("[拉卡拉分账通知] {} {},参数详情: {}", channel, errorMsg, paramsJson);
return JSONUtil.createObj()
.put("code", "FAIL")
.put("message", errorMsg);
@ -2537,9 +2538,9 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 4. 查询现有分账记录
LklOrderSeparate lklOrderSeparateExist = lklOrderSeparateService.getByLogNoAndOutTradeNo(logNo, outSeparateNo);
if (lklOrderSeparateExist == null) {
String errorMsg = "未找到对应的分账记录";
String errorMsg = "未找到对应的分账记录" + channel;
log.error("[拉卡拉分账通知] {},外部分账单号={},分账单号={},参数详情: {}",
errorMsg, outSeparateNo, separateNo, paramsJson);
errorMsg, outSeparateNo, separateNoParam, paramsJson);
return JSONUtil.createObj()
.put("code", "FAIL")
.put("message", errorMsg);
@ -2548,7 +2549,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 5. 检查分账记录状态避免重复处理
String existingFinalStatus = lklOrderSeparateExist.getFinal_status();
if ("SUCCESS".equals(existingFinalStatus) && "SUCCESS".equals(lklOrderSeparateExist.getStatus())) {
String warnMsg = "分账已处理成功,请不要重复通知";
String warnMsg = "分账已处理成功,请不要重复通知" + channel;
log.warn("[拉卡拉分账通知] {},订单号={},外部分账单号={},参数详情: {}",
warnMsg, logNo, outSeparateNo, paramsJson);
return JSONUtil.createObj()
@ -2557,7 +2558,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
}
// 6. 记录关键参数信息便于问题排查
log.info("[拉卡拉分账通知] 接收到分账通知,分账单号={},外部分账单号={},状态={},最终状态={}",
log.info("[拉卡拉分账通知] 接收到分账通知,分账单号={},外部分账单号={},状态={},最终状态={}" + channel,
separateNo, outSeparateNo, status, finalStatus);
// 7. 构建分账记录对象 - 准备更新数据库的分账记录
@ -2585,9 +2586,9 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 8. 持久化处理 - 更新分账记录到数据库
boolean updateSuccess = lklOrderSeparateService.addOrUpdateByReceiverNo(lklOrderSeparate);
if (!updateSuccess) {
String errorMsg = String.format("分账记录更新失败, separateNo=%s", separateNo);
String errorMsg = String.format("分账记录更新失败, separateNo=%s", separateNo) + channel;
// 更改分账状态分账失败
shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Fail, errorMsg);
shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Fail, "");
log.error("[拉卡拉分账通知] {}", errorMsg);
return JSONUtil.createObj()
.put("code", "FAIL")
@ -2598,13 +2599,13 @@ public class LakalaApiServiceImpl implements LakalaApiService {
shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, "");
// 9. 记录处理成功日志
log.info("[拉卡拉分账通知] 分账通知处理成功, separateNo={}, status={}", separateNo, status);
log.info("[拉卡拉分账通知] 分账通知处理成功, separateNo={}, status={}" + channel, separateNo, status);
return JSONUtil.createObj()
.put("code", "SUCCESS")
.put("message", "操作成功");
} catch (Exception e) {
// 10. 异常处理
String errorMsg = String.format("分账通知数据处理异常: %s", e.getMessage());
String errorMsg = String.format("分账通知数据处理异常: %s" + channel, e.getMessage());
log.error("[拉卡拉分账通知] {}", errorMsg, e);
return JSONUtil.createObj()
.put("code", "FAIL")

View File

@ -214,4 +214,40 @@ public class LklOrderSeparateServiceImpl extends BaseServiceImpl<LklOrderSeparat
return Collections.emptyList();
}
}
/**
* 判断订单是否已经分账完成
*
* @param orderId 订单ID
* @return Boolean true-分账已完成false-分账未完成或不存在分账记录
*/
@Override
public Boolean isOrderSeparated(String orderId) {
// 参数校验
if (StrUtil.isBlank(orderId)) {
log.warn("订单ID为空无法判断分账状态");
return false;
}
try {
// 直接查询分账成功的记录
LklOrderSeparate lklOrderSeparate = findOne(new QueryWrapper<LklOrderSeparate>()
.eq("order_id", orderId)
.eq("status", "SUCCESS")
.eq("final_status", "SUCCESS")
.orderByDesc("id"));
// 如果查询到分账成功的记录返回true
if (lklOrderSeparate == null) {
log.debug("订单[{}]分账未完成或不存在分账记录", orderId);
return false;
}
log.debug("订单[{}]分账已完成", orderId);
return true;
} catch (Exception e) {
log.error("查询订单[{}]分账状态异常", orderId, e);
return false;
}
}
}

View File

@ -38,12 +38,12 @@ import com.suisung.mall.common.modules.store.ShopStoreShippingAddress;
import com.suisung.mall.common.service.MessageService;
import com.suisung.mall.common.utils.*;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.base.service.AccountBaseConfigService;
import com.suisung.mall.shop.base.service.ShopBaseProductUnitService;
import com.suisung.mall.shop.base.service.ShopBaseStateCodeService;
import com.suisung.mall.shop.distribution.service.ShopDistributionUserCommissionService;
import com.suisung.mall.shop.distribution.service.ShopDistributionUserOrderItemService;
import com.suisung.mall.shop.distribution.service.ShopDistributionUserOrderService;
import com.suisung.mall.shop.lakala.service.LklOrderSeparateService;
import com.suisung.mall.shop.message.service.PushMessageService;
import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.order.mapper.ShopOrderReturnMapper;
@ -191,10 +191,11 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
private ShopOrderReturnService shopOrderReturnService;
@Autowired
private ShopProductIndexService shopProductIndexService;
@Lazy
@Autowired
private AccountBaseConfigService accountBaseConfigService;
@Autowired
private ShopOrderLogisticsService shopOrderLogisticsService;
private LklOrderSeparateService lklOrderSeparateService;
@Lazy
@Autowired
private SFExpressApiService sfExpressApiService;
@ -2559,7 +2560,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
try {
// === 1. 参数校验 ===
if (requestParams == null || StrUtil.isBlank(requestParams.getStr("order_id"))) {
return CommonResult.failed("退货请求参数不完整");
return CommonResult.failed("参数不完整");
}
final UserDto currentUser = getCurrentUser();
@ -2571,7 +2572,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (orderInfo == null) return CommonResult.failed("订单不存在");
if (StateCode.ORDER_PAID_STATE_NO == orderInfo.getOrder_is_paid()) {
return CommonResult.failed("订单未付款,无法退款");
return CommonResult.failed("订单未付款");
}
// if (orderInfo.getOrder_state_id() != null
@ -2582,17 +2583,21 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (orderInfo.getOrder_state_id() != null
&& StateCode.ORDER_STATE_CANCEL == orderInfo.getOrder_state_id().intValue()) {
return CommonResult.failed("已取消的订单无法退款");
return CommonResult.failed("订单已取消");
}
if (orderInfo.getStore_id() != null && !orderInfo.getStore_id().equals(Convert.toInt(currentUser.getStore_id()))) {
return CommonResult.failed("无权处理该订单");
return CommonResult.failed("无权处理此订单");
}
if (lklOrderSeparateService.isOrderSeparated(orderId)) {
// TODO 后期如果已经分账的订单一定强硬退款可能需要拉卡拉撤销分账再退款
return CommonResult.failed("订单已三方结清,无法退款");
}
List<ShopOrderItem> orderItems = shopOrderItemService.find(new QueryWrapper<ShopOrderItem>().eq("order_id", orderId));
if (CollectionUtil.isEmpty(orderItems)) return CommonResult.failed("订单没有可退货的商品");
if (CollectionUtil.isEmpty(orderItems)) return CommonResult.failed("无可退货商品");
// === 3. 检查退货单 ===
ShopOrderReturn refundOrder = findOne(new QueryWrapper<ShopOrderReturn>()
.eq("order_id", orderId)
@ -2614,8 +2619,8 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (StrUtil.isNotBlank(requestParams.getStr("order_return_vo"))) {
orderReturnInputVo = JSONUtil.toBean(requestParams.getStr("order_return_vo"), OrderReturnInputVo.class);
// 如果orderReturnInputVo中的order_id为空则使用上层参数中的order_id
if (orderReturnInputVo != null && StrUtil.isBlank(orderReturnInputVo.getOrder_id())) {
// 如果orderReturnInputVo中的order_id为空或与上层参数不一致则使用上层参数中的order_id
if (orderReturnInputVo != null) {
orderReturnInputVo.setOrder_id(orderId);
}
// 传入参数商品种类数量>0的
@ -2639,13 +2644,13 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
&& item.getReturn_item_num() > 0);
if (!validItems) {
return CommonResult.failed("退款商品信息不正确请检查商品ID、退款金额和数量是否有");
return CommonResult.failed("退款金额或数量无");
}
List<OrderReturnItemInputVo> orderReturnItems = orderReturnInputVo.getReturn_items();
refundGoodsAmount = orderReturnItems.stream().map(OrderReturnItemInputVo::getReturn_refund_amount).reduce(BigDecimal.ZERO, BigDecimal::add);
if (refundGoodsAmount.compareTo(orderGoodsAmount) > 0) {
return CommonResult.failed("退款金额有误!退款金额不能大于订单商品总金额");
return CommonResult.failed("退款金额超限");
}
refundRequest.setReturn_buyer_message(StrUtil.isBlank(reason) ? "订单部分商品退款" : reason);
@ -2669,7 +2674,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (createResult == null || createResult.getStatus() != 200) {
log.error("退货单创建失败params:{}", refundRequest);
// 提供更详细的错误信息
String errorMsg = "退货单创建失败";
String errorMsg = "创建退款单失败";
if (createResult != null && createResult.getMsg() != null) {
errorMsg += "" + createResult.getMsg();
}
@ -2691,7 +2696,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
logger.info("商家退款订单:{}", JSONUtil.toJsonStr(refundOrder));
if (!processReviewList(refundOrder, 0)) {
return CommonResult.failed("退款处理失败");
return CommonResult.failed("处理失败");
}
// === 7. 特殊场景同城配送订单取消 ===
@ -2709,7 +2714,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
} catch (Exception e) {
log.error("商家处理退款异常", e);
// 提供更具体的错误信息
String errorMsg = "退款处理异常";
String errorMsg = "处理异常";
if (e instanceof ApiException) {
errorMsg = e.getMessage();
} else if (e.getCause() instanceof ApiException) {
@ -2719,6 +2724,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
}
}
/**
* 获取拒绝退货的详细原因
*

View File

@ -969,7 +969,6 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
// 上传发货信息到微信
wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderId);
} else if (shopStoreSfOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_ARRIVED)) {
// 顺丰同城状态12-配送员到店;
// 商城订单状态 2020-待配货 2030; //待发货