From 6dda9f6f97d68e60e4187bf2bb047d8f1869e957 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Wed, 28 May 2025 20:35:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=97=E9=93=BA=E5=88=86=E7=B1=BB=20sql=20fi?= =?UTF-8?q?x=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/PayUserPayServiceImpl.java | 6 + .../service/impl/LakalaApiServiceImpl.java | 162 +------------ .../order/listener/OrderPayedListener.java | 228 +++++++++++------- .../base/ShopBaseStoreCategoryMapper.xml | 4 +- 4 files changed, 157 insertions(+), 243 deletions(-) diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java index 64bdd1f7..2aada876 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java @@ -1278,6 +1278,10 @@ public class PayUserPayServiceImpl extends BaseServiceImpl innerDoOrderSeparateTemp(String orderId) { - if (StrUtil.isBlank(orderId)) { - return Pair.of(false, "订单号不能为空"); - } - - try { - - // 1. 配置初始化 - initLKLSDK(); - - // 获取订单信息、店铺信息,商户信息、分账信息 - List shopOrderLklList = shopOrderLklService.selectByOrderId(orderId, ""); - if (CollectionUtil.isEmpty(shopOrderLklList)) { - return Pair.of(false, "订单不存在"); - } - - int successCnt = 0; - // 一个订单里包含了多个商家的时候,需要处理 - for (ShopOrderLkl shopOrderLkl : shopOrderLklList) { - String merchantNo = shopOrderLkl.getMerchant_no(); - - // 分账总金额 - Integer paymentAmount = shopOrderLkl.getTotal_amt(); - Integer shoppingFee = shopOrderLkl.getShopping_fee(); - BigDecimal splitRatioMch = shopOrderLkl.getSplit_ratio(); - // 应付款金额大于零,并且大于运费。 - if (paymentAmount <= 0 - || (shoppingFee != null && paymentAmount - shoppingFee <= 0)) { - log.error("店铺{}订单{}金额有误或运费高于分账金额,不会进行分账", shopOrderLkl.getStore_id(), orderId); - continue; - } - - // 平台和代理商的分成比例 - BigDecimal splitRatioNoMch = new BigDecimal("100").subtract(shopOrderLkl.getSplit_ratio()); - LklLedgerMerReceiverBind platform = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo); - List distributorList = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo); - if (platform == null) { - log.error("店铺{}订单{}未绑定平台方,不会进行分账", shopOrderLkl.getStore_id(), orderId); - continue; - } - - boolean canSplit = true; // 能分账并扣运费 - if (splitRatioMch == null || splitRatioMch.compareTo(new BigDecimal("100")) >= 0) { - log.warn("店铺{}分账比例为0,只扣运费,不会进行分账", shopOrderLkl.getStore_id()); - canSplit = false; //不分账,只扣运费 - } - - V3SacsSeparateRequest req = new V3SacsSeparateRequest(); - req.setMerchantNo(merchantNo); // 拉卡拉商户号 - req.setLogNo(shopOrderLkl.getLog_no()); - req.setLogDate(shopOrderLkl.getLog_date()); - req.setOutSeparateNo(orderId); // 暂时传入平台订单Id - req.setTotalAmt(shopOrderLkl.getTotal_amt().toString()); - req.setLklOrgNo(orgCode); - req.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 - req.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify"); - - List recvDatas = new ArrayList<>(); - - // 平台先代扣掉运费 - if (shoppingFee != null && shoppingFee > 0) { - V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platform.getReceiver_no()); - recvData.setSeparateValue(shoppingFee.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData); - } - - if (canSplit) { //扣完运费后还能能分账 - - // 应付款扣除运费后,得出实际分账的金额,单位:分 - Integer splitAmount = paymentAmount - shoppingFee; - if (!CollectionUtils.isEmpty(distributorList) - && (splitRatioMch != null && splitRatioNoMch.compareTo(BigDecimal.ONE) > 0)) { // 有平台和代理商,并且分账比例大于1的情况 - - // 平台方分账 - BigDecimal platformSeparateValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(new BigDecimal("0.01"))); // 平台收取1%手续费 - if (platformSeparateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platform.getReceiver_no()); - recvData.setSeparateValue(platformSeparateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData); - } - - // TODO: 一级以上的代理商以后做处理 - // 一级代理方分账, 分账比例扣掉1%的平台方费,其余都是代理商的 - BigDecimal splitRatioDistributor = splitRatioNoMch.subtract(new BigDecimal("1")).divide(new BigDecimal(100)); - BigDecimal distributorSeparateValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(splitRatioDistributor)); - if (distributorSeparateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData2 = new V3SacsSeparateRecvDatas(); - recvData2.setRecvNo(distributorList.get(0).getReceiver_no()); - recvData2.setSeparateValue(distributorSeparateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData2); - } - } else { - // 仅平台方一方分账 - BigDecimal splitRatioPlatform = CommonUtil.DecimalRoundHalfDown(splitRatioNoMch.divide(new BigDecimal(100))); - BigDecimal separateValue = new BigDecimal(splitAmount).multiply(splitRatioPlatform); - if (separateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platform.getReceiver_no()); - recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData); - } - } - } - - req.setRecvDatas(recvDatas); - - - log.debug("分账执行请求参数:{}", JSONUtil.toJsonStr(req)); - - //3. 发送请求 - String responseStr = LKLSDK.httpPost(req); - if (StrUtil.isBlank(responseStr)) { - log.error("服务器无返回值!"); - continue; - } - log.debug("分账执行响应参数:{}", responseStr); - - JSONObject lklRespJSON = JSONUtil.parseObj(responseStr); - if (lklRespJSON == null - || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) - || lklRespJSON.getJSONObject("resp_data") == null) { - log.error("分账返回值有误!"); - continue; - } - - JSONObject respData = lklRespJSON.getJSONObject("resp_data"); - - // 4. 处理返回结果,等待异步通知 - LklOrderSeparate record = new LklOrderSeparate(); - record.setSeparate_no(respData.getStr("separate_no")); // 必填参数 - record.setOut_separate_no(req.getOutSeparateNo());// 必填参数 - record.setMerchant_no(merchantNo); - record.setLog_no(shopOrderLkl.getLog_no()); - record.setLog_date(shopOrderLkl.getLog_date()); - record.setOrder_id(shopOrderLkl.getOrder_id()); - record.setTotal_amt(shopOrderLkl.getTotal_amt().toString()); - record.setNotify_url(req.getNotifyUrl()); - record.setLkl_org_no(req.getLklOrgNo()); - record.setRecv_datas(JSONUtil.toJsonStr(req.getRecvDatas())); - record.setStatus(respData.getStr("status")); - lklOrderSeparateService.addOrUpdateByReceiverNo(record); - - successCnt++; - } - - if (successCnt <= 0) { - return Pair.of(false, "分账失败"); - } else if (successCnt != shopOrderLklList.size()) { - return Pair.of(true, "部分订单分账执行成功,处理中"); - } - - return Pair.of(true, "全部订单分账执行成功,处理中"); - } catch (SDKException e) { - log.error("分账发生错误:", e); - return Pair.of(false, "分账发生错误"); - } - } - /** * 分账结果通知 * 参考:https://o.lakala.com/#/home/document/detail?id=393 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 0d474424..eb378b64 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 @@ -18,13 +18,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; -import javax.annotation.Resource; import java.io.IOException; import java.util.Collections; import java.util.List; -/*** - * 支付成功后,微信和支付宝发出异步通知消息到 mq,mq监听到消息执行更改订单状态:已支付 +/** + * 支付成功后,微信和支付宝发出异步通知消息到 RabbitMQ, + * 这里 MQ 监听到消息后执行更改订单状态为:已支付。 */ @Service @Slf4j @@ -32,103 +32,169 @@ import java.util.List; public class OrderPayedListener { private static final Logger logger = LoggerFactory.getLogger(OrderPayedListener.class); + @Autowired private ShopOrderBaseService shopOrderBaseService; + @Autowired private ShopOrderInfoService shopOrderInfoService; - // @Autowired -// private ShopStorePrinterService shopStorePrinterService; -// @Autowired -// private MqMessageService mqMessageService; + @Autowired private SFExpressApiService sfExpressApiService; + /** + * RabbitMQ 消息监听入口方法 + * + * @param data 消息体,包含订单 ID 列表 + * @param channel 当前 Channel + * @param message 消息对象 + * @throws IOException IO 异常 + */ @RabbitHandler - public void listener(String data, Channel channel, Message message) throws IOException, InterruptedException { - String messageId = message.getMessageProperties().getMessageId(); + public void listener(String data, Channel channel, Message message) throws IOException { + List orderIdList = Convert.toList(String.class, data); + boolean flag = false; - - List order_id_row = Convert.toList(String.class, data); try { - boolean flag = false; - logger.info("收到微信异步通知消息data:{}-chanel:{}-message:{},订单ID:{}", data, channel, message, order_id_row); + // 1. 记录接收到的消息内容 + logger.info("收到微信异步通知消息data:{}-channel:{}-message:{},订单ID:{}", data, channel, message, orderIdList); - for (String orderId : order_id_row) { - //判断是否为线下支付订单 - ShopOrderInfo orderInfoOld = shopOrderInfoService.get(orderId); - if (orderInfoOld == null) { - continue; - } - - // 订单状态 - int order_state_id = orderInfoOld.getOrder_state_id().intValue(); - logger.info("#### 当前订单状态: {} ####", order_state_id); - - if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY) { - // 待支付状态 - logger.info("#### 待支付业务分支 ####"); - flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId)); - } else { - //判断是否线下支付 - if (StateCode.PAYMENT_TYPE_OFFLINE == orderInfoOld.getPayment_type_id().intValue()) { - //线下支付,直接处理订单支付状态, 不处理订单状态 - logger.info("#### 线下业务分支 ####"); - ShopOrderInfo orderInfo = new ShopOrderInfo(); - orderInfo.setOrder_id(orderId); - orderInfo.setOrder_is_paid(StateCode.ORDER_PAID_STATE_YES); - flag = shopOrderInfoService.edit(orderInfo); - } else { - logger.info("#### 非线下业务分支 ####"); - flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId)); - } - } - - logger.info("#### 支付异步通知回调处理是否成功:{} ####", flag); - // 生成取单号和打印小票 - if (flag) { - // TODO 以下仅处理下单打印的情况,还需要处理退单打印分支。 - - // 原始状态 2010-待付款;2011--待订单审核;2013-待财务审核 变成 2020-待配货;2016-已经付款 的时候 - // 生成取单号,打票机打印订单,向顺丰同城下单 - if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY - || order_state_id == StateCode.ORDER_STATE_WAIT_REVIEW - || order_state_id == StateCode.ORDER_STATE_WAIT_FINANCE_REVIEW) { - // 已支付的订单,生成取单号,打票机并打印订单 - Long orderPickupNum = shopOrderInfoService.isPaidOrderGenPickNumAndPrint(orderInfoOld.getStore_id(), orderId); - - // 如果配送方式是 顺丰同城下单 - if (orderInfoOld.getDelivery_type_id() != null && orderInfoOld.getDelivery_type_id().equals(StateCode.DELIVERY_TYPE_SAME_CITY) - && orderPickupNum > 0) { - // 发送顺丰同城快递 - Pair pairCreateSfOrder = sfExpressApiService.innerCreateSfExpressOrder(orderId, orderPickupNum); - if (pairCreateSfOrder == null) { - logger.error("顺丰同城下单失败!pairCreateSfOrder 返回空值"); - return; - } - - if (!pairCreateSfOrder.getFirst()) { - logger.error("顺丰同城下单失败:{}", pairCreateSfOrder.getSecond()); - return; - } - - logger.info("顺丰同城下单成功"); - } - } - } - } + // 2. 执行订单状态更新逻辑(支持批量处理) + flag = updateOrderStatusBatch(orderIdList); if (flag) { - // 操作成功,标记消息消费成功 + // 3. 若订单更新成功,执行后续操作(取单号生成、打票机打印、顺丰下单等) + handlePostPaymentActions(orderIdList); + } + + // 4. 根据执行结果确认或拒绝消息 + if (flag) { channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } else { - log.error("消息消费失败,执行setPaidYes异常,当前订单编号:{}", order_id_row); + log.warn("订单状态更新失败,拒绝消息:{}", orderIdList); channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); - Thread.sleep(1000); } + } catch (Exception e) { - log.error("消息消费失败,执行setPaidYes异常,当前订单编号:{},失败原因:", order_id_row, e); + // 5. 捕获异常并记录日志 + log.error("消费消息失败:{},失败原因:", orderIdList, e); + + // 6. 拒绝消息并重新入队 channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); - Thread.sleep(1000); + } + } + + /** + * 批量更新订单状态为已支付 + * + * @param orderIdList 订单 ID 列表 + * @return 是否全部更新成功 + */ + private boolean updateOrderStatusBatch(List orderIdList) { + if (orderIdList == null || orderIdList.isEmpty()) { + return false; + } + + boolean flag = false; + + for (String orderId : orderIdList) { + ShopOrderInfo orderInfoOld = shopOrderInfoService.get(orderId); + if (orderInfoOld == null) { + continue; + } + + int order_state_id = orderInfoOld.getOrder_state_id().intValue(); + logger.debug("#### 当前订单状态: {} ####", order_state_id); + + // 如果是待支付状态,则调用 setPaidYes + if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY) { + logger.debug("#### 待支付业务分支 ####"); + flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId)); + } + // 非待支付状态,判断是否为线下支付 + else if (StateCode.PAYMENT_TYPE_OFFLINE == orderInfoOld.getPayment_type_id().intValue()) { + logger.debug("#### 线下业务分支 ####"); + ShopOrderInfo orderInfo = new ShopOrderInfo(); + orderInfo.setOrder_id(orderId); + orderInfo.setOrder_is_paid(StateCode.ORDER_PAID_STATE_YES); + flag = shopOrderInfoService.edit(orderInfo); + } + // 其他情况也调用 setPaidYes + else { + logger.debug("#### 非线下业务分支 ####"); + flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId)); + } + + logger.info("#### 支付异步通知回调处理是否成功:{} ####", flag); + + if (!flag) { + break; // 任意一个失败则中断处理 + } + } + + return flag; + } + + + /** + * 处理支付完成后需要执行的操作: + * - 生成取单号 + * - 打印小票 + * - 顺丰同城下单等 + * + * @param orderIdList 订单 ID 列表 + */ + /** + * 处理支付完成后需要执行的操作: + * - 生成取单号 + * - 打印小票 + * - 顺丰同城下单等 + * + * @param orderIdList 订单 ID 列表 + */ + private void handlePostPaymentActions(List orderIdList) { + if (orderIdList == null || orderIdList.isEmpty()) { + return; + } + + for (String orderId : orderIdList) { + ShopOrderInfo orderInfoOld = shopOrderInfoService.get(orderId); + if (orderInfoOld == null) { + continue; + } + + int order_state_id = orderInfoOld.getOrder_state_id().intValue(); + + // 只处理特定状态的订单:待支付、待审核、待财务审核 + if (!(order_state_id == StateCode.ORDER_STATE_WAIT_PAY || + order_state_id == StateCode.ORDER_STATE_WAIT_REVIEW || + order_state_id == StateCode.ORDER_STATE_WAIT_FINANCE_REVIEW)) { + continue; + } + + Long orderPickupNum = shopOrderInfoService.isPaidOrderGenPickNumAndPrint(orderInfoOld.getStore_id(), orderId); + if (orderPickupNum == null || orderPickupNum <= 0) { + continue; + } + + // 判断是否为“顺丰同城”配送方式 + if (orderInfoOld.getDelivery_type_id() == null || + !orderInfoOld.getDelivery_type_id().equals(StateCode.DELIVERY_TYPE_SAME_CITY)) { + continue; + } + + Pair pairCreateSfOrder = sfExpressApiService.innerCreateSfExpressOrder(orderId, orderPickupNum); + if (pairCreateSfOrder == null) { + logger.error("顺丰同城下单失败!pairCreateSfOrder 返回空值"); + continue; + } + + if (!pairCreateSfOrder.getFirst()) { + logger.error("顺丰同城下单失败:{}", pairCreateSfOrder.getSecond()); + continue; + } + + logger.debug("顺丰同城下单成功"); } } diff --git a/mall-shop/src/main/resources/mapper/base/ShopBaseStoreCategoryMapper.xml b/mall-shop/src/main/resources/mapper/base/ShopBaseStoreCategoryMapper.xml index 59b4d2d1..4e1ab329 100644 --- a/mall-shop/src/main/resources/mapper/base/ShopBaseStoreCategoryMapper.xml +++ b/mall-shop/src/main/resources/mapper/base/ShopBaseStoreCategoryMapper.xml @@ -45,8 +45,8 @@ c.category_image AS child_category_image FROM shop_base_store_category p LEFT JOIN - shop_base_store_category c ON p.store_category_parent_id = c.store_category_id and c.category_is_enable = 1 - WHERE p.store_category_parent_id = 0 and p.category_is_enable = 1 + shop_base_store_category c ON c.store_category_parent_id = p.store_category_id and c.category_is_enable = 1 + WHERE p.category_is_enable = 1 and p.store_category_parent_id = 0 AND (p.store_category_name LIKE CONCAT('%', #{keyword}, '%') OR p.description LIKE CONCAT('%', #{keyword}, '%')