diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java
index 52c0ec0f..5837be79 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java
@@ -110,4 +110,9 @@ public class CommonConstant {
//秒杀活动订阅消息模板id
public static final String BIND_SUB_TMPL_SKILL = "kiDj_hSF_ASwD-Dlgxnypi6IJBQZ12a-hEpd3zZ-Uxc";
+ //分账计算方式:1-按总金额;2-按可分账金额;
+ public static final int SeparateCalcMode_TotalAmt = 1;
+ public static final int SeparateCalcMode_CanSeparateAmt = 2;
+
+
}
diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/RedisConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/RedisConstant.java
index 86e7577c..237c6fa5 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/constant/RedisConstant.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/constant/RedisConstant.java
@@ -37,5 +37,8 @@ public class RedisConstant {
public static final String SF_Order_Proc_WillExpire_Key = ConstantRedis.Cache_NameSpace + "sf_order_proc_will_expire_key__";
public static final String Order_Pay_Retry_Count_Key = ConstantRedis.Cache_NameSpace + "order_pay_retry_count:";
-
+
+ // 您有新的订单来了
+ public static final String New_Order_Push_Flag_Key = ConstantRedis.Cache_NameSpace + "new:order:comimg:";
+
}
diff --git a/mall-common/src/main/java/com/suisung/mall/common/exception/GlobalExceptionHandler.java b/mall-common/src/main/java/com/suisung/mall/common/exception/GlobalExceptionHandler.java
index 850a999c..f36913fe 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/exception/GlobalExceptionHandler.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/exception/GlobalExceptionHandler.java
@@ -3,6 +3,7 @@ package com.suisung.mall.common.exception;
import cn.hutool.core.util.StrUtil;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.api.ResultCode;
+import io.seata.rm.datasource.exec.LockWaitTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
@@ -91,13 +92,14 @@ public class GlobalExceptionHandler {
/**
* 处理系统级异常(数据库异常/通用异常)
*/
- @ExceptionHandler({SQLException.class, DataAccessException.class, Exception.class})
+ @ExceptionHandler({SQLException.class, DataAccessException.class, LockWaitTimeoutException.class, Exception.class})
public CommonResult handleSystemException(HttpServletRequest req, Exception e) {
logError(req, e.getMessage(), e);
if (e instanceof SQLException || e instanceof DataAccessException) {
return CommonResult.failed("系统数据异常,请联系管理员!");
}
+
return CommonResult.failed("系统内部异常,请联系管理员!");
}
diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderData.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderData.java
index d00071c8..152a1839 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderData.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderData.java
@@ -92,6 +92,9 @@ public class ShopOrderData implements Serializable {
@ApiModelProperty(value = "实际运费金额-卖家可修改")
private BigDecimal order_shipping_fee;
+ @ApiModelProperty(value = "平台内部运费金额")
+ private BigDecimal order_shipping_fee_inner;
+
@ApiModelProperty(value = "总计分账金额(从拉卡拉上分账,分给平台和代理商费用),单位:元")
private BigDecimal total_separate_value;
diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java
index 09dc7562..002a4a4b 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java
@@ -72,6 +72,8 @@ public class MchOrderInfoDTO implements Serializable {
private Integer delivery_type_id;
@ApiModelProperty(value = "订单运费")
private BigDecimal order_shipping_fee;
+ @ApiModelProperty(value = "平台内部配送费")
+ private BigDecimal order_shipping_fee_inner;
@ApiModelProperty(value = "平台费")
private BigDecimal platform_fee;
@ApiModelProperty(value = "店铺统一设置的打包费")
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 4f454da4..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,15 +61,15 @@ public class LklSeparateDTO {
// 测试基于可分账金额的分账算法(正常情况)
System.out.println("\n=== 基于可分账金额的分账算法测试(正常情况) ===");
LklSeparateDTO dto2 = new LklSeparateDTO();
- dto2.setTotalSeparateAmount(1); // 分账总额 1000分
- dto2.setShippingFee(0); // 配送费 100分
+ dto2.setTotalSeparateAmount(1000); // 分账总额 1000分
+ dto2.setShippingFee(500); // 配送费 100分
// dto2.setRefCanSeparateAmount(null);
dto2.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025
- dto2.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.857 (会产生小数)
- dto2.setPlatRatio(new BigDecimal("0.01")); // 平台分账比例 0.01
+ dto2.setMchRatio(new BigDecimal("0.95")); // 商家分账比例 0.857 (会产生小数)
+ dto2.setPlatRatio(new BigDecimal("0.05")); // 平台分账比例 0.01
// 不设置一级和二级代理商分账比例,测试不参与分账的情况
- // dto2.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.023 (会产生小数)
- // dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数)
+// dto2.setAgent1stRatio(new BigDecimal("0.11")); // 一级代理商分账比例 0.023 (会产生小数)
+// dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数)
SharingResult result2 = dto2.sharingOnCanSeparateAmount();
System.out.println(result2);
diff --git a/mall-common/src/main/java/com/suisung/mall/common/service/impl/UniCloudPushServiceImpl.java b/mall-common/src/main/java/com/suisung/mall/common/service/impl/UniCloudPushServiceImpl.java
index 8f4457a3..0569d890 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/service/impl/UniCloudPushServiceImpl.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/service/impl/UniCloudPushServiceImpl.java
@@ -19,6 +19,7 @@ import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import java.util.stream.Collectors;
/**
@@ -125,16 +126,6 @@ public class UniCloudPushServiceImpl implements UniCloudPushService {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
-
-// HttpHeaders headers = new HttpHeaders();
-// headers.setContentType(MediaType.APPLICATION_JSON); // Content-Type: application/json
-// headers.add("Accept", "*/*");
-// headers.add("Host", "fc-mp-39e3d50a-2d2b-415a-9664-2e48974bcfbd.next.bspapp.com");
-// headers.add("Connection", "keep-alive");
-// headers.add("Authorization", "Basic ZWxhc3TiYzpQQjI1NkZFTjBPaDY0cFZV");
-// headers.add("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
-// headers.add("Cookie", "aliyungf_tc=3a871d6048f74707aa0ac71c13f654c4d0fba0471c40625f4c120b6aca248dcf; acw_tc=ac11000117529360358682662e17bea869dfc6fb823bd2d372dbf9eca1c342");
-// return headers;
}
/**
@@ -150,21 +141,23 @@ public class UniCloudPushServiceImpl implements UniCloudPushService {
clientIds.size(), distinctClientIds.size());
}
+ // 新增:生成消息唯一标识符
+ String messageId = UUID.randomUUID().toString().replace("-", "");
+
request.put("push_clientid", distinctClientIds)
.put("title", title)
- .put("content", content);
+ .put("content", content)
+ .put("message_id", messageId); // 添加唯一消息ID
if (payload != null) {
request.put("payload", payload);
}
request.put("settings", new JSONObject().set("ttl", DEFAULT_TTL));
-
log.debug("[推送服务] 请求参数: {}", request);
-
return request;
}
-
+
/**
* 处理推送响应
*/
diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/CheckUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/CheckUtil.java
index e1e66e23..2b1dd6f8 100644
--- a/mall-common/src/main/java/com/suisung/mall/common/utils/CheckUtil.java
+++ b/mall-common/src/main/java/com/suisung/mall/common/utils/CheckUtil.java
@@ -188,28 +188,80 @@ public class CheckUtil {
return true;
}
+ /**
+ * 校验数据的某个字段是否为指定值
+ *
+ * @param value 指定值的字段值
+ * @param data 需要对比的数据Map
+ * @param key 字段名
+ * @return boolean 字段值等于指定值返回true,否则返回false
+ */
public static boolean checkDataRights(Integer value, Map data, String key) {
- if (CollUtil.isEmpty(data)) return false;
+ // 检查数据是否为空或key是否为空
+ if (CollUtil.isEmpty(data) || StrUtil.isBlank(key)) {
+ log.debug("数据为空或key为空, data: {}, key: {}", data, key);
+ return false;
+ }
+ // 从数据中获取指定key的值并转换为Integer类型
Integer _value = Convert.toInt(data.get(key));
- return ObjectUtil.equal(_value, value);
+ // 比较获取的值与指定值是否相等
+ boolean result = ObjectUtil.equal(_value, value);
+ log.info("校验数据权限, key: {}, 值: {}==数据值: {} ? 结果: {}", key, value, _value, result);
+ return result;
}
+ /**
+ * 校验数据对象的某个字段是否为指定值
+ *
+ * @param value 指定值的字段值
+ * @param data 需要对比的数据对象
+ * @param idMapper 获取数据对象ID的函数
+ * @param 数据对象类型
+ * @return boolean 字段值等于指定值返回true,否则返回false
+ */
public static boolean checkDataRights(Integer value, T data, Function idMapper) {
- if (ObjectUtil.isEmpty(data)) return false;
+ // 检查数据对象或映射函数是否为空
+ if (ObjectUtil.isEmpty(data) || ObjectUtil.isEmpty(idMapper)) {
+ log.debug("数据对象或映射函数为空, data: {}, idMapper: {}", data, idMapper);
+ return false;
+ }
+ // 通过映射函数获取数据对象的值
Object _value = idMapper.apply(data);
- return ObjectUtil.equal(_value, value);
+ // 比较获取的值与指定值是否相等
+ boolean result = ObjectUtil.equal(_value, value);
+ log.debug("校验单个对象数据权限, 值: {}, 数据值: {}, 结果: {}", value, _value, result);
+ return result;
}
+ /**
+ * 校验数据列表中每个对象的某个字段是否为指定值
+ *
+ * @param value 指定值的字段值
+ * @param data 需要对比的数据列表
+ * @param idMapper 获取数据对象ID的函数
+ * @param 数据对象类型
+ * @return boolean 所有对象字段值都等于指定值返回true,否则返回false
+ */
public static boolean checkDataRights(Integer value, List data, Function idMapper) {
- if (CollUtil.isEmpty(data)) return false;
+ // 检查数据列表或映射函数是否为空
+ if (CollUtil.isEmpty(data) || ObjectUtil.isEmpty(idMapper)) {
+ log.debug("数据列表或映射函数为空, 数据大小: {}, 映射函数: {}",
+ data != null ? data.size() : 0, idMapper);
+ return false;
+ }
+ // 遍历数据列表中的每个对象
for (T datum : data) {
+ // 递归校验每个对象,如果有任何一个不匹配则返回false
if (!checkDataRights(value, datum, idMapper)) {
+ log.debug("列表数据权限校验失败,失败项: {}", datum);
return false;
}
}
+ log.debug("列表数据权限校验成功, 数据大小: {}, 值: {}", data.size(), value);
return true;
}
+
public static String addslashes(String str) {
if (str == null) {
return null;
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 af84ef61..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
@@ -574,6 +577,10 @@ public class LakalaPayServiceImpl implements LakalaPayService {
// TODO 重要的逻辑,获取是否已经分账?已分账:分账退回;查商家账户余额够不够退回?够就执行退回
+ if (StrUtil.isBlank(refundReason)) {
+ refundReason = "商家与买方协商退款";
+ }
+
// 5. 构造退款请求并发送
V3LabsRelationRefundRequest refundRequest = new V3LabsRelationRefundRequest();
refundRequest.setOutTradeNo(outTradeNo);
@@ -630,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-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java
index 873fc809..a8678096 100644
--- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java
+++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java
@@ -188,7 +188,7 @@ public class PayConsumeTradeServiceImpl extends BaseServiceImpl return_rows) {
- List paid_return_id_row = new ArrayList<>();
-
- // 原路退回标记
- boolean order_refund_flag = accountBaseConfigService.getConfig("order_refund_flag", false);
- List order_id_rows = return_rows.stream().map(ShopOrderReturn::getOrder_id).distinct().collect(Collectors.toList());
-
- List order_data_rows = shopService.getsShopOrderData(order_id_rows);
-
- List store_ids = return_rows.stream().map(ShopOrderReturn::getStore_id).distinct().collect(Collectors.toList());
- List
*
- * @param request
+ * @param request HTTP请求对象,包含拉卡拉分账结果通知的参数
* @param merchantNoParam 分账方商户号,非空表示是事后补偿
* @param separateNoParam 分账指令流水号,非空表示是事后补偿
- *
- * - 成功: {"code": "SUCCESS", "message": "操作成功"}
- * - 失败: {"code": "FAIL", "message": "错误信息"}
- *
+ * @return 处理结果JSON对象
+ *
+ * - 成功: {"code": "SUCCESS", "message": "操作成功"}
+ * - 失败: {"code": "FAIL", "message": "错误信息"}
+ *
*/
@Override
public JSONObject sacsSeparateNotify(HttpServletRequest request, String merchantNoParam, String separateNoParam) {
@@ -2708,7 +2715,8 @@ public class LakalaApiServiceImpl implements LakalaApiService {
JSONObject paramsJson = null;
if (!StrUtil.hasBlank(merchantNoParam, separateNoParam)) {
channel = "(补偿)";
- log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果通知异步回调 merchantNoParam={},separateNoParam={}" + channel, merchantNoParam, separateNoParam);
+ log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果通知异步回调{},merchantNoParam={},separateNoParam={}",
+ channel, merchantNoParam, separateNoParam);
paramsJson = sacsQuery(merchantNoParam, separateNoParam);
} else {
log.info("[拉卡拉分账通知] 开始处理拉卡拉分账结果通知异步回调");
@@ -2742,15 +2750,15 @@ public class LakalaApiServiceImpl implements LakalaApiService {
String status = paramsJson.getStr("status");
String finalStatus = paramsJson.getStr("final_status");
- // 必要参数
+ // 必要参数校验
List missingParams = new ArrayList<>();
if (StrUtil.isBlank(outSeparateNo)) missingParams.add("outSeparateNo");
if (StrUtil.isBlank(separateNo)) missingParams.add("separateNo");
if (StrUtil.isBlank(status)) missingParams.add("status");
if (!missingParams.isEmpty()) {
- String errorMsg = "分账通知缺少必要参数" + channel + String.join(", ", missingParams);
- log.error("[拉卡拉分账通知] {} {},参数详情: {}", channel, errorMsg, paramsJson);
+ String errorMsg = "分账通知缺少必要参数" + channel + ": " + String.join(", ", missingParams);
+ log.error("[拉卡拉分账通知] {},参数详情: {}", errorMsg, paramsJson);
return JSONUtil.createObj()
.put("code", "FAIL")
.put("message", errorMsg);
@@ -2761,7 +2769,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
if (lklOrderSeparateExist == null) {
String errorMsg = "未找到对应的分账记录" + channel;
log.error("[拉卡拉分账通知] {},外部分账单号={},分账单号={},参数详情: {}",
- errorMsg, outSeparateNo, separateNoParam, paramsJson);
+ errorMsg, outSeparateNo, separateNo, paramsJson);
return JSONUtil.createObj()
.put("code", "FAIL")
.put("message", errorMsg);
@@ -2779,8 +2787,8 @@ public class LakalaApiServiceImpl implements LakalaApiService {
}
// 6. 记录关键参数信息,便于问题排查
- log.info("[拉卡拉分账通知] 接收到分账通知,分账单号={},外部分账单号={},状态={},最终状态={}" + channel,
- separateNo, outSeparateNo, status, finalStatus);
+ log.info("[拉卡拉分账通知] 接收到分账通知{},分账单号={},外部分账单号={},状态={},最终状态={}",
+ channel, separateNo, outSeparateNo, status, finalStatus);
// 7. 构建分账记录对象 - 准备更新数据库的分账记录
LklOrderSeparate lklOrderSeparate = new LklOrderSeparate();
@@ -2819,14 +2827,34 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 更改分账状态:分账成功
shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, "");
+ // 本次分账成功后,给商户和接收方 D1提现到银行卡
+ if (CollUtil.isNotEmpty(detailDatas)) {
+ int idx = 1;
+ for (JSONObject detailData : detailDatas.jsonIter()) {
+ if (detailData == null || detailData.isEmpty()) {
+ continue;
+ }
+
+ String recvNo = Convert.toStr(detailData.get("recv_no"), "");
+ String amt = Convert.toStr(detailData.get("amt"), "");
+ if (StrUtil.isNotBlank(recvNo) && StrUtil.isNotBlank(amt)) {
+ String outSeparateNoTemp = String.format("%s_%d", outSeparateNo, idx);
+ Boolean drawSuccess = ewalletWithDrawD1(recvNo, outSeparateNoTemp, amt, JSONUtil.toJsonStr(detailDatas));
+ log.info("[拉卡拉分账通知] 账户D1提现{},商户号={},分账单号={},金额={}分",
+ Boolean.TRUE.equals(drawSuccess) ? "成功" : "失败", recvNo, outSeparateNoTemp, amt);
+ idx++;
+ }
+ }
+ }
+
// 9. 记录处理成功日志
- log.info("[拉卡拉分账通知] 分账通知处理成功, separateNo={}, status={}" + channel, 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" + channel, e.getMessage());
+ String errorMsg = String.format("分账通知数据处理异常: %s%s", e.getMessage(), channel);
log.error("[拉卡拉分账通知] {}", errorMsg, e);
return JSONUtil.createObj()
.put("code", "FAIL")
@@ -2920,15 +2948,15 @@ public class LakalaApiServiceImpl implements LakalaApiService {
* @return 成功处理的记录数量
*/
@Override
- public Integer fixedUnSuccessSeparateStatusJob() {
+ public Integer fixUnSuccessSeparateStatusJob() {
log.info("[分账状态修复任务] 开始执行未成功分账记录的状态修复任务");
- // 获取3天前分账状态未成功的记录
+ // 获取2天前分账状态未成功的记录
Date now = new Date();
- Date threeDaysAgo = DateUtils.addHours(now, -72);
+ Date threeDaysAgo = DateUtils.addHours(now, -48);
// 分页参数
- int pageSize = 100;
+ int pageSize = 200;
int currentPage = 1;
int totalSuccessCount = 0;
int totalProcessed = 0;
@@ -3384,12 +3412,11 @@ public class LakalaApiServiceImpl implements LakalaApiService {
* @param mercId 822商户号或receiveNo
* @param merOrderNo 商户订单号
* @param drawAmt 提现金额(分)
- * @param remark 备注信息
* @param summary 摘要信息
* @return 操作结果,成功返回true,失败返回false
*/
@Override
- public Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String remark, String summary) {
+ public Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String summary) {
// 1. 参数校验
if (StrUtil.hasBlank(mercId, merOrderNo, drawAmt)) {
log.warn("[D1提现申请] D1提现参数校验失败,关键参数为空: mercId={}, merOrderNo={}, drawAmt={}",
@@ -3399,8 +3426,10 @@ public class LakalaApiServiceImpl implements LakalaApiService {
// 账号类型(01:收款账户,04:分账接收方账户)
String payType = "04";
+ String payTypeStr = "接收方账户04";
if (StrUtil.startWith(mercId, "822")) {
payType = "01";
+ payTypeStr = "收款账户01";
}
try {
@@ -3411,10 +3440,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
return false;
}
- // 设置默认值
- if (StrUtil.isBlank(remark)) {
- remark = String.format("商户订单:%s 账号类型:%s 分账后立即提现", merOrderNo, payType);
- }
+ String remark = String.format("订单:%s %s 分账后立即提现", merOrderNo, payTypeStr);
log.info("[D1提现申请] 开始处理D1提现,商户号={},订单号={},提现金额={}分", mercId, merOrderNo, drawAmt);
@@ -3587,4 +3613,111 @@ public class LakalaApiServiceImpl implements LakalaApiService {
}
}
+
+ /**
+ * 商户分账参数计算及评估
+ *
+ * @param splitMode 分账模式:1-总金额为基准分账,2-可分账金额基准分账,必填参数
+ * @param orderPayAmount 订单支付总金额(单位:分)必填参数
+ * @param shippingFeeInner 平台内部配送费(单位:分)必填参数
+ * @param mchSplitRatioRaw 商户分账比例值(分子值,如10表示10%)必填参数
+ * @param platSplitRatio 平台分账比例(百分比值,如0.01表示1%)可选参数
+ * @param agent1stRatio 一级分账比例(百分比值,如0.01表示1%)可选参数
+ * @param agent2ndRatio 二级分账比例(百分比值,如0.01表示1%)可选参数
+ * @param refCanSeparateAmt 参考可分金额(单位:分) 可选参数
+ * @return Pair 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象
+ */
+ @Override
+ public Pair calculateAndEvaluateSharingParams(int splitMode,
+ Integer orderPayAmount,
+ Integer shippingFeeInner,
+ BigDecimal mchSplitRatioRaw,
+ BigDecimal platSplitRatio,
+ BigDecimal agent1stRatio,
+ BigDecimal agent2ndRatio,
+ Integer refCanSeparateAmt) {
+ log.debug("[分账参数计算] 开始计算分账参数: splitMode={}, orderPayAmount={}, shippingFeeInner={}, " +
+ "mchSplitRatioRaw={}, platSplitRatio={}, agent1stRatio={}, agent2ndRatio={}, refCanSeparateAmt={}",
+ splitMode, orderPayAmount, shippingFeeInner, mchSplitRatioRaw, platSplitRatio,
+ agent1stRatio, agent2ndRatio, refCanSeparateAmt);
+
+ // 参数校验
+ if (orderPayAmount == null || orderPayAmount <= 0) {
+ log.warn("[分账参数计算] 订单支付金额参数无效: orderPayAmount={}", orderPayAmount);
+ return Pair.of(false, null);
+ }
+
+ if (mchSplitRatioRaw == null || mchSplitRatioRaw.compareTo(BigDecimal.ZERO) <= 0 || mchSplitRatioRaw.compareTo(BigDecimal.valueOf(100)) > 0) {
+ log.warn("[分账参数计算] 商户分账比例参数无效: mchSplitRatioRaw={}", mchSplitRatioRaw);
+ return Pair.of(false, null);
+ }
+
+ if (splitMode != 1 && splitMode != 2) {
+ log.warn("[分账参数计算] 分账模式参数错误: splitMode={}", splitMode);
+ return Pair.of(false, null);
+ }
+
+ // 计算商家分账比例(转换为小数)
+ BigDecimal mchSplitRatio = mchSplitRatioRaw.divide(new BigDecimal(100));
+ log.debug("[分账参数计算] 商家分账比例转换: {} -> {}", mchSplitRatioRaw, mchSplitRatio);
+
+ // 平台分账比例处理
+ BigDecimal platformSplitRatio = platSplitRatio;
+ if (platformSplitRatio == null || platformSplitRatio.compareTo(BigDecimal.ZERO) <= 0) {
+ platformSplitRatio = BigDecimal.valueOf(0.01); // 默认平台分账1%
+ log.debug("[分账参数计算] 使用默认平台分账比例: {}", platformSplitRatio);
+ }
+
+ // 内部配送费处理
+ Integer actualShippingFeeInner = CheckUtil.isEmpty(shippingFeeInner) ? 0 : shippingFeeInner;
+ BigDecimal wxFeeRatio = StrUtil.isEmpty(wxFee) ? BigDecimal.valueOf(0.0025) : new BigDecimal(wxFee).divide(BigDecimal.valueOf(100));
+ log.debug("[分账参数计算] 配送费: {}, 拉卡拉费率: {}", actualShippingFeeInner, wxFeeRatio);
+
+ // 构建分账参数对象
+ LklSeparateDTO lklSeparateDTO = new LklSeparateDTO();
+ lklSeparateDTO.setTotalSeparateAmount(orderPayAmount);
+ lklSeparateDTO.setShippingFee(actualShippingFeeInner);
+ lklSeparateDTO.setLklRatio(wxFeeRatio); // 拉卡拉给的微信分账比例 0.0025 千分之2.5
+ lklSeparateDTO.setMchRatio(mchSplitRatio);
+ lklSeparateDTO.setPlatRatio(platformSplitRatio);
+
+ // 设置代理商分账比例
+ if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) {
+ lklSeparateDTO.setAgent1stRatio(agent1stRatio);
+ log.debug("[分账参数计算] 设置一级代理商分账比例: {}", agent1stRatio);
+ }
+
+ if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) {
+ lklSeparateDTO.setAgent2ndRatio(agent2ndRatio);
+ log.debug("[分账参数计算] 设置二级代理商分账比例: {}", agent2ndRatio);
+ }
+
+ // 设置参考可分账金额
+ if (refCanSeparateAmt != null && refCanSeparateAmt > 0) {
+ lklSeparateDTO.setRefCanSeparateAmount(refCanSeparateAmt);
+ log.debug("[分账参数计算] 设置参考可分账金额: {}", refCanSeparateAmt);
+ }
+
+ // 根据分账模式执行不同的分账计算
+ LklSeparateDTO.SharingResult canSeparateAmtResult;
+ if (splitMode == 1) {
+ // 总金额为基准分账
+ log.debug("[分账参数计算] 使用总金额为基准分账模式");
+ canSeparateAmtResult = lklSeparateDTO.sharingOnTotalAmount();
+ } else {
+ // 可分金额基准分账
+ log.debug("[分账参数计算] 使用可分账金额基准分账模式");
+ canSeparateAmtResult = lklSeparateDTO.sharingOnCanSeparateAmount();
+ }
+
+ if (!canSeparateAmtResult.isSuccess()) {
+ log.warn("[分账参数计算] 分账参数评估失败: {}", canSeparateAmtResult.getErrorMessage());
+ return Pair.of(false, lklSeparateDTO);
+ }
+
+ log.info("[分账参数计算] 分账参数计算评估成功, result={}", lklSeparateDTO);
+ return Pair.of(true, lklSeparateDTO);
+ }
+
+
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java
index 5ee629a2..fff857fb 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java
@@ -80,6 +80,8 @@ import static com.suisung.mall.common.utils.I18nUtil._;
@Slf4j
public class ShopMessageTemplateServiceImpl extends BaseServiceImpl implements ShopMessageTemplateService {
+ // 批处理阈值
+ private static final int BATCH_SIZE = 30;
@Autowired
ShopStoreBaseService shopStoreBaseService;
@Autowired
@@ -90,19 +92,13 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl mobiles, String tmplCode, Map tmplParams) {
+ // 过滤重复手机号,避免重复发送短信
+ List uniqueMobiles = mobiles.stream()
+ .filter(StrUtil::isNotBlank)
+ .distinct()
+ .collect(Collectors.toList());
+
int successCnt = 0;
- for (String mobile : mobiles) {
+ for (String mobile : uniqueMobiles) {
if (aliyunSmsSend(mobile, tmplCode, tmplParams)) {
successCnt++;
}
@@ -676,22 +678,23 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>();
queryWrapper.eq("tplmsg_id", 1022);//todo 后期改为动态
ShopWechatTplmsg wechatTplmsg = shopWechatTplmsgService.getOne(queryWrapper);
- ShopStoreBase shopStoreBase= shopStoreBaseService.get(wechatTplmsg.getStore_id());
+ ShopStoreBase shopStoreBase = shopStoreBaseService.get(wechatTplmsg.getStore_id());
args.put("storeName", shopStoreBase.getStore_name());
if (wechatTplmsg != null) {
- Map timeArgs=getActiveTime();
+ Map timeArgs = getActiveTime();
args.putAll(timeArgs);
String wechatTplData = getXcxWechatTplData(open_id, wechatTplmsg, args);
log.info(wechatTplData);
@@ -720,10 +723,10 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>();
queryWrapper.eq("tplmsg_id", 1022);//todo 后期改为动态
ShopWechatTplmsg wechatTplmsg = shopWechatTplmsgService.getOne(queryWrapper);
- ShopStoreBase shopStoreBase= shopStoreBaseService.get(wechatTplmsg.getStore_id());
- Map args=new HashMap();
+ ShopStoreBase shopStoreBase = shopStoreBaseService.get(wechatTplmsg.getStore_id());
+ Map args = new HashMap();
String[] activeProfiles = environment.getActiveProfiles();
String activeProfile = activeProfiles[0];
args.put("storeName", shopStoreBase.getStore_name());
- args.put("evn",activeProfile);
- Map timeArgs=getActiveTime();
+ args.put("evn", activeProfile);
+ Map timeArgs = getActiveTime();
args.putAll(timeArgs);
- int total= (int) allBindCount;
- Integer pages= CommonUtil.getPagesCount(total,BATCH_SIZE);
+ int total = (int) allBindCount;
+ Integer pages = CommonUtil.getPagesCount(total, BATCH_SIZE);
ExecutorService executor = Executors.newFixedThreadPool(6);
List> futures = new ArrayList<>();
- for (int i=1;i<=pages;i++){
- List finalList=accountService.getAllBindPage(CommonConstant.BIND_SUB_TMPL_SKILL,i,BATCH_SIZE);
+ for (int i = 1; i <= pages; i++) {
+ List finalList = accountService.getAllBindPage(CommonConstant.BIND_SUB_TMPL_SKILL, i, BATCH_SIZE);
int finalI = i;
futures.add(executor.submit(() -> {
finalList.forEach(accountUserBindConnect -> {
- String open_id=accountUserBindConnect.getBind_openid();
- if(StrUtil.isNotEmpty(open_id)){
+ String open_id = accountUserBindConnect.getBind_openid();
+ if (StrUtil.isNotEmpty(open_id)) {
String wechatTplData = getXcxWechatTplData(open_id, wechatTplmsg, args);
String result = WxHttpUtil.request(WxHttpUtil.MethodType.POST, WxHttpUtil.WxType.XCX, accessToken, url, null, wechatTplData);
JSONObject resultJson = JSONUtil.parseObj(result);
@@ -775,7 +778,7 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl {
*/
CommonResult orderPickingCompleted(String storeId, String orderId);
+ /**
+ * 确认收货之后更改订单的收货状态
+ *
+ * @param orderId 订单ID
+ * @return Boolean 更新是否成功
+ */
+ Boolean updateOrderReceived(String orderId);
+
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java
index de12a6d4..7dad7451 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java
@@ -12,6 +12,7 @@ import cn.hutool.json.JSONObject;
import com.suisung.mall.common.modules.order.ShopOrderLkl;
import com.suisung.mall.core.web.service.IBaseService;
+import java.math.BigDecimal;
import java.util.List;
public interface ShopOrderLklService extends IBaseService {
@@ -113,4 +114,22 @@ public interface ShopOrderLklService extends IBaseService {
* @return
*/
Boolean safeUpdate(ShopOrderLkl record);
+
+ /**
+ * 获取平台内部订单配送费
+ *
+ * @param storeId 店铺Id
+ * @param orderId 订单编号
+ * @return
+ */
+ Integer getOrderShippingFeeInner(Integer storeId, String orderId);
+
+ /**
+ * 获取平台内部订单配送费
+ *
+ * @param storeId 店铺Id
+ * @param orderId 订单编号
+ * @return
+ */
+ BigDecimal getOrderShippingFeeInnerToDecimal(Integer storeId, String orderId);
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java
index c0f35bff..d31786eb 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java
@@ -56,6 +56,7 @@ import com.suisung.mall.common.modules.product.ShopProductItem;
import com.suisung.mall.common.modules.product.ShopProductValidPeriod;
import com.suisung.mall.common.modules.store.*;
import com.suisung.mall.common.modules.user.*;
+import com.suisung.mall.common.pojo.dto.LklSeparateDTO;
import com.suisung.mall.common.pojo.dto.StandardAddressDTO;
import com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO;
import com.suisung.mall.common.pojo.req.*;
@@ -2374,10 +2375,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(2);
tmplArgs.put("order_id", order_id);
tmplArgs.put("order_payment_amount", order_payment_amount);
- // 所有店铺管理员的发送邮件, 提醒商家:您有一笔新的订单 ${order_id},请及时处理。
- shopMessageTemplateService.aliyunSmsSend(shopKeeperMobiles, "SMS_476810378", tmplArgs);//SMS_475836097
+// 所有店铺管理员的发送邮件, 提醒商家:您有一笔新的订单 ${order_id},请及时处理。
+ shopMessageTemplateService.aliyunSmsSend(shopKeeperMobiles, "SMS_476810378", tmplArgs);
}
// 付款成功,对通知推广员进行提醒
@@ -4220,7 +4223,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>();
stockDeltaMap.put(shopProductItem.getItem_src_id()+"_"+order_item_row.getOrder_id(), order_item_quantity);
@@ -5025,6 +5028,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>();
stockDeltaMap.put(item_src_id+"_"+order_id, -cart_quantity);
syncThirdDataService.incrProductStockToRedis(stockDeltaMap);
@@ -7657,14 +7665,51 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl calcResult = lakalaApiService.calculateAndEvaluateSharingParams(
+ CommonConstant.SeparateCalcMode_CanSeparateAmt,
+ Convert.toInt(order_payment_amount.multiply(BigDecimal.valueOf(100))),
+ innerMinDeliverFee,
+ storeSplitRatio,
+ BigDecimal.valueOf(0.01),
+ null, null, null);
// 计算平台费
- BigDecimal platform_fee = calculatePlatformAndAgentShareAmount(Convert.toInt(base_row.get("store_id")), split_amount_from);
- data_row.setTotal_separate_value(platform_fee); // 从拉卡拉分账,给平台和代理商的总计分账金额
- data_row.setPlatform_fee(platform_fee);
+ if (calcResult != null && calcResult.getFirst() && calcResult.getSecond() != null) {
+ try {
+ LklSeparateDTO lklSeparateDTO = calcResult.getSecond();
+ // 确保分账金额不为负数
+ BigDecimal totalSeparateAmount = BigDecimal.valueOf(lklSeparateDTO.getCanSeparateAmount())
+ .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+ BigDecimal platformFee = BigDecimal.valueOf(lklSeparateDTO.getPlatAmount() + lklSeparateDTO.getAgent1stAmount() + lklSeparateDTO.getAgent2ndAmount())
+ .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+ // 防止负值
+ data_row.setTotal_separate_value(totalSeparateAmount.max(BigDecimal.ZERO));
+ data_row.setPlatform_fee(platformFee.max(BigDecimal.ZERO));
+ } catch (Exception e) {
+ log.warn("分账金额计算异常,使用默认值: {}", e.getMessage());
+ data_row.setTotal_separate_value(BigDecimal.ZERO);
+ data_row.setPlatform_fee(BigDecimal.ZERO);
+ }
+ } else {
+ log.warn("拉卡拉分账参数计算失败,使用默认值");
+ data_row.setTotal_separate_value(BigDecimal.ZERO);
+ data_row.setPlatform_fee(BigDecimal.ZERO);
+ }
Integer voucher_id = (Integer) order_voucher_row.get("voucher_id");
String voucher_code = (String) order_voucher_row.get("voucher_code");
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java
index 4fc94588..0a37dc87 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java
@@ -479,6 +479,41 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>();
+ updateWrapper.eq("order_id", orderId)
+ .ne("order_is_received", CommonConstant.Enable);
+ updateWrapper.set("order_is_received", CommonConstant.Enable)
+ .set("order_deal_time", System.currentTimeMillis());
+
+ boolean result = update(updateWrapper);
+ if (result) {
+ logger.info("[确认收货] 订单收货状态更新成功, orderId={}", orderId);
+ } else {
+ logger.warn("[确认收货] 订单收货状态更新失败, orderId={}", orderId);
+ }
+
+ return result;
+ } catch (Exception e) {
+ logger.error("[确认收货] 订单收货状态更新异常, orderId={}", orderId, e);
+ return false;
+ }
+ }
+
// todo 优化多次远程查询
private Map dashboardPlantform() {
List order_state = Arrays.asList(
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java
index 19002f43..d2d4fd49 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java
@@ -28,7 +28,6 @@ import com.suisung.mall.shop.order.service.ShopOrderItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -228,28 +227,24 @@ public class ShopOrderItemServiceImpl extends BaseServiceImpl selectOrderItemPrintInfo(String orderId) {
+ // 输入验证
if (StrUtil.isBlank(orderId)) {
- return null;
+ return Collections.emptyList();
}
List mList = shopOrderItemMapper.selectOrderItemPrintInfo(orderId);
if (CollUtil.isEmpty(mList)) {
- return null;
+ return Collections.emptyList();
}
- DecimalFormat df2 = new DecimalFormat("#.##");
- List retList = new ArrayList<>();
- for (Map map : mList) {
+ return mList.stream().map(map -> {
ShopStoreOrderProductPrintVO vo = new ShopStoreOrderProductPrintVO();
- vo.setProduct_sn((String) map.get("product_sn"));
- vo.setItem_name((String) map.get("item_name"));
+ vo.setProduct_sn(Convert.toStr(map.get("product_sn")));
+ vo.setItem_name(Convert.toStr(map.get("item_name")));
vo.setOrder_item_amount(Convert.toBigDecimal(map.get("order_item_amount")));
- vo.setOrder_item_quantity((Integer) map.get("order_item_quantity"));
-
- retList.add(vo);
- }
-
- return retList;
+ vo.setOrder_item_quantity(Convert.toInt(map.get("order_item_quantity")));
+ return vo;
+ }).collect(Collectors.toList());
}
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java
index 5dc261b8..14ebbf37 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java
@@ -33,8 +33,10 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
+import java.util.Optional;
@Slf4j
@Service
@@ -596,4 +598,64 @@ public class ShopOrderLklServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>();
+ queryWrapper.select("shopping_fee_inner");
+ queryWrapper.eq("store_id", storeId);
+ queryWrapper.eq("order_id", orderId);
+
+ ShopOrderLkl shopOrderLkl = findOne(queryWrapper);
+ Integer fee = Optional.ofNullable(shopOrderLkl)
+ .map(ShopOrderLkl::getShopping_fee_inner)
+ .orElse(0);
+
+ log.debug("[获取平台内部订单运费] 查询成功, storeId={}, orderId={}, fee={}", storeId, orderId, fee);
+ return fee;
+ } catch (Exception e) {
+ log.error("[获取平台内部订单运费] 系统异常, storeId={}, orderId={}", storeId, orderId, e);
+ return 0;
+ }
+ }
+
+ @Override
+ public BigDecimal getOrderShippingFeeInnerToDecimal(Integer storeId, String orderId) {
+ // 参数校验
+ if (storeId == null || StringUtils.isBlank(orderId)) {
+ log.warn("[获取平台内部订单运费(Decimal)] 参数校验失败:storeId或orderId为空, storeId={}, orderId={}", storeId, orderId);
+ return BigDecimal.ZERO;
+ }
+
+ try {
+ // 调用已有的方法获取运费(单位:分)
+ Integer feeInCents = getOrderShippingFeeInner(storeId, orderId);
+
+ // 转换为元为单位的BigDecimal
+ BigDecimal feeInYuan = new BigDecimal(feeInCents).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+
+ log.debug("[获取平台内部订单运费(Decimal)] 查询成功, storeId={}, orderId={}, feeInCents={}, feeInYuan={}",
+ storeId, orderId, feeInCents, feeInYuan);
+ return feeInYuan;
+ } catch (Exception e) {
+ log.error("[获取平台内部订单运费(Decimal)] 系统异常, storeId={}, orderId={}", storeId, orderId, e);
+ return BigDecimal.ZERO;
+ }
+ }
+
+
}
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 ec7b624f..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
@@ -58,10 +58,7 @@ import com.suisung.mall.shop.sfexpress.service.SFExpressApiService;
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
import com.suisung.mall.shop.store.service.ShopStoreConfigService;
import com.suisung.mall.shop.store.service.ShopStoreShippingAddressService;
-import io.seata.core.context.RootContext;
-import io.seata.core.exception.TransactionException;
import io.seata.spring.annotation.GlobalTransactional;
-import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -196,6 +193,10 @@ 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._("请选择需要审核的退单!"));
}
@@ -1419,6 +1419,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl orderItemQueryWrapper = new QueryWrapper<>();
orderItemQueryWrapper.eq("order_id", order_id);
@@ -1593,7 +1594,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl forbiddenStates = Arrays.asList(
StateCode.ORDER_STATE_SHIPPED,
StateCode.ORDER_STATE_RECEIVED,
@@ -1603,7 +1604,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl 0) {
// 退款金额+打包费
- BigDecimal order_refund_amount_add_fee = NumberUtil.add(
+ BigDecimal orderRefundAmountAddFee = NumberUtil.add(
order_data_row.getOrder_refund_amount(),
order_data_row.getPacking_fee());
// 最后一个退款订单如果有打包费,加上打包费
- return_row.setReturn_refund_amount(order_refund_amount_add_fee);
- logger.debug("已添加打包费到退款金额,订单ID: {}", order_id);
+ return_row.setReturn_refund_amount(orderRefundAmountAddFee);
+ logger.debug("最后一个商品,已添加打包费到退款金额,订单ID: {}", order_id);
}
- // 有运费的订单
+ // 有运费的,重新生成一个运费退单
if (order_data_row != null &&
- order_data_row.getOrder_shipping_fee() != null &&
- order_data_row.getOrder_shipping_fee().compareTo(BigDecimal.ZERO) > 0) {
+ order_data_row.getOrder_shipping_fee_inner() != null &&
+ order_data_row.getOrder_shipping_fee_inner().compareTo(BigDecimal.ZERO) > 0) {
// 运费大于0的, 执行退运费操作, 有两种方案,1、生成退运费售后服务单; 2、直接执行退款
// 1、生成独立退运费售后服务单,需注意运费是退给运费代理商的,需要获取代理商的交易单号
@@ -1630,8 +1630,15 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl orderItems = shopOrderItemService.find(new QueryWrapper().eq("order_id", orderId));
if (CollectionUtil.isEmpty(orderItems)) return CommonResult.failed("无可退货商品");
-
+
// === 3. 检查退货单 ===
ShopOrderReturn refundOrder = findOne(new QueryWrapper()
.eq("order_id", orderId)
@@ -2616,7 +2615,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl
* @param printerId 打印机Id
**/
CommonResult tryPrint(Long printerId);
+
+ /**
+ * 从飞鹅厂家删除飞鹅打印机
+ *
+ * @param snList 打印机编号,多台打印机请用减号“-”连接起来。如:316500010-316500011-316500012
+ * @return
+ */
+ CommonResult deleteFeiePrinter(String snList);
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java
index c2afdec8..c3d207de 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java
@@ -786,11 +786,11 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl queryWrapper2 = new QueryWrapper<>();
queryWrapper2.eq("store_id", record.getStore_id());
queryWrapper2.eq("printer_sn", record.getPrinter_sn());
- ShopStorePrinter existRecord2 = getOne(queryWrapper2);
+ ShopStorePrinter existRecord2 = findOne(queryWrapper2);
if (existRecord2 == null || existRecord2.getPrinter_id() <= 0) {
+
// 打印机从未加入到厂家到情况,往厂商添加打印机
// 格式:"922441475#r6ZXPvHH#核销柜台";
- Pair retPair = feieUtil.addPrinter(String.format("%s#%s#%s", record.getPrinter_sn(), record.getPrinter_key(), record.getPrinter_name()));
+ String printerInfo = String.format("%s#%s#%s", record.getPrinter_sn(), record.getPrinter_key(), record.getPrinter_name());
+ logger.info("向厂家添加新打印机: {}", printerInfo);
+ Pair retPair = feieUtil.addPrinter(printerInfo);
+
if (retPair.getFirst()) {
+ logger.info("向厂家添加打印机成功,SN: {}", record.getPrinter_sn());
updateWrapper.set("flag", CommonConstant.Enable);
updateWrapper.set("status", CommonConstant.Enable);
} else {
+ logger.warn("向厂家添加打印机失败,SN: {}, 错误信息: {}", record.getPrinter_sn(), retPair.getSecond());
msg = msg + ",但打印机绑定未成功,请检查打印机编号和密钥是否填写正确。";
updateWrapper.set("flag", CommonConstant.Disable2);
updateWrapper.set("status", CommonConstant.Disable2);
}
// 解绑之前的打印机
+ logger.info("解绑原打印机,SN: {}", existRecord.getPrinter_sn());
feieUtil.delPrinter(existRecord.getPrinter_sn());
} else {
// 打印机编号已被使用
+ logger.warn("打印机已添加,请勿重复操作,SN: {}", record.getPrinter_sn());
return CommonResult.success(null, "打票机已添加,请勿重复操作");
}
}
boolean success = update(updateWrapper);
if (success) {
+ logger.info("更新打印机信息成功,打印机ID: {}", record.getPrinter_id());
return CommonResult.success(null, msg);
}
+ logger.error("更新打印机信息失败,打印机ID: {}", record.getPrinter_id());
return CommonResult.failed("修改失败!");
-
}
@@ -232,7 +259,6 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl retPair = feieUtil.delPrinter(record.getPrinter_sn());
+ if (!retPair.getFirst()) {
+ return CommonResult.failed(retPair.getSecond());
}
}
@@ -386,7 +412,6 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl retPair = feieUtil.delPrinter(snList);
+ if (!retPair.getFirst()) {
+ logger.error("从厂家删除飞鹅打印机失败,打印机编号: {}", snList);
+ return CommonResult.failed(retPair.getSecond());
+ }
+ logger.info("从厂家删除飞鹅打印机成功,打印机编号: {}", snList);
+ return CommonResult.success("从厂家删除飞鹅打印机成功");
+ }
+
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreSameCityTransportBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreSameCityTransportBaseServiceImpl.java
index 4a0c602d..46ba9523 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreSameCityTransportBaseServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreSameCityTransportBaseServiceImpl.java
@@ -410,6 +410,7 @@ public class ShopStoreSameCityTransportBaseServiceImpl extends BaseServiceImpl saveOrUpdateResult = saveOrUpdateShopStoreSameCityTransportBase(transportBase);
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java
index d8ea19fc..3f0a5ae2 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java
@@ -120,18 +120,18 @@ public class FeieUtil {
* @param snList 打印机编号,多台打印机请用减号“-”连接起来。如:316500010-316500011-316500012
* @return
*/
- public boolean delPrinter(String snList) {
+ public Pair delPrinter(String snList) {
List nvps = new ArrayList();
nvps.add(new BasicNameValuePair("snlist", snList));
FeiePrinterApiRes reps = sendHttpPost("Open_printerDelList", nvps);
logger.info("飞鹅删除打印机返回数据:{}", reps);
if (reps != null && reps.getRet().equals(0)) {
- return true;
+ return Pair.of(true, "打印机删除成功!");
}
logger.error("飞鹅删除打印机操作失败:{}", reps.getMsg());
- return false;
+ return Pair.of(true, "打印机删除失败:" + reps.getMsg());
}
/**
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxOrderShippingController.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxOrderShippingController.java
index ecb77385..889de78f 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxOrderShippingController.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxOrderShippingController.java
@@ -8,10 +8,20 @@
package com.suisung.mall.shop.wechat.controller;
+import cn.hutool.json.JSONObject;
+import com.suisung.mall.common.api.CommonResult;
+import com.suisung.mall.common.exception.ApiException;
+import com.suisung.mall.shop.wechat.service.WxOrderShippingService;
import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.data.util.Pair;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
+import javax.annotation.Resource;
+
/**
* 小程序发货信息管理服务 前端控制器
*/
@@ -19,4 +29,17 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin/shop/order-shipping")
public class WxOrderShippingController {
+
+ @Resource
+ private WxOrderShippingService wxOrderShippingService;
+
+ @ApiOperation(value = "配置订单详情路径", notes = "配置订单详情路径")
+ @RequestMapping(value = "/updateOrderDetailPath", method = RequestMethod.POST)
+ public CommonResult updateOrderDetailPath(@RequestBody JSONObject params) {
+ Pair result = wxOrderShippingService.updateOrderDetailPath(params.getStr("path"));
+ if (!result.getFirst()) {
+ throw new ApiException(result.getSecond());
+ }
+ return CommonResult.success(result.getSecond());
+ }
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxOrderShippingService.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxOrderShippingService.java
index 76db7a99..b31dacc7 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxOrderShippingService.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxOrderShippingService.java
@@ -30,4 +30,26 @@ public interface WxOrderShippingService {
* @return 返回确认收货结果,包含成功状态和错误消息
*/
Pair notifyConfirmReceive(String orderId);
+
+
+ /**
+ * 配置订单详情路径
+ * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ */
+ /**
+ * 更新订单详情路径
+ * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ *
+ * @param path 包括参数名=${商品订单号}
+ * @return
+ */
+ Pair updateOrderDetailPath(String path);
+
+ /**
+ * 消息跳转路径设置接口
+ *
+ * @param orderId 订单号
+ * @return
+ */
+ Pair setMsgJumpPath(String orderId);
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxOrderShippingServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxOrderShippingServiceImpl.java
index 4c3e2369..36c23633 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxOrderShippingServiceImpl.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxOrderShippingServiceImpl.java
@@ -33,22 +33,24 @@ import org.springframework.web.client.HttpClientErrorException;
import java.util.Date;
+/**
+ * 对接微信订单管理相关接口
+ */
@Slf4j
@Service
public class WxOrderShippingServiceImpl implements WxOrderShippingService {
+ private final String ORDER_DETAIL_PATH = "member/order/detail";
+
@Lazy
@Autowired
private WxUtil wxUtil;
-
@Lazy
@Autowired
private ShopOrderBaseService shopOrderBaseService;
-
@Lazy
@Autowired
private ShopStoreSfOrderService shopStoreSfOrderService;
-
@Lazy
@Autowired
private ShopOrderLogisticsService shopOrderLogisticsService;
@@ -159,6 +161,9 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService {
return Pair.of(false, "发货信息录入失败: " + errorMsg);
}
+ // 跳转链接设置
+ setMsgJumpPath(orderId);
+
log.info("发货信息录入成功, 订单ID: {}", orderId);
return Pair.of(true, "发货信息录入成功");
@@ -230,6 +235,9 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService {
return Pair.of(false, "通知微信用户确认收货失败: " + errorMsg);
}
+ // 跳转链接设置
+ setMsgJumpPath(orderId);
+
log.info("通知微信用户确认收货成功, 订单ID: {}", orderId);
return Pair.of(true, "通知微信用户确认收货成功");
@@ -241,4 +249,138 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService {
return Pair.of(false, "通知微信确认收货失败: " + e.getMessage());
}
}
+
+ /**
+ * 配置订单详情路径
+ * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ *
+ * @param path 订单详情页面路径
+ * @return 配置结果,包含成功状态和错误消息
+ */
+ @Override
+ public Pair updateOrderDetailPath(String path) {
+ log.debug("开始配置订单详情路径,路径: {}", path);
+ if (StrUtil.isBlank(path)) {
+ path = String.format("%s?on=${商品订单号}", ORDER_DETAIL_PATH);
+ log.warn("订单详情路径为空,默认:{}", path);
+ }
+
+ try {
+ // 获取微信访问令牌
+ String accessToken = wxUtil.getAccessToken();
+ if (StrUtil.isBlank(accessToken)) {
+ log.error("获取AccessToken失败");
+ return Pair.of(false, "获取AccessToken失败");
+ }
+
+ if (!StrUtil.contains(path, "?")) {
+ path = String.format("%s?on=${商品订单号}", path);
+ }
+
+ // 构造请求参数
+ JSONObject paramsJSON = new JSONObject()
+ .set("path", path);
+
+ // 构造请求URL
+ String postUrl = "https://api.weixin.qq.com/wxa/sec/order/update_order_detail_path?access_token=" + accessToken;
+ log.debug("配置订单详情路径请求: {} \n {}", postUrl, paramsJSON);
+
+ // 发送请求到微信API
+ JSONObject respObj = RestTemplateHttpUtil.sendPost(
+ postUrl,
+ null, paramsJSON, JSONObject.class
+ );
+
+ // 处理响应结果
+ if (respObj == null) {
+ log.error("配置订单详情路径失败,返回结果为空");
+ return Pair.of(false, "配置订单详情路径失败,返回结果为空");
+ }
+
+ int errCode = respObj.getInt("errcode", -1);
+ if (errCode != 0) {
+ String errorMsg = respObj.getStr("errmsg", "未知错误");
+ log.error("配置订单详情路径失败,错误码: {}, 错误信息: {}", errCode, errorMsg);
+ return Pair.of(false, "配置订单详情路径失败: " + errorMsg);
+ }
+
+ log.info("配置订单详情路径成功,路径: {}", path);
+ return Pair.of(true, path);
+
+ } catch (HttpClientErrorException e) {
+ log.error("HTTP请求错误,状态码: {}, 错误信息: {}", e.getStatusCode(), e.getMessage(), e);
+ return Pair.of(false, "HTTP请求错误: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("配置订单详情路径失败,错误信息: {}", e.getMessage(), e);
+ return Pair.of(false, "配置订单详情路径失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 消息跳转路径设置接口
+ * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html
+ * 用于设置微信订单消息中的跳转路径,用户点击消息时可跳转到指定页面
+ *
+ * @param orderId 订单号
+ * @return 设置结果,包含成功状态和跳转路径
+ */
+ @Override
+ public Pair setMsgJumpPath(String orderId) {
+ log.debug("开始设置消息跳转路径,订单ID: {}", orderId);
+
+ if (StrUtil.isBlank(orderId)) {
+ log.warn("订单号不能为空");
+ return Pair.of(false, "订单号不能为空");
+ }
+
+ try {
+ // 获取微信访问令牌
+ String accessToken = wxUtil.getAccessToken();
+ if (StrUtil.isBlank(accessToken)) {
+ log.error("获取AccessToken失败,订单ID: {}", orderId);
+ return Pair.of(false, "获取AccessToken失败");
+ }
+
+ // 构造跳转路径
+ String path = String.format("%s?on=%s", ORDER_DETAIL_PATH, orderId);
+ log.debug("构造消息跳转路径: {}", path);
+
+ // 构造请求参数
+ JSONObject paramsJSON = new JSONObject()
+ .set("path", path);
+
+ // 构造请求URL
+ String postUrl = "https://api.weixin.qq.com/wxa/sec/order/set_msg_jump_path?access_token=" + accessToken;
+ log.debug("消息跳转路径设置请求: {} \n {}", postUrl, paramsJSON);
+
+ // 发送请求到微信API
+ JSONObject respObj = RestTemplateHttpUtil.sendPost(
+ postUrl,
+ null, paramsJSON, JSONObject.class
+ );
+
+ // 处理响应结果
+ if (respObj == null) {
+ log.error("消息跳转路径设置失败,订单ID: {}, 返回结果为空", orderId);
+ return Pair.of(false, "消息跳转路径设置失败,返回结果为空");
+ }
+
+ int errCode = respObj.getInt("errcode", -1);
+ if (errCode != 0) {
+ String errorMsg = respObj.getStr("errmsg", "未知错误");
+ log.error("消息跳转路径设置失败,订单ID: {}, 错误码: {}, 错误信息: {}", orderId, errCode, errorMsg);
+ return Pair.of(false, "消息跳转路径设置失败: " + errorMsg);
+ }
+
+ log.info("消息跳转路径设置成功,订单ID: {}, 路径: {}", orderId, path);
+ return Pair.of(true, path);
+
+ } catch (HttpClientErrorException e) {
+ log.error("HTTP请求错误,订单ID: {}, 状态码: {}, 错误信息: {}", orderId, e.getStatusCode(), e.getMessage(), e);
+ return Pair.of(false, "HTTP请求错误: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("消息跳转路径设置失败,订单ID: {}, 错误信息: {}", orderId, e.getMessage(), e);
+ return Pair.of(false, "消息跳转路径设置失败: " + e.getMessage());
+ }
+ }
}
diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/utils/WxUtil.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/utils/WxUtil.java
index 1d6f6fc2..64b51146 100644
--- a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/utils/WxUtil.java
+++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/utils/WxUtil.java
@@ -58,18 +58,24 @@ public class WxUtil {
params.put("secret", xcx_app_secret);
params.put("grant_type", "client_credential");
- JSONObject resultObj = new JSONObject();
String response = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", params);
JSONObject jsonObject = JSONUtil.parseObj(response);
+ // 修正:使用 jsonObject 而不是 resultObj 来检查错误信息
Object errmsg = jsonObject.get("errmsg");
if (ObjectUtil.isNotEmpty(errmsg)) {
- String errmsgStr = Convert.toStr(resultObj.get("errmsg"));
- if (!errmsgStr.equals("0")) {
+ String errmsgStr = Convert.toStr(errmsg);
+ if (!"0".equals(errmsgStr)) {
throw new ApiException(I18nUtil._("获取access_token失败"));
}
}
- Integer expiresIn = Convert.toInt(jsonObject.get("expires_in"), 0);
+
+ // 优化:expiresIn 逻辑更清晰
+ Long expiresIn = Convert.toLong(jsonObject.get("expires_in"), 7200L) - 60L;
+ if (expiresIn <= 0L) {
+ expiresIn = 7140L;
+ }
redisService.set(StateCode.WX_XCX_ACCESSTOKEN, Convert.toStr(jsonObject.get("access_token")), expiresIn);
}
+
}
diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml
index 7470b438..1b38418c 100644
--- a/mall-shop/src/main/resources/bootstrap-prod.yml
+++ b/mall-shop/src/main/resources/bootstrap-prod.yml
@@ -191,7 +191,7 @@ sf-express:
# dev_id: 1715091463
# appid: 1715091463
# appkey: 47466ae69c530f831395e1bc405639fb
- enable: 2
+ enable: 1
#拉卡拉进件配置
lakala:
#服务地址
diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml
index 7b8b1793..d491d650 100644
--- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml
+++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml
@@ -575,6 +575,7 @@
+
@@ -780,11 +781,12 @@
AS is_new_buyer,
oi.payment_time,
od.order_shipping_fee,
+ IFNULL(od.order_shipping_fee_inner, 0) as order_shipping_fee_inner,
(od.order_discount_amount + od.voucher_price + od.order_points_fee + od.order_adjust_fee) as
total_discount_amount,
- (ob.order_product_amount-od.order_discount_amount-od.voucher_price-od.order_points_fee-od.order_adjust_fee-od.platform_fee-od.order_shipping_fee+od.packing_fee)
+ (ob.order_product_amount-od.order_discount_amount-od.voucher_price-od.order_points_fee-od.order_adjust_fee-od.platform_fee-order_shipping_fee_inner+od.packing_fee)
as order_income_amount,
od.platform_fee,
od.packing_fee,
diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderDataMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderDataMapper.xml
index e46db78d..4a522494 100644
--- a/mall-shop/src/main/resources/mapper/order/ShopOrderDataMapper.xml
+++ b/mall-shop/src/main/resources/mapper/order/ShopOrderDataMapper.xml
@@ -7,7 +7,7 @@
, order_desc, order_delay_time, delivery_type_id, delivery_time_id, delivery_time, delivery_time_rang,
delivery_time_h, delivery_time_i, delivery_istimer, invoice_type_id, invoice_company_code, order_invoice_title,
order_message, order_item_amount, order_discount_amount, order_adjust_fee, order_points_fee,
- order_shipping_fee_amount, order_shipping_fee, platform_fee, packing_fee, voucher_id, voucher_number, voucher_price, redpacket_id,
+ order_shipping_fee_amount, order_shipping_fee,order_shipping_fee_inner, platform_fee, packing_fee, voucher_id, voucher_number, voucher_price, redpacket_id,
redpacket_number, redpacket_price, order_redpacket_price, order_resource_ext1, order_resource_ext2,
order_resource_ext3, trade_payment_money, trade_payment_recharge_card, trade_payment_credit,
order_refund_status, order_refund_amount, order_refund_agree_amount, order_return_status, order_return_num,
diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderItemMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderItemMapper.xml
index a1e4fd2c..4af1d4f7 100644
--- a/mall-shop/src/main/resources/mapper/order/ShopOrderItemMapper.xml
+++ b/mall-shop/src/main/resources/mapper/order/ShopOrderItemMapper.xml
@@ -2,7 +2,8 @@
- order_item_id
+
+ order_item_id
, order_id, buyer_id, store_id, product_id, item_id,
item_name, category_id, spec_id, spec_info, item_unit_price, item_unit_points, item_unit_sp,
order_item_unit_price,
@@ -38,24 +39,47 @@
INNER JOIN
shop_product_index ON shop_order_item.product_id = shop_product_index.product_id
- shop_order_info.store_id=#{map.store_id}
- and shop_order_item.item_name like concat('%', #{map.item_name}, '%')
- and shop_product_index.product_name like concat('%', #{map.product_name},
+
+ shop_order_info.store_id=#{map.store_id}
+
+
+ and shop_order_item.item_name like concat('%', #{map.item_name}, '%')
+
+
+ and shop_product_index.product_name like concat('%', #{map.product_name},
'%')
- and shop_order_item.category_id=#{map.category_id}
- and shop_order_item.product_id=#{map.product_id}
- and shop_order_info.order_time >=#{map.order_st_time}
- and shop_order_info.order_time <=#{map.order_ed_time}
- and shop_order_delivery_address.da_province_id =#{map.da_province_id}
+
+ and shop_order_item.category_id=#{map.category_id}
- and shop_order_delivery_address.da_city_id =#{map.da_city_id}
- and shop_order_delivery_address.da_county_id =#{map.da_county_id}
- and shop_order_delivery_address.da_address like concat('%',
+
+ and shop_order_item.product_id=#{map.product_id}
+
+
+ and shop_order_info.order_time >=#{map.order_st_time}
+
+
+ and shop_order_info.order_time <=#{map.order_ed_time}
+
+
+ and shop_order_delivery_address.da_province_id =#{map.da_province_id}
+
+
+ and shop_order_delivery_address.da_city_id =#{map.da_city_id}
+
+
+ and shop_order_delivery_address.da_county_id =#{map.da_county_id}
+
+
+ and shop_order_delivery_address.da_address like concat('%',
#{map.da_address}, '%')
- and shop_order_info.order_is_paid =#{map.order_is_paid}
- and shop_order_info.subsite_id =#{map.subsite_id}
+
+ and shop_order_info.order_is_paid =#{map.order_is_paid}
+
+
+ and shop_order_info.subsite_id =#{map.subsite_id}
+
GROUP BY
shop_order_item.item_id
@@ -68,29 +92,72 @@
left join shop_order_delivery_address a ON a.order_id = o.order_id
left join shop_product_base b ON m.product_id = b.product_id
- m.store_id=#{params.store_id}
- and m.order_id=#{params.order_id}
- and m.item_id=#{params.item_id}
- and o.order_is_paid=#{params.order_is_paid}
- and o.order_time >=#{params.order_st_time}
- and o.order_time <=#{params.order_ed_time}
- and o.order_time >=#{params.query_order_st_time}
- and o.order_time <=#{params.query_order_ed_time}
- and m.category_id=#{params.category_id}
- and b.product_name like concat('%', #{params.product_name}, '%')
- and m.product_id=#{params.product_id}
- and a.da_province_id =#{params.da_province_id}
- and a.da_city_id =#{params.da_city_id}
- and a.da_county_id =#{params.da_county_id}
- and a.da_address like concat('%', #{params.da_address}, '%')
+
+ m.store_id=#{params.store_id}
+
+
+ and m.order_id=#{params.order_id}
+
+
+ and m.item_id=#{params.item_id}
+
+
+ and o.order_is_paid=#{params.order_is_paid}
+
+
+ and o.order_time >=#{params.order_st_time}
+
+
+ and o.order_time <=#{params.order_ed_time}
+
+
+ and o.order_time >=#{params.query_order_st_time}
+
+
+ and o.order_time <=#{params.query_order_ed_time}
+
+
+ and m.category_id=#{params.category_id}
+
+
+ and b.product_name like concat('%', #{params.product_name}, '%')
+
+
+ and m.product_id=#{params.product_id}
+
+
+ and a.da_province_id =#{params.da_province_id}
+
+
+ and a.da_city_id =#{params.da_city_id}
+
+
+ and a.da_county_id =#{params.da_county_id}
+
+
+ and a.da_address like concat('%', #{params.da_address}, '%')
+