From 8b2a127ce3fbabd95df06028d2db64d45b21b4ff Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 9 Oct 2025 23:31:30 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=89=93=E5=8D=B0=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20=E6=A0=87=E9=A2=98=E5=90=8E=E9=9D=A2=E5=8A=A0?= =?UTF-8?q?=E8=A7=84=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/pojo/dto/LklSeparateDTO.java | 4 +- .../pay/controller/admin/PayController.java | 20 ++- .../mall/pay/service/LakalaPayService.java | 25 ++++ .../service/impl/LakalaPayServiceImpl.java | 135 ++++++++++++++++++ .../impl/ShopOrderReturnServiceImpl.java | 26 ++-- 5 files changed, 195 insertions(+), 15 deletions(-) diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java index 5ddeedbc..b8abf1ff 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java @@ -61,12 +61,12 @@ public class LklSeparateDTO { // 测试基于可分账金额的分账算法(正常情况) System.out.println("\n=== 基于可分账金额的分账算法测试(正常情况) ==="); LklSeparateDTO dto2 = new LklSeparateDTO(); - dto2.setTotalSeparateAmount(900); // 分账总额 1000分 + dto2.setTotalSeparateAmount(1000); // 分账总额 1000分 dto2.setShippingFee(500); // 配送费 100分 // dto2.setRefCanSeparateAmount(null); dto2.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025 dto2.setMchRatio(new BigDecimal("0.95")); // 商家分账比例 0.857 (会产生小数) - dto2.setPlatRatio(new BigDecimal("0.01")); // 平台分账比例 0.01 + dto2.setPlatRatio(new BigDecimal("0.05")); // 平台分账比例 0.01 // 不设置一级和二级代理商分账比例,测试不参与分账的情况 // dto2.setAgent1stRatio(new BigDecimal("0.11")); // 一级代理商分账比例 0.023 (会产生小数) // dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数) diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/controller/admin/PayController.java b/mall-pay/src/main/java/com/suisung/mall/pay/controller/admin/PayController.java index b8ca6d0c..fa853514 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/controller/admin/PayController.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/controller/admin/PayController.java @@ -1,6 +1,7 @@ package com.suisung.mall.pay.controller.admin; import cn.hutool.core.util.NumberUtil; +import cn.hutool.json.JSONArray; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.order.ShopOrderReturn; @@ -17,6 +18,7 @@ import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.util.Pair; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -56,6 +58,8 @@ public class PayController { private PayPlantformResourceService payPlantformResourceService; @Autowired private PayUserPayService payUserPayService; + @Autowired + private LakalaPayService lakalaPayService; @ApiOperation(value = "根据user_id 删除门店顾客关系数据", notes = "根据user_id 删除门店顾客关系数据") @RequestMapping(value = "/deleteUserChainByUid", method = RequestMethod.POST) @@ -410,8 +414,22 @@ public class PayController { @ApiOperation(value = "批量保存", notes = "用户资源表") @RequestMapping(value = "/saveBatchPayUserResources", method = {RequestMethod.POST}) public ThirdApiRes saveBatchPayUserResources(@RequestBody List payUserResourceList) { - return payUserPayService.saveBatchPayUserResources(payUserResourceList); + return payUserPayService.saveBatchPayUserResources(payUserResourceList); } + /** + * 退款通知 + */ + @ApiOperation(value = "【测试】平台强制到拉卡拉退款", notes = "平台强制到拉卡拉退款") + @RequestMapping(value = "/lkl/refund", method = {RequestMethod.POST}) + public CommonResult refundFromLakala(HttpServletRequest request, @RequestBody JSONArray returnJsonArray) { + Pair pair = lakalaPayService.innerLklRefundForDemo(request, returnJsonArray); + if (pair.getFirst()) { + return CommonResult.success(pair.getSecond()); + } + return CommonResult.failed(pair.getSecond()); + } + + } diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java index f9e42fb4..251ee499 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java @@ -8,9 +8,12 @@ package com.suisung.mall.pay.service; +import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import org.springframework.data.util.Pair; +import javax.servlet.http.HttpServletRequest; + public interface LakalaPayService { Boolean initLKLSDK(); @@ -70,6 +73,28 @@ public interface LakalaPayService { */ Pair innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason, String lklMerchantNo, String lklTermNo); + + /** + * 执行内部拉卡拉交易退款(测试) + * 参考地址:https://o.lakala.com/#/home/document/detail?id=113 + * 参数示例 + * [ + * { + * "sub_trade_no": "20251009110113130266250070712668", + * "merchant_no": "822584059990FYP", + * "amount": "500", + * "settle_type": "0", + * "sub_log_no": "66250070712668", + * "out_sub_trade_no": "DF_DD_20251009_2", + * "term_no": "N5811590" + * } + * ] + * + * @param returnJsonArray 退款信息数组 + * @return Pair,包含退款是否成功以及消息 + */ + Pair innerLklRefundForDemo(HttpServletRequest request, JSONArray returnJsonArray); + /** * 文件上传 * 参考:https://o.lakala.com/#/home/document/detail?id=90 diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java index 7777d5df..5044240d 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java @@ -22,6 +22,7 @@ import com.lkl.laop.sdk.request.V2MmsOpenApiUploadFileRequest; import com.lkl.laop.sdk.request.V3LabsRelationRefundRequest; import com.lkl.laop.sdk.request.model.V3LabsTradeLocationInfo; import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.feignService.ShopService; import com.suisung.mall.common.modules.store.ShopStoreBase; @@ -44,6 +45,8 @@ import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; +import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser; + @Slf4j @Service @@ -634,6 +637,138 @@ public class LakalaPayServiceImpl implements LakalaPayService { } + /** + * 执行内部拉卡拉交易退款 + * 参考地址:https://o.lakala.com/#/home/document/detail?id=113 + * 参数示例 + * [ + * { + * "sub_trade_no": "20251009110113130266250070712668", + * "merchant_no": "822584059990FYP", + * "amount": "500", + * "settle_type": "0", + * "sub_log_no": "66250070712668", + * "out_sub_trade_no": "DF_DD_20251009_2", + * "term_no": "N5811590" + * } + * ] + * + * @param returnJsonArray 退款信息数组 + * @return Pair,包含退款是否成功以及消息 + */ + @Override + public Pair innerLklRefundForDemo(HttpServletRequest request, JSONArray returnJsonArray) { + try { + log.info("[拉卡拉退款Demo] 开始执行拉卡拉内部退款,退款订单数: {}", + returnJsonArray != null ? returnJsonArray.size() : 0); + + UserDto userDto = getCurrentUser(); + if (userDto == null || !userDto.isAdmin()) { + log.error("[拉卡拉退款Demo] 非管理员用户尝试执行退款, userId={}", + userDto != null ? userDto.getId() : "unknown"); + return Pair.of(false, I18nUtil._("非管理员用户尝试执行拉卡拉内部退款,请勿非法操作!")); + } + + if (returnJsonArray == null || returnJsonArray.isEmpty()) { + log.error("[拉卡拉退款Demo] 退款信息为空"); + return Pair.of(false, I18nUtil._("退款信息为空,退款失败!")); + } + + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) { + log.error("[拉卡拉退款Demo] 无法获取请求上下文"); + return Pair.of(false, I18nUtil._("系统异常,无法获取请求信息!")); + } + + String requestIp = IpKit.getRealIp(request); + if (StrUtil.isBlank(requestIp)) { + requestIp = request.getRemoteAddr(); + } + + String refundReason = "系统触发退款"; + int successCount = 0; + int totalCount = returnJsonArray.size(); + + // 初始化拉卡拉SDK + initLKLSDK(); + + for (int i = 0; i < returnJsonArray.size(); i++) { + JSONObject returnJsonObject = returnJsonArray.getJSONObject(i); + + // 参数提取 + String outTradeNo = returnJsonObject.getStr("out_sub_trade_no"); + String originTradeNo = returnJsonObject.getStr("sub_trade_no"); + String refundAmount = returnJsonObject.getStr("amount"); + String lklMerchantNo = returnJsonObject.getStr("merchant_no"); + String lklTermNo = returnJsonObject.getStr("term_no"); + + // 参数校验 + if (StrUtil.hasBlank(outTradeNo, originTradeNo, refundAmount, lklMerchantNo, lklTermNo)) { + log.warn("[拉卡拉退款Demo] 第{}笔退款参数不完整, 跳过处理", i + 1); + continue; + } + + // 金额格式校验 + if (!refundAmount.matches("\\d+") || Integer.parseInt(refundAmount) <= 0) { + log.warn("[拉卡拉退款Demo] 第{}笔退款金额格式不正确: {}, 跳过处理", i + 1, refundAmount); + continue; + } + + // 构造退款请求 + V3LabsRelationRefundRequest refundRequest = new V3LabsRelationRefundRequest(); + refundRequest.setOutTradeNo(outTradeNo); + refundRequest.setOriginTradeNo(originTradeNo); + refundRequest.setMerchantNo(lklMerchantNo); + refundRequest.setTermNo(lklTermNo); + refundRequest.setRefundAmount(refundAmount); + refundRequest.setRefundReason(refundReason); + refundRequest.setLocationInfo(new V3LabsTradeLocationInfo(requestIp, null, "")); + + // 发送请求 + String responseString = LKLSDK.httpPost(refundRequest); + + // 处理响应 + if (StrUtil.isBlank(responseString)) { + log.error("[拉卡拉退款Demo] 第{}笔退款无响应, outTradeNo={}", i + 1, outTradeNo); + continue; + } + + JSONObject lakalaResponseJson = JSONUtil.parseObj(responseString); + if (lakalaResponseJson == null) { + log.error("[拉卡拉退款Demo] 第{}笔退款响应解析失败, outTradeNo={}", i + 1, outTradeNo); + continue; + } + + String responseCode = lakalaResponseJson.getStr("code"); + if (StrUtil.isBlank(responseCode)) { + log.error("[拉卡拉退款Demo] 第{}笔退款响应码为空, outTradeNo={}", i + 1, outTradeNo); + continue; + } + + if (!"BBS00000".equals(responseCode)) { + String errorMessage = lakalaResponseJson.getStr("msg", "未知错误"); + log.error("[拉卡拉退款Demo] 第{}笔退款失败, 响应码: {}, 错误信息: {}, outTradeNo={}", + i + 1, responseCode, errorMessage, outTradeNo); + continue; + } + + successCount++; + log.info("[拉卡拉退款Demo] 第{}笔退款成功, outTradeNo={}", i + 1, outTradeNo); + } + + log.info("[拉卡拉退款Demo] 退款处理完成,总笔数: {}, 成功笔数: {}", totalCount, successCount); + return Pair.of(true, I18nUtil._("退款成功!") + successCount + "笔/" + totalCount + "笔"); + + } catch (SDKException e) { + log.error("[拉卡拉退款Demo] SDK异常", e); + return Pair.of(false, I18nUtil._("拉卡拉退款SDK异常,退款失败!") + e.getMessage()); + } catch (Exception e) { + log.error("[拉卡拉退款Demo] 未知异常", e); + return Pair.of(false, I18nUtil._("拉卡拉退款发生未知异常,退款失败!") + e.getMessage()); + } + } + + /** * 文件上传 * 参考:https://o.lakala.com/#/home/document/detail?id=90 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java index f10dd34d..ef26333c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java @@ -1278,6 +1278,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl return_ids, List return_rows, Integer return_curr_state_id, Integer return_next_state_id) { + logger.info("退款审核 processReviewList 参数:return_ids {},return_rows {},return_curr_state_id {},return_next_state_id {}", + return_ids, return_rows, return_curr_state_id, return_next_state_id); + if (CollUtil.isEmpty(return_ids)) { throw new ApiException(I18nUtil._("请选择需要审核的退单!")); } @@ -1415,6 +1419,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl orderItemQueryWrapper = new QueryWrapper<>(); orderItemQueryWrapper.eq("order_id", order_id); @@ -1589,7 +1594,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl forbiddenStates = Arrays.asList( StateCode.ORDER_STATE_SHIPPED, StateCode.ORDER_STATE_RECEIVED, @@ -1599,7 +1604,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl