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); } + } } }