From 9f62bf8ddd5d67785fd42ba4247b2929b81d5623 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 27 Sep 2025 12:16:46 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=E6=8B=89=E5=8D=A1=E6=8B=89=E5=88=86?= =?UTF-8?q?=E8=B4=A6=E5=BC=82=E6=AD=A5=E9=80=9A=E7=9F=A5=E5=A4=84=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=8F=90=E7=8E=B0=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../quartz/job/UpdateOrderSeparateJob.java | 2 +- .../admin/LakalaAdminController.java | 2 +- .../shop/lakala/service/LakalaApiService.java | 5 +- .../service/impl/LakalaApiServiceImpl.java | 59 +++++++++++-------- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderSeparateJob.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderSeparateJob.java index ccaf0825..8fea6e7c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderSeparateJob.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderSeparateJob.java @@ -27,7 +27,7 @@ public class UpdateOrderSeparateJob extends QuartzJobBean { return; } - Integer processedCount = lakalaApiService.fixedUnSuccessSeparateStatusJob(); + Integer processedCount = lakalaApiService.fixUnSuccessSeparateStatusJob(); logger.info("[分账状态修复定时任务] 执行完成,共处理 {} 条记录", processedCount); } catch (Exception e) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java index 1eb0afe7..f1da4113 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java @@ -49,7 +49,7 @@ public class LakalaAdminController extends BaseControllerImpl { } // 执行业务逻辑 - Boolean success = lakalaPayService.ewalletWithDrawD1(paramsJSON.getStr("mercId"), paramsJSON.getStr("merOrderNo"), paramsJSON.getStr("drawAmt"), paramsJSON.getStr("remark"), paramsJSON.getStr("summary")); + Boolean success = lakalaPayService.ewalletWithDrawD1(paramsJSON.getStr("mercId"), paramsJSON.getStr("merOrderNo"), paramsJSON.getStr("drawAmt"), paramsJSON.getStr("summary")); if (success) { return CommonResult.success("账户D1提现提交成功"); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java index 49366084..6f12006b 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java @@ -299,7 +299,7 @@ public interface LakalaApiService { * * @return */ - Integer fixedUnSuccessSeparateStatusJob(); + Integer fixUnSuccessSeparateStatusJob(); /** * 检测修复补全商户的商户分账业务信息及分账接收方绑定关系(分账业务申请异步通知的补偿机制) @@ -337,10 +337,9 @@ public interface LakalaApiService { * @param mercId 822商户号或receiveNo * @param merOrderNo 商户订单号 * @param drawAmt 提现金额(分) - * @param remark * @param summary */ - Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String remark, String summary); + Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String summary); /** * 拉卡拉账户D1提现结果通知 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 70dea245..ab640bfc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -2693,13 +2693,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { * 参考文档:https://o.lakala.com/#/home/document/detail?id=393 *

* - * @param request + * @param request HTTP请求对象,包含拉卡拉分账结果通知的参数 * @param merchantNoParam 分账方商户号,非空表示是事后补偿 * @param separateNoParam 分账指令流水号,非空表示是事后补偿 - * + * @return 处理结果JSON对象 + * */ @Override public JSONObject sacsSeparateNotify(HttpServletRequest request, String merchantNoParam, String separateNoParam) { @@ -2708,7 +2709,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 +2744,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 +2763,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 +2781,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 +2821,25 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 更改分账状态:分账成功 shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, ""); + // RMK 本次分账成功后,给商户和接收方 D1提现到银行卡 + if (CollUtil.isNotEmpty(detailDatas)) { + for (JSONObject detailData : detailDatas.jsonIter()) { + String recvNo = detailData.getStr("recv_no"); // 822商户号或接收方号 + String amt = detailData.getStr("amt"); // 单位(分) + if (StrUtil.isNotBlank(recvNo) && StrUtil.isNotBlank(amt)) { + ewalletWithDrawD1(recvNo, outSeparateNo, amt, JSONUtil.toJsonStr(detailDatas)); + } + } + } + // 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 +2933,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 +3397,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 +3411,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 +3425,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); From f16efdd8b9fe595d911f411b6b0b08b3b085fe36 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 27 Sep 2025 12:27:31 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E6=9C=89=E6=96=B0=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C=E4=B8=8D=E7=BB=99=E5=95=86?= =?UTF-8?q?=E5=AE=B6=E5=8F=91=E7=9F=AD=E4=BF=A1=E4=BA=86=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E6=89=93=E6=89=B0=E5=95=86=E5=AE=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lakala/service/impl/LakalaApiServiceImpl.java | 8 +++++--- .../order/service/impl/ShopOrderBaseServiceImpl.java | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index ab640bfc..78ddc55d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -2824,10 +2824,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { // RMK 本次分账成功后,给商户和接收方 D1提现到银行卡 if (CollUtil.isNotEmpty(detailDatas)) { for (JSONObject detailData : detailDatas.jsonIter()) { - String recvNo = detailData.getStr("recv_no"); // 822商户号或接收方号 - String amt = detailData.getStr("amt"); // 单位(分) + String recvNo = detailData.getStr("recv_no"); + String amt = detailData.getStr("amt"); if (StrUtil.isNotBlank(recvNo) && StrUtil.isNotBlank(amt)) { - ewalletWithDrawD1(recvNo, outSeparateNo, amt, JSONUtil.toJsonStr(detailDatas)); + Boolean drawSuccess = ewalletWithDrawD1(recvNo, outSeparateNo, amt, JSONUtil.toJsonStr(detailDatas)); + log.info("[拉卡拉分账通知] 账户D1提现{},商户号={},分账单号={},金额={}分", + Boolean.TRUE.equals(drawSuccess) ? "成功" : "失败", recvNo, outSeparateNo, amt); } } } 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 6812c641..fc71150d 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 @@ -3236,11 +3236,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(2); - tmplArgs.put("order_id", order_id); - tmplArgs.put("order_payment_amount", order_payment_amount); + // Map 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 + // shopMessageTemplateService.aliyunSmsSend(shopKeeperMobiles, "SMS_476810378", tmplArgs); } // 付款成功,对通知推广员进行提醒 @@ -4220,7 +4220,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); stockDeltaMap.put(Convert.toStr(shopProductItem.getItem_src_id()), order_item_quantity); @@ -7234,7 +7234,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); stockDeltaMap.put(Convert.toStr(item_src_id), -cart_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); From 1d7234300442f926e03e32d5fc8b31988026f9c9 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 27 Sep 2025 15:28:55 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=AC=A1=E6=97=A5?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/product/ShopProductItem.java | 4 ++ .../quartz/job/ProductItemAutoFillJob.java | 53 +++++++++++++++++++ .../impl/ShopProductBaseServiceImpl.java | 13 +++++ sql/shop/dev/20250927_ddl.sql | 3 ++ 4 files changed, 73 insertions(+) create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/ProductItemAutoFillJob.java create mode 100644 sql/shop/dev/20250927_ddl.sql diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductItem.java b/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductItem.java index bb6508b8..d0105d68 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductItem.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductItem.java @@ -183,5 +183,9 @@ public class ShopProductItem implements Serializable { @TableField(exist=false) private String mergedUnitPrices; + @ApiModelProperty(value = "次日自动补满至") + private Integer automatic; + @ApiModelProperty(value = "是否开启次日补全1开启,0不开启") + private String is_open_automatic; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/ProductItemAutoFillJob.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/ProductItemAutoFillJob.java new file mode 100644 index 00000000..404f7a3f --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/ProductItemAutoFillJob.java @@ -0,0 +1,53 @@ +package com.suisung.mall.shop.components.quartz.job; + + +import cn.hutool.core.util.PageUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import com.suisung.mall.common.modules.product.ShopProductItem; +import com.suisung.mall.shop.config.SpringUtil; +import com.suisung.mall.shop.product.service.ShopProductItemService; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 任务:用于次日更新商品的库存 + */ +public class ProductItemAutoFillJob extends QuartzJobBean { + @Override + protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { + Logger logger = LoggerFactory.getLogger(ProductItemAutoFillJob.class); + logger.info("次日更新商品库存开始"); + ShopProductItemService shopProductItemService= SpringUtil.getBean(ShopProductItemService.class); + long startTime = System.nanoTime(); + // 更新商品库存 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("is_open_automatic","1"); + queryWrapper.apply("item_quantity queryWrapper2 = new QueryWrapper<>(); + queryWrapper2.eq("is_open_automatic","1"); + queryWrapper2.apply("item_quantity pageShopProductItem=shopProductItemService.lists(queryWrapper2,i, batchSize); + List shopProductItems= pageShopProductItem.getRecords(); + shopProductItems= shopProductItems.stream() + .peek(shopProductItem -> shopProductItem.setItem_quantity(shopProductItem.getAutomatic())) + .collect(Collectors.toList()); + shopProductItemService.updateBatchById(shopProductItems); + } + + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新商品库存结束,总共更新商品数量:{},耗时{}",total,duration / 1000000000); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java index 72572971..1eeb211a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java @@ -25,6 +25,7 @@ import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.constant.ConfigConstant; import com.suisung.mall.common.constant.MqConstant; import com.suisung.mall.common.domain.UserDto; +import com.suisung.mall.common.enums.DicEnum; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.feignService.EduService; import com.suisung.mall.common.feignService.SearchService; @@ -480,6 +481,18 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl item_image_other = Convert.toList(String.class, productItemMap.get("color_img")); diff --git a/sql/shop/dev/20250927_ddl.sql b/sql/shop/dev/20250927_ddl.sql new file mode 100644 index 00000000..ff68d04b --- /dev/null +++ b/sql/shop/dev/20250927_ddl.sql @@ -0,0 +1,3 @@ +alter table shop_product_item add automatic INTEGER DEFAULT NULL COMMENT '次日自动补满至'; +alter table shop_product_item add is_open_automatic varchar(1) not null DEFAULT '0' COMMENT '是否开启次日补全1开启,0不开启'; +alter table shop_product_item add index `index_is_open_automatic` (`is_open_automatic`) USING BTREE; \ No newline at end of file From 43f88175cda1f4139ef636e9101b7aae00ce8f95 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 27 Sep 2025 17:16:34 +0800 Subject: [PATCH 04/22] =?UTF-8?q?shop=5Fbase=E9=98=B2=E6=AD=A2=E7=A9=BA?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/suisung/mall/common/modules/store/ShopStoreBase.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java index 17a6c8cf..8b1b0ef9 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java @@ -1,8 +1,6 @@ package com.suisung.mall.common.modules.store; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -58,6 +56,7 @@ public class ShopStoreBase implements Serializable { private String store_area; @ApiModelProperty(value = "所属地区(DOT)") + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String store_district_id; @ApiModelProperty(value = "店铺详细地址") From 77fdb2727557a6ed304c41617fa956d788f9bd3f Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 27 Sep 2025 18:16:05 +0800 Subject: [PATCH 05/22] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E4=B8=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E5=BA=97=E9=93=BA=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/service/impl/ShopBaseStoreCategoryServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java index cce836ee..2ac3ad33 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java @@ -65,8 +65,8 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl Date: Sat, 27 Sep 2025 19:18:41 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=E5=BA=97=E9=93=BA=E6=8C=89=E5=AD=90?= =?UTF-8?q?=E7=B1=BB=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/service/impl/ShopStoreBaseServiceImpl.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 6250405a..53d8eb7a 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 @@ -790,9 +790,15 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl 1) { - queryWrapper.in("store_category_id", store_category_ids); + queryWrapper.and(q->{ + q.in("store_category_id",store_category_ids); + q.or().in("store_2nd_category_id",store_category_ids); + }); } else { - queryWrapper.eq("store_category_id", store_category_id); + queryWrapper.or(q->{ + q.eq("store_category_id",store_category_ids); + q.or().eq("store_2nd_category_id",store_category_ids); + }); } params.put("store_category_id", store_category_id); } From 22cfe81958b314c9dcdd8cb089840b2352a637ee Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 27 Sep 2025 19:35:22 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E5=BA=97=E9=93=BA=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=90=8C=E6=AD=A5=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E6=89=8D=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ShopStoreBaseServiceImpl.java | 23 ++++-- .../controller/WxOrderShippingController.java | 23 ++++++ .../service/WxOrderShippingService.java | 7 ++ .../impl/WxOrderShippingServiceImpl.java | 70 ++++++++++++++++++- 4 files changed, 113 insertions(+), 10 deletions(-) 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 6250405a..08b8836f 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 @@ -3199,18 +3199,27 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl result = wxOrderShippingService.updateOrderDetailPath(params.getStr("path")); + if (!result.getFirst()) { + throw new ApiException(result.getSecond()); + } + return CommonResult.success(); + } } 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..8f996fe5 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,11 @@ public interface WxOrderShippingService { * @return 返回确认收货结果,包含成功状态和错误消息 */ Pair notifyConfirmReceive(String orderId); + + + /** + * 配置订单详情路径 + * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html + */ + Pair updateOrderDetailPath(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..72fe8e24 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,23 @@ import org.springframework.web.client.HttpClientErrorException; import java.util.Date; +/** + * 对接微信订单管理相关接口 + */ @Slf4j @Service public class WxOrderShippingServiceImpl implements WxOrderShippingService { + private final String ORDER_DETAIL_PATH = "https://api.weixin.qq.com/wxa/business/shipping"; @Lazy @Autowired private WxUtil wxUtil; - @Lazy @Autowired private ShopOrderBaseService shopOrderBaseService; - @Lazy @Autowired private ShopStoreSfOrderService shopStoreSfOrderService; - @Lazy @Autowired private ShopOrderLogisticsService shopOrderLogisticsService; @@ -241,4 +242,67 @@ 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)) { + log.warn("订单详情路径为空,默认:{}", ORDER_DETAIL_PATH); +// return Pair.of(false, "详情路径 path 不能为空"); + path = ORDER_DETAIL_PATH; + } + + try { + // 获取微信访问令牌 + String accessToken = wxUtil.getAccessToken(); + if (StrUtil.isBlank(accessToken)) { + log.error("获取AccessToken失败"); + return Pair.of(false, "获取AccessToken失败"); + } + + // 构造请求参数 + JSONObject paramsJSON = new JSONObject() + .set("path", String.format("%s?on=${商品订单号}", 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, "配置订单详情路径成功"); + + } 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()); + } + } } From 62b5d71b3be4c704d1c2f0f27f4944542603faf8 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 27 Sep 2025 21:09:53 +0800 Subject: [PATCH 08/22] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E6=B6=88=E6=81=AF=20?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=20=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/WxOrderShippingController.java | 2 +- .../service/WxOrderShippingService.java | 17 +++- .../impl/WxOrderShippingServiceImpl.java | 91 +++++++++++++++++-- 3 files changed, 102 insertions(+), 8 deletions(-) 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 a5e897c0..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 @@ -40,6 +40,6 @@ public class WxOrderShippingController { if (!result.getFirst()) { throw new ApiException(result.getSecond()); } - return CommonResult.success(); + 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 8f996fe5..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 @@ -36,5 +36,20 @@ public interface WxOrderShippingService { * 配置订单详情路径 * 参考:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html */ - Pair updateOrderDetailPath(String orderId); + /** + * 更新订单详情路径 + * 参考: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 72fe8e24..dbecf5e4 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 @@ -40,7 +40,8 @@ import java.util.Date; @Service public class WxOrderShippingServiceImpl implements WxOrderShippingService { - private final String ORDER_DETAIL_PATH = "https://api.weixin.qq.com/wxa/business/shipping"; + private final String ORDER_DETAIL_PATH = "member/order/detail"; + @Lazy @Autowired private WxUtil wxUtil; @@ -160,6 +161,9 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { return Pair.of(false, "发货信息录入失败: " + errorMsg); } + // 跳转链接设置 + setMsgJumpPath(orderId); + log.info("发货信息录入成功, 订单ID: {}", orderId); return Pair.of(true, "发货信息录入成功"); @@ -231,6 +235,10 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { return Pair.of(false, "通知微信用户确认收货失败: " + errorMsg); } + // 跳转链接设置 + setMsgJumpPath(orderId); + + log.info("通知微信用户确认收货成功, 订单ID: {}", orderId); return Pair.of(true, "通知微信用户确认收货成功"); @@ -254,9 +262,8 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { public Pair updateOrderDetailPath(String path) { log.debug("开始配置订单详情路径,路径: {}", path); if (StrUtil.isBlank(path)) { - log.warn("订单详情路径为空,默认:{}", ORDER_DETAIL_PATH); -// return Pair.of(false, "详情路径 path 不能为空"); - path = ORDER_DETAIL_PATH; + path = String.format("%s?on=${商品订单号}", ORDER_DETAIL_PATH); + log.warn("订单详情路径为空,默认:{}", path); } try { @@ -267,9 +274,13 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { return Pair.of(false, "获取AccessToken失败"); } + if (!StrUtil.contains(path, "?")) { + path = String.format("%s?on=${商品订单号}", path); + } + // 构造请求参数 JSONObject paramsJSON = new JSONObject() - .set("path", String.format("%s?on=${商品订单号}", path)); + .set("path", path); // 构造请求URL String postUrl = "https://api.weixin.qq.com/wxa/sec/order/update_order_detail_path?access_token=" + accessToken; @@ -295,7 +306,7 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { } log.info("配置订单详情路径成功,路径: {}", path); - return Pair.of(true, "配置订单详情路径成功"); + return Pair.of(true, path); } catch (HttpClientErrorException e) { log.error("HTTP请求错误,状态码: {}, 错误信息: {}", e.getStatusCode(), e.getMessage(), e); @@ -305,4 +316,72 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { 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()); + } + } } From 129ba5b394f39822b771a3695820413d42a46d52 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Mon, 29 Sep 2025 10:15:39 +0800 Subject: [PATCH 09/22] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=BE=AE=E4=BF=A1=20ac?= =?UTF-8?q?cessToken=20redis=20=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=8F=90=E5=89=8D1=E5=88=86=E9=92=9F=E8=BF=87=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/suisung/mall/shop/wechat/utils/WxUtil.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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); } + } From 8687a295c0608f876163be0e804fdb8fc94b34d7 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Mon, 29 Sep 2025 10:51:54 +0800 Subject: [PATCH 10/22] =?UTF-8?q?=E5=BA=97=E9=93=BA=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8C=89=E5=AD=90=E7=B1=BB=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ShopBaseStoreCategoryServiceImpl.java | 3 +- .../impl/ShopStoreBaseServiceImpl.java | 59 ++++++++----------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java index 2ac3ad33..d1591c8f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseStoreCategoryServiceImpl.java @@ -89,7 +89,8 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl row = new HashMap<>(); - row.put("store_category_id", String.valueOf(store_category_id)); + row.put("store_category_id", String.valueOf(store_category_parent_id)); + row.put("store_2nd_category_id", String.valueOf(store_category_id)); row.put("findStore", true); row.put("store_type", "1"); Map storeListMap = shopStoreBaseService.getStoreList(1, pageSize, row); 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 2d696425..97a28e00 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 @@ -736,12 +736,18 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl store_category_ids = new ArrayList<>(); - QueryWrapper storeCategoryQueryWrapper = new QueryWrapper<>(); - storeCategoryQueryWrapper.eq("store_category_parent_id", store_category_id); - storeCategoryQueryWrapper.select("store_category_id"); - List shopBaseStoreCategories = shopBaseStoreCategoryService.list(storeCategoryQueryWrapper); - store_category_ids.add(store_category_id); - for (ShopBaseStoreCategory storeCategory : shopBaseStoreCategories) { - store_category_ids.add(storeCategory.getStore_category_id()); - } - if (store_category_ids.size() > 1) { - queryWrapper.and(q->{ - q.in("store_category_id",store_category_ids); - q.or().in("store_2nd_category_id",store_category_ids); - }); - } else { - queryWrapper.or(q->{ - q.eq("store_category_id",store_category_ids); - q.or().eq("store_2nd_category_id",store_category_ids); - }); - } + queryWrapper.eq("store_category_id",store_category_id); params.put("store_category_id", store_category_id); } + if(CheckUtil.isNotEmpty(store_2nd_category_id)){ + queryWrapper.eq("store_2nd_category_id",store_2nd_category_id); + } + queryWrapper.eq("store_is_open", 1); params.put("store_is_open", 1); @@ -3100,20 +3091,20 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl storeCategoryIds = new ArrayList<>(); - QueryWrapper storeCategoryQueryWrapper = new QueryWrapper<>(); - storeCategoryQueryWrapper.eq("store_category_parent_id", storeCategoryId); - List shopBaseStoreCategories = shopBaseStoreCategoryService.list(storeCategoryQueryWrapper); - if (!shopBaseStoreCategories.isEmpty()) { - storeCategoryIds.add(storeCategoryId); - shopBaseStoreCategories.forEach(shopBaseStoreCategory -> { - storeCategoryIds.add(shopBaseStoreCategory.getStore_category_id()); - }); - params.put("storeCategoryIds", storeCategoryIds); - } else { - params.put("storeCategoryId", storeCategoryId); - } - +// List storeCategoryIds = new ArrayList<>(); +// QueryWrapper storeCategoryQueryWrapper = new QueryWrapper<>(); +// storeCategoryQueryWrapper.eq("store_category_parent_id", storeCategoryId); +// List shopBaseStoreCategories = shopBaseStoreCategoryService.list(storeCategoryQueryWrapper); +// if (!shopBaseStoreCategories.isEmpty()) { +// storeCategoryIds.add(storeCategoryId); +// shopBaseStoreCategories.forEach(shopBaseStoreCategory -> { +// storeCategoryIds.add(shopBaseStoreCategory.getStore_category_id()); +// }); +// params.put("storeCategoryIds", storeCategoryIds); +// } else { +// params.put("storeCategoryId", storeCategoryId); +// } + params.put("storeCategoryId", storeCategoryId); params.put("subSiteId", subSiteId); params.put("storeName", storeName); From 8eb1e4f59f9773ffee8edebfbc27e678dc37956a Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Mon, 29 Sep 2025 14:54:24 +0800 Subject: [PATCH 11/22] =?UTF-8?q?=E5=BA=97=E9=93=BA=E6=A0=91=E5=BD=A2?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=98=BE=E7=A4=BA=E6=9D=A1=E4=BB=B6=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/ShopBaseStoreCategoryAdminController.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseStoreCategoryAdminController.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseStoreCategoryAdminController.java index a1e03660..ffb48e8e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseStoreCategoryAdminController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseStoreCategoryAdminController.java @@ -72,8 +72,12 @@ public class ShopBaseStoreCategoryAdminController { @RequestMapping(value = "/categoryTree", method = RequestMethod.GET) public CommonResult categoryTree(ShopBaseStoreCategory category) { QueryWrapper queryWrapper = new QueryWrapper<>(); - if (category.getStore_category_parent_id() != null) + if (category.getStore_category_parent_id() != null){ queryWrapper.eq("store_category_parent_id", category.getStore_category_parent_id()); + } + if(category.getCategory_is_enable() != null){ + queryWrapper.eq("category_is_enable", category.getCategory_is_enable()); + } return CommonResult.success(shopBaseStoreCategoryService.getCategoryTree(queryWrapper)); } From f2a07771b28fbb66d04478079b3fc11ede312012 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Mon, 29 Sep 2025 17:11:37 +0800 Subject: [PATCH 12/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=97=E9=93=BA?= =?UTF-8?q?=E4=BA=8C=E7=BA=A7=E5=88=86=E7=B1=BB=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/store/service/impl/ShopStoreBaseServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) 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 97a28e00..d5e690da 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 @@ -2301,6 +2301,11 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl Date: Mon, 29 Sep 2025 17:30:41 +0800 Subject: [PATCH 13/22] =?UTF-8?q?=E6=8F=90=E7=8E=B0=E5=92=8C=E9=A1=BA?= =?UTF-8?q?=E4=B8=B0=E5=90=8C=E5=9F=8E=E9=85=8D=E7=BD=AE=20=E8=BD=AC?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/pojo/dto/LklSeparateDTO.java | 8 +-- .../suisung/mall/common/utils/CheckUtil.java | 62 +++++++++++++++-- .../service/impl/LakalaApiServiceImpl.java | 5 +- .../service/impl/SFExpressApiServiceImpl.java | 2 +- .../admin/ShopStorePrinterController.java | 6 ++ .../service/ShopStorePrinterService.java | 8 +++ .../impl/ShopStoreBaseServiceImpl.java | 8 ++- .../impl/ShopStorePrinterServiceImpl.java | 66 +++++++++++++++++-- .../mall/shop/store/utis/FeieUtil.java | 6 +- .../src/main/resources/bootstrap-prod.yml | 2 +- 10 files changed, 148 insertions(+), 25 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 4f454da4..13c43d3a 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(1); // 分账总额 1000分 - dto2.setShippingFee(0); // 配送费 100分 + dto2.setTotalSeparateAmount(1500); // 分账总额 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.06")); // 平台分账比例 0.01 // 不设置一级和二级代理商分账比例,测试不参与分账的情况 // dto2.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.023 (会产生小数) // dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数) 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-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 78ddc55d..8c194b82 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -2823,11 +2823,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { // RMK 本次分账成功后,给商户和接收方 D1提现到银行卡 if (CollUtil.isNotEmpty(detailDatas)) { + int idx = 1; for (JSONObject detailData : detailDatas.jsonIter()) { String recvNo = detailData.getStr("recv_no"); String amt = detailData.getStr("amt"); if (StrUtil.isNotBlank(recvNo) && StrUtil.isNotBlank(amt)) { - Boolean drawSuccess = ewalletWithDrawD1(recvNo, outSeparateNo, amt, JSONUtil.toJsonStr(detailDatas)); + String outSeparateNoTemp = String.format("%s_%s", outSeparateNo, idx); + Boolean drawSuccess = ewalletWithDrawD1(recvNo, outSeparateNoTemp, amt, JSONUtil.toJsonStr(detailDatas)); + idx++; log.info("[拉卡拉分账通知] 账户D1提现{},商户号={},分账单号={},金额={}分", Boolean.TRUE.equals(drawSuccess) ? "成功" : "失败", recvNo, outSeparateNo, amt); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java index 2e3021f1..efb562c7 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java @@ -1112,7 +1112,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { // wxOrderShippingService.notifyConfirmReceive(shopStoreSfOrder.getShop_order_id()); // 订单确认收货 - // shopOrderBaseService.receive(shopStoreSfOrder.getShop_order_id(), null); +// shopOrderBaseService.receive(shopStoreSfOrder.getShop_order_id(), null); String orderId = shopStoreSfOrder.getShop_order_id(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStorePrinterController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStorePrinterController.java index 27f9eaf9..ec799cf9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStorePrinterController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStorePrinterController.java @@ -137,5 +137,11 @@ public class ShopStorePrinterController { return shopStorePrinterService.tryPrint(printer_id); } + @ApiOperation(value = "向厂家删除一个打票机", notes = "向厂家删除一个打票机") + @RequestMapping(value = "/delete/from/feie", method = {RequestMethod.POST}) + public CommonResult deleteFeiePrinter(@RequestParam(name = "sn_list", required = true) String snList) { + return shopStorePrinterService.deleteFeiePrinter(snList); + } + } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java index 123ae4f1..833c0e35 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java @@ -94,5 +94,13 @@ public interface ShopStorePrinterService extends IBaseService * @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 97a28e00..7bc5b37a 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("修改失败!"); - } @@ -243,9 +270,9 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl retPair = feieUtil.delPrinter(record.getPrinter_sn()); + if (!retPair.getFirst()) { + return CommonResult.failed(retPair.getSecond()); } } @@ -442,5 +469,30 @@ 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/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/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: #服务地址 From 2a4dfe2b24102d6a6e1a3c1d901848bc79b10813 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Mon, 29 Sep 2025 17:57:16 +0800 Subject: [PATCH 14/22] =?UTF-8?q?=E6=8F=90=E7=8E=B0=E8=AE=A2=E5=8D=95fix?= =?UTF-8?q?=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/lakala/service/impl/LakalaApiServiceImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 8c194b82..445f9b1c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -2821,18 +2821,18 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 更改分账状态:分账成功 shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, ""); - // RMK 本次分账成功后,给商户和接收方 D1提现到银行卡 + // 本次分账成功后,给商户和接收方 D1提现到银行卡 if (CollUtil.isNotEmpty(detailDatas)) { int idx = 1; for (JSONObject detailData : detailDatas.jsonIter()) { String recvNo = detailData.getStr("recv_no"); String amt = detailData.getStr("amt"); if (StrUtil.isNotBlank(recvNo) && StrUtil.isNotBlank(amt)) { - String outSeparateNoTemp = String.format("%s_%s", outSeparateNo, idx); + String outSeparateNoTemp = String.format("%s_%d", outSeparateNo, idx); Boolean drawSuccess = ewalletWithDrawD1(recvNo, outSeparateNoTemp, amt, JSONUtil.toJsonStr(detailDatas)); - idx++; log.info("[拉卡拉分账通知] 账户D1提现{},商户号={},分账单号={},金额={}分", - Boolean.TRUE.equals(drawSuccess) ? "成功" : "失败", recvNo, outSeparateNo, amt); + Boolean.TRUE.equals(drawSuccess) ? "成功" : "失败", recvNo, outSeparateNoTemp, amt); + idx++; } } } From ce85d72ceefb5656e2fca76fa54a0cc2d82844a1 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Tue, 30 Sep 2025 00:23:16 +0800 Subject: [PATCH 15/22] =?UTF-8?q?=E5=B0=8F=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/LakalaApiServiceImpl.java | 15 ++++++-- .../order/service/ShopOrderInfoService.java | 8 +++++ .../impl/ShopOrderBaseServiceImpl.java | 11 +++--- .../impl/ShopOrderInfoServiceImpl.java | 35 +++++++++++++++++++ .../service/impl/SFExpressApiServiceImpl.java | 5 +-- .../impl/ShopStorePrinterServiceImpl.java | 2 -- ...StoreSameCityTransportBaseServiceImpl.java | 1 + .../impl/WxOrderShippingServiceImpl.java | 3 +- 8 files changed, 67 insertions(+), 13 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 445f9b1c..311cb06c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -39,6 +39,7 @@ import com.suisung.mall.shop.lakala.utils.LakalaUtil; import com.suisung.mall.shop.message.service.PushMessageService; import com.suisung.mall.shop.message.service.ShopMessageTemplateService; import com.suisung.mall.shop.order.service.ShopOrderBaseService; +import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.order.service.ShopOrderLklService; import com.suisung.mall.shop.page.service.OssService; import com.suisung.mall.shop.store.service.ShopMchEntryService; @@ -153,6 +154,10 @@ public class LakalaApiServiceImpl implements LakalaApiService { @Resource private ShopOrderBaseService shopOrderBaseService; + @Lazy + @Resource + private ShopOrderInfoService shopOrderInfoService; + @Lazy @Resource private PushMessageService pushMessageService; @@ -772,7 +777,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { /** - * 发货类交易确认收货通知处理 + * 发货类交易确认收货通知处理, 微信通知拉卡拉,拉卡拉通知我们系统,已经完成确认收货 *

* 参考文档:https://o.lakala.com/#/home/document/detail?id=1003 *

@@ -2825,8 +2830,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { if (CollUtil.isNotEmpty(detailDatas)) { int idx = 1; for (JSONObject detailData : detailDatas.jsonIter()) { - String recvNo = detailData.getStr("recv_no"); - String amt = detailData.getStr("amt"); + 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)); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java index 812ec6d3..c0488662 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java @@ -81,4 +81,12 @@ public interface ShopOrderInfoService extends IBaseService { */ 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/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index fc71150d..47dc8acf 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 @@ -2374,10 +2374,10 @@ public class ShopOrderBaseServiceImpl 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/sfexpress/service/impl/SFExpressApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java index efb562c7..72cbfbbd 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java @@ -263,7 +263,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { //汽配 18:珠宝 20:披萨 21:中餐 22:水产 27:专人直送 32:中端饮品 33:便利店 34:面包糕点 35:火锅 36:证照 40:烧烤小龙虾 41:外部落地配 47:烟酒 //行 48:成人用品 99:其他 // 经营类型: 快餐,百货,生鲜,高端饮品,蛋糕,鲜花,数码,服装,披萨,水产,中端饮品,便利店,面包糕点,烟酒,其他 - params.put("shop_product_types", "1,3,6,8,13,14,15,16,20,22,32,33,34,47,99"); + params.put("shop_product_types", "33"); //"1,3,6,8,13,14,15,16,20,22,32,33,34,47,99" params.put("shop_type", 1); // 店铺类型: 1-普通型 2-平台型 params.put("shop_address", shopAddress); // 店铺地址 params.put("longitude", longitude); // 经度 @@ -1014,7 +1014,8 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { orderStatus = StateCode.ORDER_STATE_RECEIVED; //已签收 // 通知微信用户确认收货(同城配送不能调用微信的确认收货) - // wxOrderShippingService.notifyConfirmReceive(shopStoreSfOrder.getShop_order_id()); + wxOrderShippingService.notifyConfirmReceive(shopStoreSfOrder.getShop_order_id()); + // 不要提前 订单确认收货,等微信拉卡拉确认通知 // try { // shopOrderBaseService.receive(shopOrderId, null); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java index f76971fb..bf7f484e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java @@ -259,7 +259,6 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl saveOrUpdateResult = saveOrUpdateShopStoreSameCityTransportBase(transportBase); 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 dbecf5e4..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 @@ -237,8 +237,7 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService { // 跳转链接设置 setMsgJumpPath(orderId); - - + log.info("通知微信用户确认收货成功, 订单ID: {}", orderId); return Pair.of(true, "通知微信用户确认收货成功"); From 62d76b3a67290509e9f9c3f6b5877ee50fab0f2a Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 30 Sep 2025 10:47:55 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=97=E9=93=BA?= =?UTF-8?q?=E8=90=A5=E4=B8=9A=E6=97=B6=E9=97=B4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/service/impl/ShopStoreBaseServiceImpl.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 a00c33b9..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 @@ -3875,6 +3875,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl shopStoreInfoUpdateWrapper=new UpdateWrapper<>(); + shopStoreInfoUpdateWrapper.eq("store_id",storeId); + shopStoreInfoUpdateWrapper.set("store_opening_hours",store_opening_hours); + shopStoreInfoUpdateWrapper.set("store_close_hours",store_close_hours); + shopStoreInfoService.update(shopStoreInfoUpdateWrapper); + } // 使用 UpdateWrapper 更新店铺营业状态 boolean updated = update(new UpdateWrapper() .eq("store_id", storeId) From afaca094f52941927263c9e2fe409cd76c9d20e1 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Tue, 30 Sep 2025 22:16:19 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E9=80=80=E6=AC=BE=20=E9=85=8D=E9=80=81?= =?UTF-8?q?=E8=B4=B9=20=E6=B5=81=E7=A8=8B=E6=9B=B4=E6=94=B9=EF=BC=8C?= =?UTF-8?q?=E6=8B=89=E5=8D=A1=E6=8B=89=20=E5=90=88=E5=8D=95=20=E9=9C=80?= =?UTF-8?q?=E5=88=86=E5=BC=80=20=E9=80=80=E6=AC=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/constant/CommonConstant.java | 5 + .../exception/GlobalExceptionHandler.java | 4 +- .../common/modules/order/ShopOrderData.java | 3 + .../modules/order/dto/MchOrderInfoDTO.java | 2 + .../mall/common/pojo/dto/LklSeparateDTO.java | 4 +- .../service/impl/LakalaPayServiceImpl.java | 4 + .../impl/PayConsumeTradeServiceImpl.java | 358 +----------------- .../shop/lakala/service/LakalaApiService.java | 24 ++ .../service/impl/LakalaApiServiceImpl.java | 107 ++++++ .../impl/ShopMessageTemplateServiceImpl.java | 81 ++-- .../admin/ShopOrderReturnController.java | 3 + .../order/service/ShopOrderLklService.java | 19 + .../impl/ShopOrderBaseServiceImpl.java | 62 ++- .../service/impl/ShopOrderLklServiceImpl.java | 62 +++ .../impl/ShopOrderReturnServiceImpl.java | 65 ++-- .../mapper/order/ShopOrderBaseMapper.xml | 4 +- .../mapper/order/ShopOrderDataMapper.xml | 2 +- 17 files changed, 367 insertions(+), 442 deletions(-) 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/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 13c43d3a..7050b80f 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 @@ -68,8 +68,8 @@ public class LklSeparateDTO { dto2.setMchRatio(new BigDecimal("0.95")); // 商家分账比例 0.857 (会产生小数) dto2.setPlatRatio(new BigDecimal("0.06")); // 平台分账比例 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-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..7777d5df 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 @@ -574,6 +574,10 @@ public class LakalaPayServiceImpl implements LakalaPayService { // TODO 重要的逻辑,获取是否已经分账?已分账:分账退回;查商家账户余额够不够退回?够就执行退回 + if (StrUtil.isBlank(refundReason)) { + refundReason = "商家与买方协商退款"; + } + // 5. 构造退款请求并发送 V3LabsRelationRefundRequest refundRequest = new V3LabsRelationRefundRequest(); refundRequest.setOutTradeNo(outTradeNo); 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 store_rows = shopService.getsShopStoreBase(store_ids); - - List user_ids = return_rows.stream().map(ShopOrderReturn::getBuyer_user_id).distinct().collect(Collectors.toList()); - // 不使用缓存防止缓存被保存导致回滚异常 - List payUserResources = payUserResourceService.listByIds(user_ids); - List order_info_rows = shopService.getsShopOrderInfo(order_id_rows); - - List return_ids = return_rows.stream().map(ShopOrderReturn::getReturn_id).distinct().collect(Collectors.toList()); - if (CollUtil.isEmpty(return_ids)) { - return false; - } - - Map queryParams = new HashMap(); - queryParams.put("return_id:in", return_ids); - List orderReturnItemList = shopService.findShopOrderReturnItem(queryParams); - if (CollUtil.isEmpty(orderReturnItemList)) { - throw new ApiException(I18nUtil._("没有找到相关的退货商品信息!")); - } - - List order_item_ids = orderReturnItemList.stream().map(ShopOrderReturnItem::getOrder_item_id).distinct().collect(Collectors.toList()); - List orderItemList = shopService.getsShopOrderItem(order_item_ids); - - Date curTime = new Date(); - DateTime ymdTime = DateUtil.parse(DateUtil.format(curTime, "yyyy-MM-dd")); - - float points_vaue_rate = accountBaseConfigService.getConfig("points_vaue_rate", 0f); - - // 积分抵扣,暂时忽略,不涉及此处支付。 - // 按照次序,依次支付。 - for (ShopOrderReturn return_row : return_rows) { - - Integer user_id = return_row.getBuyer_user_id(); - if (CheckUtil.isEmpty(user_id)) { - throw new ApiException(I18nUtil._("买家信息有误!")); - } - - Integer buyer_store_id = return_row.getBuyer_store_id(); - - Integer store_id = return_row.getStore_id(); - Optional storeOpl = store_rows.stream().filter(s -> ObjectUtil.equal(Convert.toInt(s.get("store_id")), store_id)).findFirst(); - Map store_row = storeOpl.orElseGet(HashMap::new); - - Integer seller_id = (Integer) store_row.get("user_id"); - if (CheckUtil.isEmpty(seller_id)) { - throw new ApiException(I18nUtil._("卖家信息有误!")); - } - - // 判断当前余额 - Optional userResourceOpl = payUserResources.stream().filter(s -> ObjectUtil.equal(user_id, s.getUser_id())).findFirst(); - PayUserResource user_resource_row = userResourceOpl.orElseGet(PayUserResource::new); - - // todo 判断是否需要退佣金 $return_row['return_commision_fee'] - BigDecimal return_commision_fee = BigDecimal.ZERO; - - // 不是退运费 - String order_id = return_row.getOrder_id(); - Integer return_is_shipping_fee = return_row.getReturn_is_shipping_fee(); - if (CheckUtil.isEmpty(return_is_shipping_fee)) { - float withdraw_received_day = Convert.toFloat(accountBaseConfigService.getConfig("withdraw_received_day")); - if (withdraw_received_day == 0) { - withdraw_received_day = 7f; - } - - if (withdraw_received_day >= 0) { - Optional orderInfoOpl = order_info_rows.stream().filter(s -> ObjectUtil.equal(order_id, s.getOrder_id())).findFirst(); - ShopOrderInfo order_info_row = orderInfoOpl.orElseGet(ShopOrderInfo::new); - - Integer order_state_id = order_info_row.getOrder_state_id(); - Integer order_is_paid = order_info_row.getOrder_is_paid(); - Long order_deal_time = order_info_row.getOrder_deal_time(); - long time = DateUtil.offsetDay(curTime, -7).getTime(); - - // 未到可结算时间可退佣金 - if (ObjectUtil.equal(order_state_id, StateCode.ORDER_STATE_FINISH) - && ObjectUtil.equal(order_is_paid, StateCode.ORDER_PAID_STATE_YES) - && order_deal_time < time) { - return_commision_fee = BigDecimal.ZERO; - } else { - return_commision_fee = return_row.getReturn_commision_fee(); - } - } - } - - BigDecimal waiting_refund_amount = return_row.getReturn_refund_amount(); - if (CheckUtil.isNotEmpty(waiting_refund_amount)) { - - Optional OrderDataOpl = order_data_rows.stream().filter(s -> ObjectUtil.equal(s.getOrder_id(), order_id)).findFirst(); - ShopOrderData order_data_row = OrderDataOpl.orElseGet(ShopOrderData::new); - - BigDecimal order_points_fee = order_data_row.getOrder_points_fee(); - BigDecimal order_refund_agree_points = order_data_row.getOrder_refund_agree_points(); - - BigDecimal buyer_user_money = waiting_refund_amount; - BigDecimal buyer_user_points = BigDecimal.ZERO; - - BigDecimal seller_user_money = waiting_refund_amount.negate(); - String return_id = return_row.getReturn_id(); - - // 写入流水 - PayConsumeRecord buyer_consume_record_row = new PayConsumeRecord(); - - buyer_consume_record_row.setOrder_id(return_id); - buyer_consume_record_row.setUser_id(user_id); - buyer_consume_record_row.setStore_id(buyer_store_id); - buyer_consume_record_row.setUser_nickname(""); // todo User_nickname - buyer_consume_record_row.setRecord_date(ymdTime); - buyer_consume_record_row.setRecord_year(DateUtil.year(ymdTime)); - buyer_consume_record_row.setRecord_month(DateUtil.month(ymdTime) + 1); - buyer_consume_record_row.setRecord_day(DateUtil.dayOfMonth(ymdTime)); - buyer_consume_record_row.setRecord_title(I18nUtil._("退款单:") + return_id); - buyer_consume_record_row.setRecord_time(curTime); - buyer_consume_record_row.setPayment_met_id(PaymentType.PAYMENT_MET_MONEY); - - // 增加流水 - buyer_consume_record_row.setRecord_money(waiting_refund_amount); // 佣金问题? - buyer_consume_record_row.setTrade_type_id(StateCode.TRADE_TYPE_REFUND_GATHERING); - - // 卖家流水记录 - PayConsumeRecord seller_consume_record_row = ObjectUtil.clone(buyer_consume_record_row); - - seller_consume_record_row.setUser_id(seller_id); - seller_consume_record_row.setStore_id(store_id); - seller_consume_record_row.setRecord_money(NumberUtil.add(waiting_refund_amount.negate(), return_commision_fee)); - seller_consume_record_row.setRecord_commission_fee(return_commision_fee.negate()); - seller_consume_record_row.setTrade_type_id(StateCode.TRADE_TYPE_REFUND_PAY); - - order_data_row.setOrder_refund_agree_amount(NumberUtil.add(waiting_refund_amount, order_refund_agree_points)); - - // todo 不能在这里统计,并发情况下seata操作同一张表数据会导致无法回滚的问题 原因:A线程修改完数据记录到undo_log表中,回滚的时候发现B线程也修改了这个数据 - // 平台佣金总额 - /*if (CheckUtil.isNotEmpty(return_commision_fee)) { - PayPlantformResource plantform_resource_row = payPlantformResourceService.get(DATA_ID); - if (plantform_resource_row == null) { - plantform_resource_row = new PayPlantformResource(); - plantform_resource_row.setPlantform_resource_id(DATA_ID); - } - plantform_resource_row.setPlantform_commission_fee(NumberUtil.add(return_commision_fee.negate(), plantform_resource_row.getPlantform_commission_fee())); - if (!payPlantformResourceService.saveOrUpdate(plantform_resource_row)) { - throw new ApiException(ResultCode.FAILED); - } - - BigDecimal order_commission_fee_refund = order_data_row.getOrder_commission_fee_refund(); - order_data_row.setOrder_commission_fee_refund(NumberUtil.add(order_commission_fee_refund, return_commision_fee)); - }*/ - - // 读取退款单项目 - List order_return_item_rows = orderReturnItemList.stream().filter(s -> ObjectUtil.equal(s.getReturn_id(), return_id)).collect(Collectors.toList()); - if (CollUtil.isNotEmpty(orderItemList)) { - - List order_item_rows = new ArrayList<>(); - for (ShopOrderReturnItem order_return_item_row : order_return_item_rows) { - - Long order_item_id = order_return_item_row.getOrder_item_id(); - Optional orderItemOpl = orderItemList.stream().filter(s -> ObjectUtil.equal(order_item_id, s.getOrder_item_id())).findFirst(); - if (orderItemOpl.isPresent()) { - ShopOrderItem order_item_row = orderItemOpl.get(); - - BigDecimal return_item_subtotal = order_return_item_row.getReturn_item_subtotal(); - Integer return_item_num = order_return_item_row.getReturn_item_num(); - BigDecimal order_item_return_agree_amount = order_item_row.getOrder_item_return_agree_amount(); - Integer order_item_return_agree_num = ObjectUtil.defaultIfNull(order_item_row.getOrder_item_return_agree_num(), 0); - - order_item_row.setOrder_item_return_agree_amount(NumberUtil.add(order_item_return_agree_amount, return_item_subtotal)); - order_item_row.setOrder_item_return_agree_num(order_item_return_agree_num + return_item_num); - - // 订单未结算才发放佣金 - if (CheckUtil.isNotEmpty(return_commision_fee)) { - BigDecimal return_item_commision_fee = order_return_item_row.getReturn_item_commision_fee(); - BigDecimal order_item_commission_fee_refund = ObjectUtil.defaultIfNull(order_item_row.getOrder_item_commission_fee_refund(), BigDecimal.ZERO); - order_item_row.setOrder_item_commission_fee_refund(NumberUtil.add(return_item_commision_fee, order_item_commission_fee_refund)); - } - order_item_rows.add(order_item_row); - } - } - - if (CollUtil.isNotEmpty(order_item_rows)) { - if (!shopService.editsShopOrderItem(order_item_rows)) { - throw new ApiException(I18nUtil._("修改订单商品数据失败!")); - } - } - } - - // 买家数据 - if (!payConsumeRecordService.saveOrUpdate(buyer_consume_record_row)) { - throw new ApiException(I18nUtil._("添加买家流水数据失败!")); - } - - // 如果混合了积分,优先退积分 - if (order_points_fee.compareTo(BigDecimal.ZERO) > 0 - && order_points_fee.compareTo(order_refund_agree_points) > 0) { - BigDecimal refund_points = NumberUtil.round(NumberUtil.min(NumberUtil.sub(order_points_fee, order_refund_agree_points)), 2); - buyer_user_money = NumberUtil.round(NumberUtil.sub(buyer_user_money, refund_points), 2); - - if (points_vaue_rate > 0) { - buyer_user_points = NumberUtil.div(refund_points, points_vaue_rate); - } - order_data_row.setOrder_refund_agree_points(NumberUtil.add(order_refund_agree_points, refund_points)); - } - - // todo 优化代码 - if (order_refund_flag) { - // 读取在线支付信息,如果无在线支付信息,则余额支付 否则在线支付【联合支付】判断 - QueryWrapper depositQueryWrapper = new QueryWrapper<>(); - depositQueryWrapper.apply(order_id != null, "FIND_IN_SET ('" + order_id + "', order_id )"); - PayConsumeDeposit consume_row = payConsumeDepositService.findOne(depositQueryWrapper); - - if (consume_row != null) { - Integer payment_channel_id = consume_row.getPayment_channel_id(); - PayPaymentChannel payment_channel_row = payPaymentChannelService.get(payment_channel_id); - String payment_channel_code = payment_channel_row.getPayment_channel_code(); - BigDecimal deposit_total_fee = consume_row.getDeposit_total_fee(); - - if (Arrays.asList("alipay", "wx_native").contains(payment_channel_code)) { - BigDecimal d_money = NumberUtil.round(NumberUtil.sub(buyer_user_money, deposit_total_fee), 2); - if (d_money.compareTo(BigDecimal.ZERO) > 0) { - PayUserResource payUserResource = payUserResourceService.getById(user_id); - payUserResource.setUser_money(NumberUtil.add(payUserResource.getUser_money(), d_money)); - if (!payUserResourceService.edit(payUserResource)) { - throw new ApiException(I18nUtil._("用户退款失败!")); - } - } - - if (buyer_user_points.compareTo(BigDecimal.ZERO) > 0) { - if (!payUserResourceService.points(user_id, buyer_user_points, PointsType.POINTS_TYPE_CONSUME_RETRUN, return_id, store_id, null, return_id)) { - throw new ApiException(I18nUtil._("用户退积分失败!")); - } - } - - String deposit_trade_no = consume_row.getDeposit_trade_no(); - - ShopOrderReturn orderReturn = new ShopOrderReturn(); - orderReturn.setReturn_id(return_id); - orderReturn.setReturn_channel_code(payment_channel_code); - orderReturn.setDeposit_trade_no(deposit_trade_no); - orderReturn.setPayment_channel_id(payment_channel_id); - orderReturn.setTrade_payment_amount(deposit_total_fee); - if (!shopService.editShopOrderReturn(orderReturn)) { - throw new ApiException(I18nUtil._("修改退单信息失败!")); - } - } else { - if (buyer_user_money.compareTo(BigDecimal.ZERO) > 0) { - PayUserResource payUserResource = payUserResourceService.getById(user_id); - payUserResource.setUser_money(NumberUtil.add(payUserResource.getUser_money(), buyer_user_money)); - if (!payUserResourceService.edit(payUserResource)) { - throw new ApiException(I18nUtil._("用户退款失败!")); - } - } - - if (buyer_user_points.compareTo(BigDecimal.ZERO) > 0) { - if (!payUserResourceService.points(user_id, buyer_user_points, PointsType.POINTS_TYPE_CONSUME_RETRUN, return_id, store_id, null, return_id)) { - throw new ApiException(I18nUtil._("用户退积分失败!")); - } - } - - ShopOrderReturn orderReturn = new ShopOrderReturn(); - orderReturn.setReturn_id(return_id); - orderReturn.setReturn_channel_flag(1); - if (!shopService.editShopOrderReturn(orderReturn)) { - throw new ApiException(I18nUtil._("修改退单信息失败!")); - } - } - } else { - if (buyer_user_money.compareTo(BigDecimal.ZERO) > 0) { - PayUserResource payUserResource = payUserResourceService.getById(user_id); - payUserResource.setUser_money(NumberUtil.add(payUserResource.getUser_money(), buyer_user_money)); - if (!payUserResourceService.edit(payUserResource)) { - throw new ApiException(I18nUtil._("用户退款失败!")); - } - } - - if (buyer_user_points.compareTo(BigDecimal.ZERO) > 0) { - if (!payUserResourceService.points(user_id, buyer_user_points, PointsType.POINTS_TYPE_CONSUME_RETRUN, return_id, store_id, null, return_id)) { - throw new ApiException(I18nUtil._("用户退积分失败!")); - } - } - - ShopOrderReturn orderReturn = new ShopOrderReturn(); - orderReturn.setReturn_id(return_id); - orderReturn.setReturn_channel_flag(1); - if (!shopService.editShopOrderReturn(orderReturn)) { - throw new ApiException(I18nUtil._("修改退单信息失败!")); - } - } - } else { - if (buyer_user_money.compareTo(BigDecimal.ZERO) > 0) { - PayUserResource payUserResource = payUserResourceService.getById(user_id); - payUserResource.setUser_money(NumberUtil.add(payUserResource.getUser_money(), buyer_user_money)); - if (!payUserResourceService.edit(payUserResource)) { - throw new ApiException(I18nUtil._("用户退款失败!")); - } - } - - if (buyer_user_points.compareTo(BigDecimal.ZERO) > 0) { - if (!payUserResourceService.points(user_id, buyer_user_points, PointsType.POINTS_TYPE_CONSUME_RETRUN, return_id, store_id, null, return_id)) { - throw new ApiException(I18nUtil._("用户退积分失败!")); - } - } - - ShopOrderReturn orderReturn = new ShopOrderReturn(); - orderReturn.setReturn_id(return_id); - orderReturn.setReturn_channel_flag(1); - if (!shopService.editShopOrderReturn(orderReturn)) { - throw new ApiException(I18nUtil._("修改退单信息失败!")); - } - } - - if (!shopService.editShopOrderData(order_data_row)) { - throw new ApiException(I18nUtil._("修改详细信息失败!")); - } - - // todo 修改订单状态? - if (buyer_user_money != null) { - // 流水记录 - if (!payConsumeRecordService.saveOrUpdate(seller_consume_record_row)) { - throw new ApiException(I18nUtil._("写入卖家信息失败!")); - } - - seller_user_money = NumberUtil.add(buyer_user_money.negate(), return_commision_fee); - PayUserResource payUserResource = payUserResourceService.getById(seller_id); - payUserResource.setUser_money(NumberUtil.add(payUserResource.getUser_money(), seller_user_money)); - if (!payUserResourceService.edit(payUserResource)) { - throw new ApiException(I18nUtil._("卖家用户退款失败!")); - } - } - - paid_return_id_row.add(return_id); - } - } - - if (CollUtil.isNotEmpty(paid_return_id_row)) { - // 远程服务器订单更改放入 - // 本地服务器订单更改 - if (!shopService.setReturnPaidYes(paid_return_id_row)) { - throw new ApiException(ResultCode.FAILED); - } - } - - return true; - } /** * 更改交易订单的订单状态和付款状态 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java index 6f12006b..43fdabd5 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java @@ -11,9 +11,11 @@ package com.suisung.mall.shop.lakala.service; import cn.hutool.json.JSONObject; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.store.ShopMchEntry; +import com.suisung.mall.common.pojo.dto.LklSeparateDTO; import org.springframework.data.util.Pair; import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; /** * 拉卡拉业务接口 @@ -350,4 +352,26 @@ public interface LakalaApiService { */ JSONObject ewalletWithDrawNotify(HttpServletRequest request); + /** + * 商户分账参数计算及评估 + * + * @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 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象 + */ + Pair calculateAndEvaluateSharingParams(int splitMode, + Integer orderPayAmount, + Integer shippingFeeInner, + BigDecimal mchSplitRatioRaw, + BigDecimal platSplitRatio, + BigDecimal agent1stRatio, + BigDecimal agent2ndRatio, + Integer refCanSeparateAmt); + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 311cb06c..2f17c316 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -3612,4 +3612,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 { @@ -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 47dc8acf..d77a68ae 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.*; @@ -3228,6 +3229,8 @@ 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); + Map 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); } // 付款成功,对通知推广员进行提醒 @@ -6554,6 +6557,8 @@ 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/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..eae5a6b9 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 @@ -196,6 +196,10 @@ 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); + 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 +1635,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 +2620,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl + @@ -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, From 1d4b8842159702dc0d9ad1fb0028009b67c7f273 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Wed, 1 Oct 2025 01:03:52 +0800 Subject: [PATCH 18/22] =?UTF-8?q?=E5=88=86=E8=B4=A6=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=BA=86=E5=85=AC=E5=85=B1=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/constant/RedisConstant.java | 5 +- .../mall/common/pojo/dto/LklSeparateDTO.java | 8 +- .../service/impl/UniCloudPushServiceImpl.java | 21 ++--- .../service/impl/LakalaApiServiceImpl.java | 85 ++++++++++--------- .../order/listener/OrderPayedListener.java | 11 +++ 5 files changed, 69 insertions(+), 61 deletions(-) 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/pojo/dto/LklSeparateDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java index 7050b80f..5ddeedbc 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(1500); // 分账总额 1000分 + dto2.setTotalSeparateAmount(900); // 分账总额 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.06")); // 平台分账比例 0.01 + dto2.setPlatRatio(new BigDecimal("0.01")); // 平台分账比例 0.01 // 不设置一级和二级代理商分账比例,测试不参与分账的情况 - dto2.setAgent1stRatio(new BigDecimal("0.11")); // 一级代理商分账比例 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-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 2f17c316..b72c8235 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -2471,16 +2471,16 @@ public class LakalaApiServiceImpl implements LakalaApiService { return Pair.of(true, "订单已分账,请勿重复操作"); } - // 5. 检查可分账余额 - Integer canSeparateAmt = null; + // 5. 从拉卡拉平台检查可分账余额 + Integer refCanSeparateAmt = null; Pair mchCanSplitAmt = queryMchCanSplitAmt(lklMerchantNo, receiveLogNo, shopOrderLkl.getLkl_log_date()); if (mchCanSplitAmt != null) { - log.info("[分账操作] 查询拉卡拉可分账余额接口:lklMerchantNo={} logDate={} receiveLogNo={}", - lklMerchantNo, shopOrderLkl.getLkl_log_date(), receiveLogNo); + log.info("[分账操作] 查询拉卡拉可分账余额接口:lklMerchantNo={} logDate={} receiveLogNo={} 结果:{}", + lklMerchantNo, shopOrderLkl.getLkl_log_date(), receiveLogNo, mchCanSplitAmt); // 可分账金额 - canSeparateAmt = Convert.toInt(mchCanSplitAmt.getSecond()); - if (canSeparateAmt == null || canSeparateAmt <= 0) { + refCanSeparateAmt = Convert.toInt(mchCanSplitAmt.getSecond()); + if (CheckUtil.isEmpty(refCanSeparateAmt)) { log.warn("[分账操作] lklMerchantNo={} receiveTradeNo={} receiveLogNo={} 拉卡拉可分账金额无值或0,系统将自动计算可分账金额", lklMerchantNo, receiveTradeNo, receiveLogNo); } @@ -2493,7 +2493,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { BigDecimal mchSplitRatioRaw = shopOrderLkl.getSplit_ratio(); if (mchSplitRatioRaw == null || mchSplitRatioRaw.compareTo(BigDecimal.ZERO) <= 0 || - mchSplitRatioRaw.compareTo(new BigDecimal(100)) > 0) { + mchSplitRatioRaw.compareTo(new BigDecimal(100)) >= 0) { log.error("[分账操作] 店铺[{}]商家分账比例[{}]不在(0-100]范围内,无法分账", shopOrderLkl.getStore_id(), mchSplitRatioRaw); return Pair.of(false, "商家分账比例有误"); @@ -2505,60 +2505,55 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 获取分账平台接收方信息 LklLedgerMerReceiverBind platformReceiver = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo); if (platformReceiver == null) { - log.error("[分账操作] 店铺[{}]未绑定平台方接收账户,跳过分账", shopOrderLkl.getStore_id()); + log.error("[分账操作] 商户号{} 未绑定平台方接收账户,跳过分账", merchantNo); return Pair.of(false, "平台方未绑定账户"); } // 获取代理商分账信息 BigDecimal platformSplitRatio = BigDecimal.valueOf(0.01); BigDecimal distributorSplitRatio = BigDecimal.ZERO; - List distributorReceivers = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo); if (CollUtil.isNotEmpty(distributorReceivers)) { - distributorSplitRatio = BigDecimal.valueOf(1).subtract(platformSplitRatio).subtract(mchSplitRatio); + distributorSplitRatio = BigDecimal.valueOf(1).subtract(mchSplitRatio).subtract(platformSplitRatio); log.debug("[分账操作] 检测到代理商存在,调整分账比例: 代理商比例={}, 平台比例={}", distributorSplitRatio, platformSplitRatio); + } else { + platformSplitRatio = BigDecimal.valueOf(1).subtract(mchSplitRatio); + distributorSplitRatio = null; } // 内部配送费 Integer shoppingFeeInner = CheckUtil.isEmpty(shopOrderLkl.getShopping_fee_inner()) ? 0 : shopOrderLkl.getShopping_fee_inner(); - BigDecimal wxFeeRatio = StrUtil.isEmpty(wxFee) ? BigDecimal.valueOf(0.0025) : new BigDecimal(wxFee).divide(BigDecimal.valueOf(100)); - // 构建分账参数对象 - LklSeparateDTO lklSeparateDTO = new LklSeparateDTO(); - lklSeparateDTO.setTotalSeparateAmount(shopOrderLkl.getTotal_amt()); - lklSeparateDTO.setShippingFee(shoppingFeeInner); - lklSeparateDTO.setLklRatio(wxFeeRatio); // 拉卡拉给的微信分账比例 0.0025 千分之2.5 - lklSeparateDTO.setMchRatio(mchSplitRatio); - lklSeparateDTO.setPlatRatio(platformSplitRatio); + // 计算拉卡拉手续费、商家分账金额、平台和代理商的分账金额 + Pair calcResult = calculateAndEvaluateSharingParams( + CommonConstant.SeparateCalcMode_CanSeparateAmt, + shopOrderLkl.getTotal_amt(), + shoppingFeeInner, + mchSplitRatio, + platformSplitRatio, + null, distributorSplitRatio, refCanSeparateAmt); - if (distributorSplitRatio.compareTo(BigDecimal.ZERO) > 0) { // 二级代理商参与分账 - lklSeparateDTO.setAgent2ndRatio(distributorSplitRatio); - } - lklSeparateDTO.setRefCanSeparateAmount(canSeparateAmt); // 拉卡拉实时返回的可分账金额 - - // 分账方式:根据可分账金额分账 - LklSeparateDTO.SharingResult canSeparateAmtResult = lklSeparateDTO.sharingOnCanSeparateAmount(); - if (!canSeparateAmtResult.isSuccess()) { - log.error("[分账操作] 分账参数评估,结果无法分账 {}", lklSeparateDTO); - return Pair.of(false, "分账参数评估,结果无法分账"); + if (calcResult == null || !calcResult.getFirst() || calcResult.getSecond() == null) { + log.error("[分账操作] 分账参数评估,结果无法分账"); + return Pair.of(false, "分账数据评估,结果无法分账"); } + LklSeparateDTO lklSeparateDTO = calcResult.getSecond(); log.debug("[分账操作] 分账参数计算结果:{}", lklSeparateDTO); // 更新分账计算结果 shopOrderLkl.setSeparate_remark(lklSeparateDTO.toString()); // 写入分账具体情况 - if (CheckUtil.isEmpty(canSeparateAmt)) { + if (CheckUtil.isEmpty(refCanSeparateAmt)) { shopOrderLkl.setSplit_amt(lklSeparateDTO.getCanSeparateAmount()); - canSeparateAmt = lklSeparateDTO.getCanSeparateAmount(); + refCanSeparateAmt = lklSeparateDTO.getCanSeparateAmount(); } else { - shopOrderLkl.setSplit_amt_ref(canSeparateAmt); + shopOrderLkl.setSplit_amt_ref(refCanSeparateAmt); } - shopOrderLklService.safeUpdate(shopOrderLkl); // 分账金额校验 - if (canSeparateAmt <= 0) { + if (CheckUtil.isEmpty(refCanSeparateAmt)) { String errorMsg = String.format("[分账操作] 店铺[%s]订单[%s]分账金额[%d]低于1分钱,跳过分账", - shopOrderLkl.getStore_id(), orderId, canSeparateAmt); + shopOrderLkl.getStore_id(), orderId, refCanSeparateAmt); log.error(errorMsg); if (existingSeparateRecord != null) { lklOrderSeparateService.updateRemark(existingSeparateRecord.getId(), errorMsg); @@ -2574,7 +2569,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { Integer agentAmount = lklSeparateDTO.getAgent2ndAmount(); log.info("[分账操作] 金额计算结果:订单={}, 商户={}, 总金额={}分, 可分金额={}分, 商家比例={}, 商家分得={}分, 平台比例={}, 平台分得={}分, 代理商比例={}, 代理商分得={}分", - orderId, merchantNo, shopOrderLkl.getTotal_amt(), canSeparateAmt, mchSplitRatio, merchantAmount, + orderId, merchantNo, shopOrderLkl.getTotal_amt(), refCanSeparateAmt, mchSplitRatio, merchantAmount, platformSplitRatio, platformAmount, distributorSplitRatio, agentAmount); // 构建分账接收方分账参数 @@ -2598,11 +2593,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 二级代理商(县级)分账参数 if (agentAmount != null && agentAmount > 0 && CollUtil.isNotEmpty(distributorReceivers)) { - V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); - receiver.setRecvNo(distributorReceivers.get(0).getReceiver_no()); - receiver.setSeparateValue(agentAmount.toString()); - recvDatas.add(receiver); - log.debug("[分账操作] 添加代理商接收方: receiverNo={}, amount={}", distributorReceivers.get(0).getReceiver_no(), agentAmount); + LklLedgerMerReceiverBind distributorReceiver = distributorReceivers.get(0); + if (distributorReceiver != null && StrUtil.isNotBlank(distributorReceiver.getReceiver_no())) { + V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); + receiver.setRecvNo(distributorReceiver.getReceiver_no()); + receiver.setSeparateValue(agentAmount.toString()); + recvDatas.add(receiver); + log.debug("[分账操作] 添加代理商接收方: receiverNo={}, amount={}", distributorReceiver.getReceiver_no(), agentAmount); + } } // 初始化拉卡拉SDK @@ -2615,7 +2613,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { separateRequest.setOutSeparateNo(shopOrderLkl.getOut_separate_no()); separateRequest.setLogNo(shopOrderLkl.getLkl_receive_log_no()); // 使用确认收货流水号作为分账流水号 separateRequest.setLogDate(shopOrderLkl.getLkl_log_date()); - separateRequest.setTotalAmt(canSeparateAmt.toString()); + separateRequest.setTotalAmt(refCanSeparateAmt.toString()); separateRequest.setLklOrgNo(orgCode); separateRequest.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 separateRequest.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify"); @@ -2650,6 +2648,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { (respJson != null ? respJson.getStr("msg") : "未知错误")); } + // 只有在分账请求成功发送后才更新分账备注 + shopOrderLklService.safeUpdate(shopOrderLkl); + // 保存分账记录 JSONObject respData = respJson.getJSONObject("resp_data"); LklOrderSeparate separateRecord = new LklOrderSeparate(); @@ -2665,7 +2666,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { separateRecord.setRecv_datas(JSONUtil.toJsonStr(separateRequest.getRecvDatas())); separateRecord.setStatus(respData.getStr("status")); separateRecord.setTotal_amt(separateRequest.getTotalAmt()); - separateRecord.setActual_separate_amt(Convert.toStr(canSeparateAmt)); + separateRecord.setActual_separate_amt(Convert.toStr(refCanSeparateAmt)); separateRecord.setTotal_fee_amt(Convert.toStr(lklSeparateDTO.getLklAmount())); if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java index 04d9918b..a506e727 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java @@ -172,6 +172,13 @@ public class OrderPayedListener { // 处理异常,不抛出,以免影响到主流程 try { + // 检查是否已发送过推送消息(幂等性检查) + String pushFlagKey = RedisConstant.New_Order_Push_Flag_Key + orderId; + if (redisService.get(pushFlagKey) != null) { + logger.info("[订单支付监听] 推送消息已发送,跳过重复推送. 订单ID: {}", orderId); + continue; + } + // 同城配送或普通快递,都发送 unipush 推送:您有一个新的订单,请查收! String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" : ""; String title = String.format("您有一笔新的%s订单,请注意查收。", orderType); @@ -189,9 +196,13 @@ public class OrderPayedListener { mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L); redisService.set(RedisConstant.SF_Order_Proc_Expire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); + // 记录推送已发送状态,避免重复推送 + redisService.set(pushFlagKey, "1", 24 * 3600); // 24小时过期 + } catch (Exception e) { log.error("[订单支付监听] 发送推送消息失败. 订单ID: {}", orderId, e); } + } } } From 4ff33b938d82e895515a31a15b9011a92508f649 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Wed, 1 Oct 2025 23:56:49 +0800 Subject: [PATCH 19/22] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E6=A0=87=E9=A2=98=E5=90=8E=E9=9D=A2?= =?UTF-8?q?=E5=8A=A0=E8=A7=84=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ShopOrderItemServiceImpl.java | 23 ++- .../impl/ShopOrderReturnServiceImpl.java | 21 +-- .../mapper/order/ShopOrderItemMapper.xml | 133 +++++++++++++----- 3 files changed, 116 insertions(+), 61 deletions(-) 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/ShopOrderReturnServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java index eae5a6b9..f10dd34d 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; @@ -968,7 +965,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl - 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}, '%') + 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 20/22] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E6=A0=87=E9=A2=98=E5=90=8E=E9=9D=A2?= =?UTF-8?q?=E5=8A=A0=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 Date: Sat, 11 Oct 2025 14:22:08 +0800 Subject: [PATCH 21/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=AD=E9=97=B4=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sync/ProductQuantityConsumption.java | 52 ++++++ .../impl/ShopOrderBaseServiceImpl.java | 6 +- .../controller/SyncThirdDataController.java | 9 ++ .../mall/shop/sync/keymanage/RedisKey.java | 2 +- .../ProductQuantityConsumptionMapper.java | 11 ++ .../ProductQuantityConsumptionService.java | 8 + .../sync/service/SyncThirdDataService.java | 11 +- ...ProductQuantityConsumptionServiceImpl.java | 18 +++ .../impl/SyncThirdDataServiceImpl.java | 152 ++++++++++++++---- sql/shop/dev/20250929_ddl.sql | 12 ++ 10 files changed, 244 insertions(+), 37 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java create mode 100644 sql/shop/dev/20250929_ddl.sql diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java new file mode 100644 index 00000000..ddfdc6a7 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java @@ -0,0 +1,52 @@ +package com.suisung.mall.common.modules.sync; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 同步消费中间表,同步数据时保存数据到这个表中,消费完更新已消费,证明数据已经同步给思迅客户端 + */ +@Data +@TableName("product_quantity_consumption") +@ApiModel("商品数量消费表") +public class ProductQuantityConsumption { + @TableId(value = "consume_id", type = IdType.INPUT) + @ApiModelProperty("自定义主键ID") + private String consumeId; + + @TableField(value = "order_id",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("订单编号") + private String orderId; + + @TableField(value = "product_number", updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("商品编号") + private String productNumber; + + @ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)") + @TableField(value = "quantity",updateStrategy = FieldStrategy.NOT_EMPTY) + private BigDecimal quantity; + + @ApiModelProperty("消费状态:0-未消费,1-已消费") + @TableField(value = "status",updateStrategy = FieldStrategy.NOT_EMPTY) + private Integer status; + + @TableField(value = "store_id",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("店铺ID") + private Integer storeId; + + @TableField(value = "update_time",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @TableField(value = "update_time",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} 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 d77a68ae..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 @@ -3303,7 +3303,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(item_src_id, -order_item_quantity); + stockDeltaMap.put(item_src_id+"_"+order_item_row.getOrder_id(), -order_item_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } } @@ -4226,7 +4226,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(Convert.toStr(shopProductItem.getItem_src_id()), order_item_quantity); + stockDeltaMap.put(shopProductItem.getItem_src_id()+"_"+order_item_row.getOrder_id(), order_item_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } } @@ -7244,7 +7244,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(Convert.toStr(item_src_id), -cart_quantity); + stockDeltaMap.put(item_src_id+"_"+order_id, -cart_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java index 86e26bb8..f5716fcc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java @@ -137,6 +137,15 @@ public class SyncThirdDataController { return syncThirdDataService.getStoreDataRelease(appKey,sign); } + @ApiOperation(value = "扣完库存之后做确认", notes = "扣完库存之后做确认") + @RequestMapping(value = "/syncStoreDataReleaseResponse", method = RequestMethod.POST) + public ThirdApiRes syncStoreDataReleaseResponse(@RequestBody List consumIds, + @RequestParam String appKey, + @RequestParam String sign) { + syncThirdDataService.getStoreDataReleaseResponse(appKey,sign,consumIds); + return new ThirdApiRes().success("请求成功!"); + } + @ApiOperation(value = "通知上传商品文件到cos", notes = "通知上传商品文件到cos") @RequestMapping(value = "/uploudToCos", method = RequestMethod.POST) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java index d89a5068..64f746ef 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java @@ -4,7 +4,7 @@ public class RedisKey { //public static final String SXCLIENTKEYVERSION="sxclientKey:version";//客户端版本 - public static final String STOREDATARELEASE="storedata:release"; + public static final String STOREDATARELEASE="shopQuality:release"; public static final String STOREDATAPRODUCTMAPING="storedata:productMaping"; diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java new file mode 100644 index 00000000..bf59ed3a --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java @@ -0,0 +1,11 @@ + + +package com.suisung.mall.shop.sync.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductQuantityConsumptionMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java new file mode 100644 index 00000000..0ba0e209 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java @@ -0,0 +1,8 @@ +package com.suisung.mall.shop.sync.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; + +public interface ProductQuantityConsumptionService extends IService { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java index 878c0a0d..8857053c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java @@ -12,6 +12,7 @@ import cn.hutool.json.JSONArray; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.base.ShopBaseProductBrand; import com.suisung.mall.common.modules.base.ShopBaseProductCategory; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; import org.springframework.core.io.Resource; @@ -110,6 +111,14 @@ public interface SyncThirdDataService { */ ThirdApiRes getStoreDataRelease(String appKey, String sign); + /** + * 扣减完库存之后做确认,确认完成更新队列表为已消费 + * @param appKey + * @param sign + * @return + */ + ThirdApiRes getStoreDataReleaseResponse(String appKey, String sign,List consumIds); + /** * 保存一个或多个商品刚刚变化的库存到 redis hash 缓存 * @@ -122,7 +131,7 @@ public interface SyncThirdDataService { * * @return */ - Map getProductStockFromRedis(String storeId); + List getProductStockFromRedis(String storeId,Map redisHash); /** * 下单或支付后,批量累加减商品库存,使用 Redis Hash 的原子自增操作,保证并发安全 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java new file mode 100644 index 00000000..1b87dc10 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java @@ -0,0 +1,18 @@ +package com.suisung.mall.shop.sync.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; + +import com.suisung.mall.shop.sync.mapper.ProductQuantityConsumptionMapper; +import com.suisung.mall.shop.sync.service.ProductQuantityConsumptionService; +import org.springframework.stereotype.Service; + +import org.springframework.transaction.annotation.Transactional; + + +@Service +@lombok.extern.slf4j.Slf4j +@Transactional +public class ProductQuantityConsumptionServiceImpl extends ServiceImpl implements ProductQuantityConsumptionService { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java index 28d94b10..fa39feb8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java @@ -13,10 +13,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.ZipUtil; +import cn.hutool.core.util.*; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -27,6 +24,7 @@ import com.google.gson.GsonBuilder; import com.qcloud.cos.model.COSObjectSummary; import com.suisung.mall.common.adapter.BigDecimalTypeAdapter; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.api.ResultCode; import com.suisung.mall.common.api.StateCode; import com.suisung.mall.common.enums.DicEnum; import com.suisung.mall.common.exception.ApiException; @@ -42,10 +40,7 @@ import com.suisung.mall.common.modules.sixun.SxSyncGoods; import com.suisung.mall.common.modules.sixun.SxSyncVip; import com.suisung.mall.common.modules.store.ShopStoreActivityBase; import com.suisung.mall.common.modules.store.ShopStoreActivityItem; -import com.suisung.mall.common.modules.sync.StoreDbConfig; -import com.suisung.mall.common.modules.sync.SyncApp; -import com.suisung.mall.common.modules.sync.SyncConfig; -import com.suisung.mall.common.modules.sync.SyncFileLog; +import com.suisung.mall.common.modules.sync.*; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; import com.suisung.mall.common.utils.I18nUtil; @@ -77,6 +72,7 @@ import com.suisung.mall.shop.sync.dto.BrandModel; import com.suisung.mall.shop.sync.keymanage.RedisKey; import com.suisung.mall.shop.sync.service.*; import io.seata.spring.annotation.GlobalTransactional; +import org.apache.commons.collections4.ListUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -180,10 +176,12 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements @Autowired private ProductMappingService productMappingService; - @Autowired private ShopProductIndexService shopProductIndexService; + @Autowired + private ProductQuantityConsumptionService productQuantityConsumptionService; + /** * 批量保存商品的分类 * @@ -780,18 +778,91 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements if (syncAppO == null) { return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); } + //持久化保存数据到数据库队列 + String key=RedisKey.STOREDATARELEASE+":"+syncAppO.getStore_id(); + Map redisHash = redisTemplate.opsForHash().entries(key); + List productQuantityConsumptionList=getProductStockFromRedis(syncAppO.getStore_id(),redisHash); + List productQuantityConsumptions=new ArrayList<>(); + if (!productQuantityConsumptionList.isEmpty()) { + boolean result= productQuantityConsumptionService.saveBatch(productQuantityConsumptionList,productQuantityConsumptionList.size()); + if(result){ + Set fields = redisHash.keySet(); + redisTemplate.opsForHash().delete(key, fields.toArray());//清除持久化数据 + } + } + //查询未消费数据 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", syncAppO.getStore_id()); + queryWrapper.eq("status",0);//未消费数据 + productQuantityConsumptions=productQuantityConsumptionService.list(queryWrapper); + return new ThirdApiRes().success("success", productQuantityConsumptions); + } -// Object obRst = redisService.get(RedisKey.STOREDATARELEASE);//商品库存扣减 -// Map storeDataResultMap = new HashMap(); -// if (obRst != null) { -// Map resultMap = (Map) obRst; -// Set sme = resultMap.entrySet(); -// storeDataResultMap = sme.stream().filter(m -> !(m.getValue().equals((double) 0))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); -// } + @Override + public ThirdApiRes getStoreDataReleaseResponse(String appKey, String sign,List consumIds) { + if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign)) { + logger.info("缺少必要参数!"); + } + Gson gson = new Gson(); + String jsonStr = gson.toJson(consumIds); + SyncApp syncApp = syncAppService.checkAppSign(appKey, sign, jsonStr); // 验签、appid,必要参数判断 + if (syncApp == null) { + logger.info("签名有误!"); + return new ThirdApiRes().fail(250,"签名有误"); + } + if(consumIds!=null){ + ExecutorService executor = Executors.newFixedThreadPool(6); + List> futures = new ArrayList<>(); + Integer pages=CommonUtil.getPagesCount(consumIds.size(),limitCnt); + List> pagesPatition = ListUtils.partition(consumIds, limitCnt); + AtomicInteger success = new AtomicInteger(); + AtomicInteger fails = new AtomicInteger(); + for (int i = 1; i <=pages ; i++) { + int finalI = i; + int finalI1 = i; + futures.add(executor.submit(() -> { + try { + List pageData = pagesPatition.get(finalI - 1); + productQuantityConsumptionPageSave(pageData); + success.getAndIncrement(); + return "成功执行"+ finalI1; + }catch (Exception e){ + fails.getAndIncrement(); + return "执行失败:"+finalI+":"+e.getMessage(); + } + })); + } + // 等待所有任务完成 + for (Future future : futures) { + try { + logger.info("任务结果: {}", future.get()); + } catch (Exception e) { + logger.error("任务执行异常: {}", e.getMessage()); + } + } + executor.shutdown(); + logger.info("总任务数据:{}", pagesPatition.size()); + logger.info("成功数据:{}", success.get()); + logger.info("失败数据:{}", fails.get()); + } + return new ThirdApiRes().success("success"); + } - Map storeDataResultMap = getProductStockFromRedis(syncAppO.getStore_id()); - - return new ThirdApiRes().success("success", storeDataResultMap); + /** + * 更新消费状态 + * @param consumIds + */ + private void productQuantityConsumptionPageSave(List consumIds){ + List productQuantityConsumptionList=new ArrayList<>(); + for (String consumeId : consumIds) { + ProductQuantityConsumption productQuantityConsumption=new ProductQuantityConsumption(); + productQuantityConsumption.setConsumeId(consumeId); + productQuantityConsumption.setStatus(1); + productQuantityConsumptionList.add(productQuantityConsumption); + } + if(!productQuantityConsumptionList.isEmpty()){ + productQuantityConsumptionService.updateBatchById(productQuantityConsumptionList,productQuantityConsumptionList.size()); + } } // @Override @@ -813,23 +884,34 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements @Override - public Map getProductStockFromRedis(String storeId) { + public List getProductStockFromRedis(String storeId,Map redisHash) { try { // 从 Redis 获取 hash 结构的所有键值对 - String key=RedisKey.STOREDATARELEASE+":"+storeId; - Map redisHash = redisTemplate.opsForHash().entries(key); if (redisHash.isEmpty()) { - return Collections.emptyMap(); + return Collections.emptyList(); } - // 转换为 Map - return redisHash.entrySet().stream() - .collect(Collectors.toMap( - entry -> String.valueOf(entry.getKey()), - entry -> Convert.toDouble(entry.getValue(), 0.00) // 转换失败时默认为 0 - )); + // 转换为 list + List productQuantityConsumptionList=new ArrayList<>(); + redisHash.forEach((k, v)->{ + ProductQuantityConsumption productQuantityConsumption=new ProductQuantityConsumption(); + String[] productKeyArrys=k.split("_"); + if(productKeyArrys.length!=2){ + return; + } + String productNumber=productKeyArrys[0]; + String orderId=productKeyArrys[1]; + productQuantityConsumption.setConsumeId(IdUtil.getSnowflakeNextIdStr()); + productQuantityConsumption.setOrderId(orderId); + productQuantityConsumption.setProductNumber(productNumber); + productQuantityConsumption.setQuantity(Convert.toBigDecimal(v)); + productQuantityConsumption.setStoreId(Integer.valueOf(storeId)); + productQuantityConsumption.setStatus(0); + productQuantityConsumptionList.add(productQuantityConsumption); + }); + return productQuantityConsumptionList; } catch (Exception e) { logger.error("从 Redis 获取商品库存失败: {}", e.getMessage(), e); - return Collections.emptyMap(); + return Collections.emptyList(); } } @@ -847,9 +929,15 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements if (StrUtil.isBlank(productKey) || delta == null) { continue; } + String[] productKeyArrys=productKey.split("_"); + if(productKeyArrys.length!=2){ + continue; + } try { + String item_src_id=productKeyArrys[0]; + String orderId=productKeyArrys[1]; QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("item_src_id", productKey); + queryWrapper.eq("item_src_id", item_src_id); List shopProductItems=shopProductItemService.list(queryWrapper); if(shopProductItems==null || shopProductItems.isEmpty()){ continue; @@ -871,7 +959,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements String name = Convert.toStr(item.get("name")); BigDecimal itemQuaryty = getBigDecimal(delta, name); // 使用 Redis 的 HINCRBY 保证原子性和高性能 - redisTemplate.opsForHash().increment(key, itemId, itemQuaryty.doubleValue()); + redisTemplate.opsForHash().increment(key, itemId+"_"+orderId, itemQuaryty.doubleValue()); } catch (Exception e) { logger.error("库存累计失败,productKey={}, delta={}, error={}", productKey, delta, e.getMessage(), e); } diff --git a/sql/shop/dev/20250929_ddl.sql b/sql/shop/dev/20250929_ddl.sql new file mode 100644 index 00000000..5ec58add --- /dev/null +++ b/sql/shop/dev/20250929_ddl.sql @@ -0,0 +1,12 @@ +CREATE TABLE `product_quantity_consumption` ( + `consume_id` varchar(32) NOT NULL COMMENT '自定义主键ID', + `product_number` varchar(50) NOT NULL COMMENT '商品编号', + `order_id` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '' COMMENT '订单编号', + `quantity` decimal(15,3) NOT NULL COMMENT '数量(正数表示入库/增加,负数表示出库/减少)', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '消费状态:0-未消费,1-已消费', + `store_id` int(20) NOT NULL COMMENT '店铺ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`consume_id`), + KEY `idx_shop_product` (`store_id`, `product_number`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品数量消费表'; From 3e7c7f8cd9b27ca22b52ffef6c0165c06e51f5ab Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 11 Oct 2025 14:23:57 +0800 Subject: [PATCH 22/22] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=AD=E9=97=B4=E8=A1=A8=E7=9A=84=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=EF=BC=8C=E4=BF=9D=E8=AF=81=E5=BA=93=E5=AD=98=E7=9A=84?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/small/client/dao/SxDataDao.java | 46 ++++++++++++++++--- .../dto/ProductQuantityConsumptionDto.java | 42 +++++++++++++++++ .../client/service/imp/SxDataServiceImp.java | 19 +++++++- 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java diff --git a/client/src/main/java/com/small/client/dao/SxDataDao.java b/client/src/main/java/com/small/client/dao/SxDataDao.java index 88fb3f51..5329c644 100644 --- a/client/src/main/java/com/small/client/dao/SxDataDao.java +++ b/client/src/main/java/com/small/client/dao/SxDataDao.java @@ -3,16 +3,25 @@ package com.small.client.dao; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.google.gson.Gson; import com.small.client.Utils.BigDecimalFormatter; +import com.small.client.Utils.CommonUtil; +import com.small.client.Utils.HttpUtils; import com.small.client.dto.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.*; import java.util.*; +import java.util.stream.Collectors; /** * 考虑到每个思迅软件都是自己的数据,所以采用动态获取的方式获取数据 @@ -22,7 +31,10 @@ import java.util.*; @Service @Slf4j public class SxDataDao extends BaseDao{ - + @Autowired + private RestTemplate restTemplate; + @Value("${remoteIp}") + private String remoteIp; private final static String T_BD_ITEM_CLS="t_bd_item_cls";//商品分类 private final static String T_BD_ITEM_INFO="t_bd_item_info b";//商品表 private final static String T_RM_VIP_INFO="t_rm_vip_info";//会员表 @@ -458,7 +470,8 @@ public class SxDataDao extends BaseDao{ * @param dataBaseInfo * @param map */ - public void updateStoreData(DataBaseInfo dataBaseInfo, Map map){ + public void updateStoreData(DataBaseInfo dataBaseInfo, Map map,List productQuantityConsumptionDtoList, + CommentModel commentModel){ if(CollectionUtil.isEmpty(map)){ log.info("同步数据为空"); return; @@ -482,8 +495,12 @@ public class SxDataDao extends BaseDao{ int count = 0; Set sme=map.entrySet(); for (Map.Entry entry : sme) { + BigDecimal stock_qty= (BigDecimal) entry.getValue(); + if(stock_qty.compareTo(BigDecimal.ZERO)==0){ + continue; + } ps.setString(1, (String) entry.getKey()); - ps.setDouble(2, (double) entry.getValue()); + ps.setDouble(2, stock_qty.doubleValue()); ps.setTimestamp(3, timestamp); ps.addBatch(); // 添加至批处理 count++; @@ -495,22 +512,37 @@ public class SxDataDao extends BaseDao{ } // 执行剩余未满 batchSize 的批次 int[] remainingCounts = ps.executeBatch(); + + List consumIds=productQuantityConsumptionDtoList + .stream() + .map(ProductQuantityConsumptionDto::getConsumeId) + .collect(Collectors.toList()); + Gson gson=new Gson(); + String jsonString=gson.toJson(consumIds); + JSONArray jsonArray = JSONUtil.parseArray(jsonString); + String sign= CommonUtil.generateOpenSign(jsonString,commentModel.getAppKey(),commentModel.getAppId()); + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_POST_STORE_DATA_RESPONSE + +"?appKey="+commentModel.getAppKey() + +"&sign="+sign, jsonArray); + if (!HttpUtils.SUCCESSCODE.equals(code)) { + throw new Exception("服务器异常"); + } log.info("剩余批次更新数: {}", Arrays.toString(remainingCounts)); conn.commit(); // 最终提交事务 log.info("批量更新完成,总记录数: {}" , count); //baseUpdateImBrancStock(dataBaseInfo); - } catch (SQLException e) { + } catch (Exception e) { conn.rollback(); // 出错时回滚整个事务 - e.printStackTrace(); + log.info("业务失败:: {}", e.getMessage()); } } catch (SQLException e) { - e.printStackTrace(); + log.info("sql失败:: {}", e.getMessage()); }finally { if(conn!=null){ try { conn.close(); } catch (SQLException e) { - throw new RuntimeException(e); + log.info("最后sql失败:: {}", e.getMessage()); } } } diff --git a/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java b/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java new file mode 100644 index 00000000..70cb361e --- /dev/null +++ b/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java @@ -0,0 +1,42 @@ +package com.small.client.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 同步消费中间表,同步数据时保存数据到这个表中,消费完更新已消费,证明数据已经同步给思迅客户端 + */ +@Data +@ApiModel("商品数量消费表") +public class ProductQuantityConsumptionDto { + @ApiModelProperty("自定义主键ID") + private String consumeId; + + @ApiModelProperty("订单编号") + private String orderId; + + @ApiModelProperty("商品编号") + private String productNumber; + + @ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)") + private BigDecimal quantity; + + @ApiModelProperty("消费状态:0-未消费,1-已消费") + private Integer status; + + @ApiModelProperty("店铺ID") + private Integer storeId; + + @ApiModelProperty("创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java index ebd3a212..afb3e193 100644 --- a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java +++ b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java @@ -218,6 +218,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_MEMBER +"?appKey="+commentModel.getAppKey() +"&sign="+sign,memberList);//todo 后期改为文件传输 + if (!HttpUtils.SUCCESSCODE.equals(code)) { continue; } @@ -739,8 +740,22 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService +"?appKey="+commentModel.getAppKey() +"&sign="+commentModel.getAppId(),JSONObject.class); if(null!=jsonObject.get("result")){ - Map map=(Map)jsonObject.get("result"); - sxDataDao.updateStoreData(dataBaseInfo,map); + // Map map=(Map)jsonObject.get("result"); + List productQuantityConsumptionDtoList = + JSONUtil.toList(jsonObject.getStr("result"),ProductQuantityConsumptionDto.class); + if(!productQuantityConsumptionDtoList.isEmpty()){ + Map map = productQuantityConsumptionDtoList.stream() + .collect(Collectors.groupingBy( + ProductQuantityConsumptionDto::getProductNumber, + Collectors.reducing( + BigDecimal.ZERO, + ProductQuantityConsumptionDto::getQuantity, + BigDecimal::add + ) + )); + sxDataDao.updateStoreData(dataBaseInfo,map,productQuantityConsumptionDtoList,commentModel); + } + } }