From 7c8120b43fedfc43c14e669edb7d267055dc3764 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 23 Oct 2025 10:36:43 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E9=A2=84=E8=AE=A2=E5=8D=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=A1=A8=20=E6=96=B9=E6=B3=95=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ShopOrderBookingService.java | 25 ++++++- .../impl/ShopOrderBaseServiceImpl.java | 2 - .../impl/ShopOrderBookingServiceImpl.java | 67 ++++++++++++++++++- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java index a34aa61b..39fdd889 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java @@ -8,10 +8,33 @@ package com.suisung.mall.shop.order.service; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.modules.order.ShopOrderBooking; import com.suisung.mall.core.web.service.IBaseService; public interface ShopOrderBookingService extends IBaseService { + /** + * 添加或更新预约订单任务 + * @param shopOrderBooking 预约订单任务信息 + * @return 操作后的预约订单任务实例 + */ + ShopOrderBooking addOrUpdate(ShopOrderBooking shopOrderBooking); -} + /** + * 查询有效的预约订单任务分页列表(按预约时间升序排序) + * @param pageNum 页码 + * @param pageSize 页大小 + * @return 分页结果 + */ + Page findValidBookingList(Integer pageNum, Integer pageSize); + + /** + * 更新预约订单任务 + * @param updateWrapper 更新条件包装器 + * @return 是否更新成功 + */ + boolean update(UpdateWrapper updateWrapper); + +} \ No newline at end of file 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 8af35887..5b31bc61 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 @@ -6644,9 +6644,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl implements ShopOrderBookingService { -} + @Override + public ShopOrderBooking addOrUpdate(ShopOrderBooking shopOrderBooking) { + log.debug("添加或更新预约订单任务: {}", shopOrderBooking); + if (shopOrderBooking == null) { + log.warn("预约订单任务信息为空"); + return null; + } + + if (shopOrderBooking.getId() != null && shopOrderBooking.getId() > 0) { + // 更新操作 + log.debug("执行更新操作,ID: {}", shopOrderBooking.getId()); + this.updateById(shopOrderBooking); + return shopOrderBooking; + } else { + // 添加操作 + log.debug("执行添加操作"); + boolean saved = this.save(shopOrderBooking); + if (saved) { + return shopOrderBooking; + } else { + log.error("保存预约订单任务失败: {}", shopOrderBooking); + return null; + } + } + } + + @Override + public Page findValidBookingList(Integer pageNum, Integer pageSize) { + log.debug("查询有效的预约订单任务分页列表,页码: {},页大小: {}", pageNum, pageSize); + if (pageNum == null || pageNum <= 0) { + log.warn("页码无效,使用默认值 1"); + pageNum = 1; + } + if (pageSize == null || pageSize <= 0) { + log.warn("页大小无效,使用默认值 10"); + pageSize = 10; + } + + Page page = new Page<>(pageNum, pageSize); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("status", CommonConstant.Enable); + queryWrapper.orderByAsc("booking_at"); + return this.page(page, queryWrapper); + } + + @Override + public boolean update(UpdateWrapper updateWrapper) { + log.debug("更新预约订单任务"); + if (updateWrapper == null) { + log.warn("更新条件不能为空"); + return false; + } + + try { + return this.update(updateWrapper); + } catch (Exception e) { + log.error("更新预约订单任务时发生异常", e); + return false; + } + } + +} \ No newline at end of file From 3ce2b7cbd6608526ad8516f2968fe0222e43d272 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 23 Oct 2025 14:27:53 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E5=95=86=E5=AE=B6=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E7=A1=AE=E8=AE=A4=E5=90=8E=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/sfexpress/service/impl/SFExpressApiServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 03591074..1b439657 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 @@ -1291,10 +1291,11 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { return CommonResult.failed("该状态不允许完成订单操作"); } // 送达后,发出催促微信用户确认收货通知 (同城配送不能调用微信的确认收货) - wxOrderShippingService.notifyConfirmReceive(shopOrderBase.getOrder_id()); shopOrderInfoService.changeOrderStatus(shopOrderId, orderStatus, 0, 0); //完成订单后修改订单异常操作流程 - shopOrderBase.setOperate_flag("2"); + ShopOrderBase updateShopOrderBase=new ShopOrderBase(); + updateShopOrderBase.setOrder_id(shopOrderId); + updateShopOrderBase.setOperate_flag("2"); shopOrderBaseService.updateById(shopOrderBase); return CommonResult.success("操作成功"); } From d886bb51abbb500de68f7b643de3d0183e3d14f3 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 23 Oct 2025 14:58:11 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A1=BA=E4=B8=B0?= =?UTF-8?q?=E5=90=8C=E5=9F=8E=E5=BA=93=E5=AD=98=E6=89=A3=E5=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/SFExpressApiServiceImpl.java | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) 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 1b439657..759c60b1 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 @@ -24,6 +24,7 @@ import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.modules.order.ShopOrderBase; import com.suisung.mall.common.modules.order.ShopOrderItem; +import com.suisung.mall.common.modules.product.ShopProductItem; import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.modules.store.ShopStoreSameCityTransportBase; import com.suisung.mall.common.modules.store.ShopStoreSfOrder; @@ -36,6 +37,7 @@ import com.suisung.mall.shop.order.service.ShopOrderBaseService; import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.order.service.ShopOrderItemService; import com.suisung.mall.shop.order.service.ShopOrderReturnService; +import com.suisung.mall.shop.product.service.ShopProductItemService; import com.suisung.mall.shop.sfexpress.service.SFExpressApiService; import com.suisung.mall.shop.store.service.ShopMchEntryService; import com.suisung.mall.shop.store.service.ShopStoreSameCityTransportBaseService; @@ -57,6 +59,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.suisung.mall.common.utils.I18nUtil._; + @Service public class SFExpressApiServiceImpl implements SFExpressApiService { private static final Logger logger = LoggerFactory.getLogger(SFExpressApiServiceImpl.class); @@ -112,6 +116,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { @Autowired private ShopOrderItemService shopOrderItemService; + @Autowired + private ShopProductItemService shopProductItemService; + /** * 创建顺丰同店铺-连锁店铺 * @@ -713,7 +720,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { shopStoreSfOrder.setStatus_desc("线上商城发起取消订单"); Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder); if (!success) { - throw new ApiException(I18nUtil._("取消顺丰订单失败!")); + throw new ApiException(_("取消顺丰订单失败!")); } @@ -1067,7 +1074,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder); if (!success) { - throw new ApiException(I18nUtil._("状态处理失败!")); + throw new ApiException(_("状态处理失败!")); } // 更改商城订单状态为:注意事务问题 @@ -1101,6 +1108,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { String order_id = shopStoreSfOrder.getShop_order_id(); itemQueryWrapper.eq("order_id", order_id); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); + if(picking(order_item_rows)){ + logger.info("顺丰发货商品扣减库存成功"); + } if (!order_item_rows.isEmpty()) { for (ShopOrderItem shopOrderItem : order_item_rows) { Map stockDeltaMap = new HashMap<>(); @@ -1212,7 +1222,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder); if (!success) { - throw new ApiException(I18nUtil._("状态处理失败!")); + throw new ApiException(_("状态处理失败!")); } // 更改商城订单状态为:已完成,注意事务问题 @@ -1221,7 +1231,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { // success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), StateCode.ORDER_STATE_FINISH, 0, 0); success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), StateCode.ORDER_STATE_RECEIVED, 0, 0); if (!success) { - throw new ApiException(I18nUtil._("状态处理失败!")); + throw new ApiException(_("状态处理失败!")); } String orderId = shopStoreSfOrder.getShop_order_id(); @@ -1264,6 +1274,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { QueryWrapper itemQueryWrapper = new QueryWrapper<>(); itemQueryWrapper.eq("order_id", orderId); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); + if(picking(order_item_rows)){ + logger.info("自行发货商品扣减库存成功"); + } if (!order_item_rows.isEmpty()) { for (ShopOrderItem shopOrderItem : order_item_rows) { Map stockDeltaMap = new HashMap<>(); @@ -1299,7 +1312,36 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { shopOrderBaseService.updateById(shopOrderBase); return CommonResult.success("操作成功"); } - + /** + * 根据订单列表扣除商品库存 + * @param order_item_rows + * @return + */ + public boolean picking(List order_item_rows){ + logger.info("出库商品扣除库存--开始"); + List updateProductItems = new ArrayList<>(); + for (ShopOrderItem shopOrderItem : order_item_rows) { + Long item_id = Long.valueOf(shopOrderItem.getItem_src_id()); + ShopProductItem productItem = shopProductItemService.get(item_id); + if (productItem == null) { + throw new ApiException(String.format(_("SKU【%s】不存在!"), item_id)); + } + Integer item_change_quantity=shopOrderItem.getOrder_item_quantity()*-1; + Integer quantity = productItem.getItem_quantity() + item_change_quantity; + if (quantity.compareTo(0) < 0) { + quantity = 0; + } + productItem.setItem_quantity(quantity); + //防止负数 + Integer tmp_quantity = productItem.getItem_quantity_frozen() + item_change_quantity.intValue(); + if (tmp_quantity.compareTo(0) < 0) { + tmp_quantity = 0; + } + productItem.setItem_quantity_frozen(tmp_quantity); + updateProductItems.add(productItem); + } + return shopProductItemService.updateBatchById(updateProductItems,updateProductItems.size()); + } // 私有方法 From 0b738e1b05fe9b32cfe8607f70b11faf6e2beeab Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 23 Oct 2025 15:16:59 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E8=87=AA=E8=A1=8C=E5=8F=91=E8=B4=A7=E7=8A=B6=E6=80=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=8C=E6=94=B9=E4=B8=BA=E5=B7=B2=E5=87=BA=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/sfexpress/service/impl/SFExpressApiServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) 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 759c60b1..502c89ce 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 @@ -1266,6 +1266,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { } Pair pair = wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderBase.getOrder_id()); if (pair.getFirst()) { + Integer orderStatus = StateCode.ORDER_STATE_SHIPPED;//已发货 + Integer orderIsOutStatus = StateCode.ORDER_PICKING_STATE_YES; // 已出库 + shopOrderInfoService.changeOrderStatus(orderId, orderStatus, orderIsOutStatus, 0); //完成订单后修改订单异常操作流程进入完成订单环节 shopOrderBase.setOperate_flag("1"); shopOrderBaseService.updateById(shopOrderBase); From 675d2109564ddde2819dd40f45284383d61874db Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 23 Oct 2025 16:09:36 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E9=A2=84=E8=AE=A2=E5=8D=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=A1=A8=20=E6=96=B9=E6=B3=95=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ShopOrderBookingService.java | 14 ++- .../impl/ShopOrderBaseServiceImpl.java | 6 +- .../impl/ShopOrderBookingServiceImpl.java | 115 +++++++++++++++--- 3 files changed, 118 insertions(+), 17 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java index 39fdd889..31c87590 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java @@ -15,8 +15,18 @@ import com.suisung.mall.core.web.service.IBaseService; public interface ShopOrderBookingService extends IBaseService { + /** + * 添加预约订单任务 + * + * @param orderId 订单ID + * @param bookingAt 预约时间 + * @return 是否添加成功 + */ + Boolean setupRedisBookingTask(String orderId, Long bookingAt); + /** * 添加或更新预约订单任务 + * * @param shopOrderBooking 预约订单任务信息 * @return 操作后的预约订单任务实例 */ @@ -24,7 +34,8 @@ public interface ShopOrderBookingService extends IBaseService /** * 查询有效的预约订单任务分页列表(按预约时间升序排序) - * @param pageNum 页码 + * + * @param pageNum 页码 * @param pageSize 页大小 * @return 分页结果 */ @@ -32,6 +43,7 @@ public interface ShopOrderBookingService extends IBaseService /** * 更新预约订单任务 + * * @param updateWrapper 更新条件包装器 * @return 是否更新成功 */ 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 5b31bc61..14d1490c 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 @@ -97,7 +97,6 @@ import com.suisung.mall.shop.product.pojo.vo.FixOrderVo; import com.suisung.mall.shop.product.service.*; import com.suisung.mall.shop.sfexpress.service.SFExpressApiService; import com.suisung.mall.shop.store.service.*; -import com.suisung.mall.shop.sync.service.SyncThirdDataService; import com.suisung.mall.shop.user.service.*; import com.suisung.mall.shop.wechat.service.WxOrderShippingService; import io.seata.common.util.StringUtils; @@ -383,7 +382,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl implements ShopOrderBookingService { + private static final Long MINUTES_BEFORE_BOOKING = 35L; + + @Autowired + private RedisService redisService; + + /** + * 添加预约订单任务 + * + * @param orderId 订单ID + * @param bookingAt 预约时间 + * @return 是否添加成功 + */ + @Override + public Boolean setupRedisBookingTask(String orderId, Long bookingAt) { + log.debug("设置预约订单任务: orderId={}, bookingAt={}", orderId, bookingAt); + + // 参数校验 + if (StringUtils.isEmpty(orderId) || CheckUtil.isEmpty(bookingAt)) { + log.warn("参数无效: orderId={}, bookingAt={}", orderId, bookingAt); + return false; + } + + try { + // 计算执行时间(预约时间前35分钟) + long runAt = bookingAt - TimeUnit.MINUTES.toSeconds(MINUTES_BEFORE_BOOKING); + + ShopOrderBooking shopOrderBooking = new ShopOrderBooking(); + shopOrderBooking.setOrder_id(orderId); + shopOrderBooking.setBooking_at(bookingAt); + shopOrderBooking.setRun_at(runAt); + shopOrderBooking.setStatus(CommonConstant.Enable); + + shopOrderBooking = addOrUpdate(shopOrderBooking); + + // 如果保存成功,设置Redis过期键 + if (shopOrderBooking != null) { + String redisKey = "booking::" + orderId; + // 设置过期时间为runAt时间点(相对于当前时间的秒数) + if (runAt > 0) { + redisService.set(redisKey, String.valueOf(runAt), runAt); + log.debug("Redis键设置成功: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); + } else { + log.warn("过期时间无效,未设置Redis键: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); + } + return true; + } else { + log.error("保存预约订单任务失败: orderId={}, bookingAt={}", orderId, bookingAt); + return false; + } + } catch (Exception e) { + log.error("设置预约订单任务时发生异常: orderId={}, bookingAt={}", orderId, bookingAt, e); + return false; + } + } + @Override public ShopOrderBooking addOrUpdate(ShopOrderBooking shopOrderBooking) { log.debug("添加或更新预约订单任务: {}", shopOrderBooking); @@ -31,21 +93,46 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl 0) { - // 更新操作 - log.debug("执行更新操作,ID: {}", shopOrderBooking.getId()); - this.updateById(shopOrderBooking); - return shopOrderBooking; - } else { - // 添加操作 - log.debug("执行添加操作"); - boolean saved = this.save(shopOrderBooking); - if (saved) { - return shopOrderBooking; - } else { - log.error("保存预约订单任务失败: {}", shopOrderBooking); - return null; + // 参数校验:order_id 和 booking_at 是必须项目 + if (StrUtil.isBlank(shopOrderBooking.getOrder_id()) || CheckUtil.isEmpty(shopOrderBooking.getBooking_at())) { + log.warn("缺少必要参数: order_id 或 booking_at"); + return null; + } + + try { + if (CheckUtil.isEmpty(shopOrderBooking.getRun_at())) { + long runAt = shopOrderBooking.getBooking_at() - TimeUnit.MINUTES.toSeconds(MINUTES_BEFORE_BOOKING); + shopOrderBooking.setRun_at(runAt); } + + // 如果 id 有值,直接更新 + if (CheckUtil.isNotEmpty(shopOrderBooking.getId())) { + log.debug("根据ID直接更新记录: {}", shopOrderBooking.getId()); + boolean updated = this.updateById(shopOrderBooking); + return updated ? shopOrderBooking : null; + } + + // 如果 order_id 有值,先查询记录是否存在 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("order_id", shopOrderBooking.getOrder_id()).eq("status", CommonConstant.Enable); + ShopOrderBooking existingBooking = this.getOne(queryWrapper); + + if (existingBooking != null) { + // 存在则更新 + shopOrderBooking.setId(existingBooking.getId()); + log.debug("更新已存在的记录,ID: {}", shopOrderBooking.getId()); + boolean updated = this.updateById(shopOrderBooking); + return updated ? shopOrderBooking : null; + } else { + // 不存在则保存 + log.debug("保存新记录"); + boolean saved = this.save(shopOrderBooking); + return saved ? shopOrderBooking : null; + } + } catch (Exception e) { + log.error("添加或更新预约订单任务时发生异常: orderId={}, bookingAt={}", + shopOrderBooking.getOrder_id(), shopOrderBooking.getBooking_at(), e); + return null; } } From bc1c308043b5b2f0438657b446fa80d1e8f79d4c Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 23 Oct 2025 16:37:26 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=93=8D=E4=BD=9C=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/sfexpress/service/impl/SFExpressApiServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 502c89ce..db6a17e7 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 @@ -1312,7 +1312,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { ShopOrderBase updateShopOrderBase=new ShopOrderBase(); updateShopOrderBase.setOrder_id(shopOrderId); updateShopOrderBase.setOperate_flag("2"); - shopOrderBaseService.updateById(shopOrderBase); + shopOrderBaseService.updateById(updateShopOrderBase); return CommonResult.success("操作成功"); } /** From a62b4b837d7192a0e1c890d7afb88bea46483b22 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 23 Oct 2025 16:51:05 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E9=A2=84=E8=AE=A2=E5=8D=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=A1=A8=20=E6=96=B9=E6=B3=95=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/suisung/mall/common/constant/RedisConstant.java | 3 +++ .../shop/order/service/impl/ShopOrderBaseServiceImpl.java | 5 ++++- .../shop/order/service/impl/ShopOrderBookingServiceImpl.java | 3 ++- 3 files changed, 9 insertions(+), 2 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 237c6fa5..37d51763 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 @@ -41,4 +41,7 @@ public class RedisConstant { // 您有新的订单来了 public static final String New_Order_Push_Flag_Key = ConstantRedis.Cache_NameSpace + "new:order:comimg:"; + // 预约订单任务新增键 + public static final String Order_Booking_Task_Key = ConstantRedis.Cache_NameSpace + "new:order:booking:task:"; + } 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 14d1490c..d82deb67 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 @@ -6642,7 +6642,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl 0) { redisService.set(redisKey, String.valueOf(runAt), runAt); From b21f526797c1083f9359dbd056d7c1c56e5afeeb Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 23 Oct 2025 17:22:15 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E9=80=80=E5=9B=9E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/impl/ShopOrderReturnServiceImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 514ce668..4751eda3 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 @@ -1469,8 +1469,8 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl returnItemQueryWrapper = new QueryWrapper<>(); returnItemQueryWrapper.in("return_id", return_ids); @@ -1508,7 +1508,12 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl Date: Fri, 24 Oct 2025 00:22:36 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E9=A2=84=E8=AE=A2=E5=8D=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=A1=A8=20=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8Credis=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/suisung/mall/common/utils/IdUtil.java | 5 +- .../mall/common/utils/StringUtils.java | 18 ++ .../quartz/job/UpdateOrderStatusJob.java | 58 +++--- .../order/listener/OrderPayedListener.java | 35 ++-- .../listener/RedisKeyExpiredListener.java | 25 +++ .../order/mapper/ShopOrderBookingMapper.java | 11 ++ .../service/ShopOrderBookingService.java | 17 +- .../order/service/ShopOrderInfoService.java | 17 ++ .../impl/ShopOrderBaseServiceImpl.java | 59 ++++-- .../impl/ShopOrderBookingServiceImpl.java | 89 ++++++++- .../impl/ShopOrderInfoServiceImpl.java | 170 +++++++++++++++--- .../service/impl/SFExpressApiServiceImpl.java | 20 ++- .../mapper/order/ShopOrderBaseMapper.xml | 1 + .../mapper/order/ShopOrderBookingMapper.xml | 16 ++ .../resources/templates/order_printer.txt | 6 +- 15 files changed, 452 insertions(+), 95 deletions(-) diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java index 60adc578..d9da2e16 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java @@ -21,8 +21,8 @@ public class IdUtil { private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS; private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); - private long workerId; - private long dataCenterId; + private final long workerId; + private final long dataCenterId; private long sequence = 0L; private long lastTimestamp = -1L; @@ -116,4 +116,5 @@ public class IdUtil { return timestamp; } + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java index d3d2ef85..e2d10b10 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java @@ -750,6 +750,24 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { } } + /** + * 取货单号格式化 + * + * @param pickNum 一般是 Long 类型 + * @return + */ + public static String fmtPickNum(Long pickNum) { + if (pickNum == null || pickNum <= 0) { + return ""; + } + + if (pickNum > 9999999) { + return String.valueOf(pickNum); + } + + return String.format("%07d", pickNum); + } + /** * 生成的随机数类型 */ diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java index 9cf3c70c..6174b61e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java @@ -6,6 +6,7 @@ import com.suisung.mall.common.utils.LogUtil; import com.suisung.mall.shop.config.SpringUtil; import com.suisung.mall.shop.distribution.service.ShopDistributionUserOrderService; import com.suisung.mall.shop.order.service.ShopOrderBaseService; +import com.suisung.mall.shop.order.service.ShopOrderBookingService; import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.sixun.utils.CommonUtil; import org.quartz.JobExecutionContext; @@ -31,6 +32,7 @@ public class UpdateOrderStatusJob extends QuartzJobBean { ShopOrderBaseService shopOrderBaseService = SpringUtil.getBean(ShopOrderBaseService.class); ShopOrderInfoService shopOrderInfoService = SpringUtil.getBean(ShopOrderInfoService.class); ShopDistributionUserOrderService shopDistributionUserOrderService = SpringUtil.getBean(ShopDistributionUserOrderService.class); + ShopOrderBookingService shopOrderBookingService = SpringUtil.getBean(ShopOrderBookingService.class); // 在UpdateOrderStatusJob.execute方法开始添加日志 logger.info("UpdateOrderStatusJob 方法开始执行"); @@ -56,35 +58,45 @@ public class UpdateOrderStatusJob extends QuartzJobBean { } } } - // 自动确认收货 - //shopOrderBaseService.autoReceive(); + // 自动确认收货 + //shopOrderBaseService.autoReceive(); - // 2. 更新为确认收货 - List order_id_receipt = shopOrderInfoService.getAutoFinishOrderId(); - if (CollUtil.isNotEmpty(order_id_receipt)) { - for (String order_id : order_id_receipt) { - try { - if (!shopOrderBaseService.receive(order_id, null)) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id)); - } - } catch (Exception e) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id), e); + // 2. 更新为确认收货 + List order_id_receipt = shopOrderInfoService.getAutoFinishOrderId(); + if (CollUtil.isNotEmpty(order_id_receipt)) { + for (String order_id : order_id_receipt) { + try { + if (!shopOrderBaseService.receive(order_id, null)) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id)); } + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id), e); } } + } - // 3.分销结算 - List order_id_settle_commission = shopOrderBaseService.getWaitingSettleCommissionOrder(null, null); - if (CollUtil.isNotEmpty(order_id_settle_commission)) { - for (String order_id : order_id_settle_commission) { - try { - if (!shopDistributionUserOrderService.settleDistributionUserOrder(order_id)) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id)); - } - } catch (Exception e) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id), e); - } + // 3.分销结算 + List order_id_settle_commission = shopOrderBaseService.getWaitingSettleCommissionOrder(null, null); + if (CollUtil.isNotEmpty(order_id_settle_commission)) { + for (String order_id : order_id_settle_commission) { + try { + if (!shopDistributionUserOrderService.settleDistributionUserOrder(order_id)) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id)); } + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id), e); } } + } + + // 4. 预约订单定时任务,同步到 redis + try { + int count = shopOrderBookingService.syncOrderBooking2RedisTask(); + logger.info("同步预约订单任务到 redis 定时任务成功,成功数量:" + count); + + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("同步预约订单任务到 redis 定时任务失败")), e); + } + + } } \ No newline at end of file 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 149a70a8..9787b2a1 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 @@ -149,15 +149,20 @@ public class OrderPayedListener { 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) { + + // 是预约订单吗? + boolean isBookingOrder = shopOrderInfoService.isBookingOrder(orderInfoOld); + // 已支付的订单,生成取单号,打票机并打印订单 Long orderPickupNum = shopOrderInfoService.isPaidOrderGenPickNumAndPrint(orderInfoOld.getStore_id(), orderId); - // 如果配送方式是 顺丰同城下单 + // 如果配送方式是 顺丰同城, 且立即下单,则触发顺丰同城下单 if (CheckUtil.isNotEmpty(orderInfoOld.getDelivery_type_id()) && orderInfoOld.getDelivery_type_id().equals(StateCode.DELIVERY_TYPE_SAME_CITY) - && CheckUtil.isNotEmpty(orderPickupNum)) { + && CheckUtil.isNotEmpty(orderPickupNum) + && !isBookingOrder) { - // 顺丰同城下单 + // 如果是立即下单,将触发顺丰同城下单,否则(预约下单) redis+cron 定时触发顺丰同城下单 Pair pairCreateSfOrder = sfExpressApiService.innerCreateSfExpressOrder(orderId, orderPickupNum); if (pairCreateSfOrder == null) { logger.error("[订单支付监听] 顺丰同城下单失败,无返回值 订单ID: {}", orderId); @@ -183,24 +188,28 @@ public class OrderPayedListener { } // 同城配送或普通快递,都发送 unipush 推送:您有一个新的订单,请查收! - String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" : ""; + String bookingTip = isBookingOrder ? "预约" : ""; + + String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" + bookingTip : bookingTip; String title = String.format("您有一笔新的%s订单,请注意查收。", orderType); - String content = String.format("这笔新%s订单:%s,用户于%s下的单,请注意查收。", orderType, orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); + String content = String.format("这笔新的%s订单:%s,用户于%s下的单,请注意查收。", orderType, orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); JSONObject payload = new JSONObject(); payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST); payload.put("orderId", orderId); pushMessageService.noticeMerchantEmployeeOrderAction(orderInfoOld.getStore_id(), orderId, title, content, payload); - // 发送延迟消息 25分钟拣货配送时间(提前5分钟,下单20分钟后,提醒商家及时拣货) - Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 300; - redisService.set(RedisConstant.SF_Order_Proc_WillExpire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); + if (!isBookingOrder) { // 非预约下单的情况 + // 发送延迟消息 25分钟拣货配送时间(提前5分钟,下单20分钟后,提醒商家及时拣货) + Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 300; + redisService.set(RedisConstant.SF_Order_Proc_WillExpire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); - // 发送延迟消息 25分钟拣货配送时间(下单25分钟后,提醒商家及时拣货) - mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L); - redisService.set(RedisConstant.SF_Order_Proc_Expire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); + // 发送延迟消息 25分钟拣货配送时间(下单25分钟后,提醒商家及时拣货) + 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小时过期 + // 记录推送已发送状态,避免重复推送 + redisService.set(pushFlagKey, "1", 24 * 3600); // 24小时过期 + } } catch (Exception e) { log.error("[订单支付监听] 发送推送消息失败. 订单ID: {}", orderId, e); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java index 099633eb..caa742f4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java @@ -15,9 +15,12 @@ import cn.hutool.json.JSONObject; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.constant.RedisConstant; import com.suisung.mall.shop.message.service.PushMessageService; +import com.suisung.mall.shop.order.service.ShopOrderInfoService; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.util.Pair; import javax.annotation.Resource; @@ -27,6 +30,10 @@ public class RedisKeyExpiredListener implements MessageListener { @Resource private PushMessageService pushMessageService; + @Lazy + @Resource + private ShopOrderInfoService shopOrderInfoService; + /** * Callback for processing received objects through Redis. * @@ -83,6 +90,24 @@ public class RedisKeyExpiredListener implements MessageListener { } else { log.error("[Redis过期监听] 订单即将超时事件处理失败. 店铺ID: {}, 订单号: {}", args[0], args[1]); } + } else if (expiredKey.startsWith(RedisConstant.Order_Booking_Task_Key)) { + // 预约订单向顺丰同城下单 + log.debug("[预约订单顺丰同城下单Redis过期监听] 开始处理预约订单顺丰同城下单超时事件. 过期键: {}", expiredKey); + + // orderId + String orderId = expiredKey.replace(RedisConstant.Order_Booking_Task_Key, ""); + if (StrUtil.isBlank(orderId)) { + log.error("[预约订单顺丰同城下单Redis过期监听] 键: {} 不符合预期格式,无法解析订单号", expiredKey); + return; + } + + // 重要:预约订单向顺丰同城下单 + Pair result = shopOrderInfoService.runBookingOrder2CreateSfExpressOrder(orderId); + if (result == null || !result.getFirst()) { + log.error("[预约订单顺丰同城下单Redis过期监听] 顺丰同城下单失败:{}, 订单号: {}", result != null ? result.getSecond() : "", orderId); + } else { + log.info("[预约订单顺丰同城下单Redis过期监听] 顺丰同城下单成功. 订单号: {}", orderId); + } } else { //log.debug("[Redis过期监听] 忽略非订单超时事件. 过期键: {}", expiredKey); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java index 939bc0fc..f6786bc1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java @@ -10,6 +10,17 @@ package com.suisung.mall.shop.order.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.suisung.mall.common.modules.order.ShopOrderBooking; +import org.apache.ibatis.annotations.Param; + +import java.util.List; public interface ShopOrderBookingMapper extends BaseMapper { + + /** + * 查询有效的(未到下单时机,执行时间未过期,订单状态正确)预约订单任务 + * + * @param limit_count + * @return + */ + List selectValidBookingList(@Param("limit_count") Integer limit_count); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java index 31c87590..8fa70126 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java @@ -47,6 +47,21 @@ public interface ShopOrderBookingService extends IBaseService * @param updateWrapper 更新条件包装器 * @return 是否更新成功 */ - boolean update(UpdateWrapper updateWrapper); + Boolean update(UpdateWrapper updateWrapper); + + /** + * 更改预约订单任务下单完成状态(同时删除 redis 的定时任务) + * + * @param orderId 订单ID + * @return 是否完成同城下单 + */ + Boolean updateOrderBookingFinishState(String orderId); + + /** + * 同步预约订单任务到 redis 定时任务 + * + * @return 同步数量 + */ + Integer syncOrderBooking2RedisTask(); } \ No newline at end of file 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 8f79f149..c4e39042 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 @@ -116,4 +116,21 @@ public interface ShopOrderInfoService extends IBaseService { * @return */ Pair checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTime, String bookingEndTime); + + + /** + * 判断订单是否为预约订单 + * + * @param orderInfo 订单信息 + * @return 是否为预约订单 + */ + Boolean isBookingOrder(ShopOrderInfo orderInfo); + + /** + * 预约订单到点后生成顺丰订单 + * + * @param orderId 订单ID + * @return + */ + Pair runBookingOrder2CreateSfExpressOrder(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 d82deb67..7b46bced 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 @@ -99,7 +99,6 @@ import com.suisung.mall.shop.sfexpress.service.SFExpressApiService; import com.suisung.mall.shop.store.service.*; import com.suisung.mall.shop.user.service.*; import com.suisung.mall.shop.wechat.service.WxOrderShippingService; -import io.seata.common.util.StringUtils; import io.seata.core.context.RootContext; import io.seata.core.exception.TransactionException; import io.seata.spring.annotation.GlobalTransactional; @@ -8700,8 +8699,12 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_id", shopOrderId); List shopOrderItemList = shopOrderItemService.list(queryWrapper); @@ -8774,6 +8787,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl 9999999) { - return String.valueOf(pickNum); - } - - return String.format("%07d", pickNum); - } /** * 计算平台与代理商的总分账金额 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java index 65c6bef4..67a7ff14 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java @@ -8,6 +8,8 @@ package com.suisung.mall.shop.order.service.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -22,9 +24,12 @@ import com.suisung.mall.shop.order.mapper.ShopOrderBookingMapper; import com.suisung.mall.shop.order.service.ShopOrderBookingService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import javax.annotation.Resource; +import java.util.List; import java.util.concurrent.TimeUnit; @Slf4j @@ -33,6 +38,10 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl 0) { - redisService.set(redisKey, String.valueOf(runAt), runAt); + redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), runAt); log.debug("Redis键设置成功: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); } else { log.warn("过期时间无效,未设置Redis键: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); @@ -152,12 +161,13 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl page = new Page<>(pageNum, pageSize); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", CommonConstant.Enable); - queryWrapper.orderByAsc("booking_at"); + queryWrapper.gt("run_at", System.currentTimeMillis() / 1000); + queryWrapper.orderByAsc("run_at"); return this.page(page, queryWrapper); } @Override - public boolean update(UpdateWrapper updateWrapper) { + public Boolean update(UpdateWrapper updateWrapper) { log.debug("更新预约订单任务"); if (updateWrapper == null) { log.warn("更新条件不能为空"); @@ -172,4 +182,77 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("order_id", orderId); + updateWrapper.set("status", CommonConstant.Disable2); + + // 执行更新操作 + boolean result = this.update(updateWrapper); + + if (result) { + log.debug("成功完成预约订单任务的同城配送下单: orderId={}", orderId); + + // 同时删除对应的Redis键 + String redisKey = RedisConstant.Order_Booking_Task_Key + orderId; + redisService.del(redisKey); + log.debug("已清理对应的Redis定时任务键: {}", redisKey); + } else { + log.warn("未找到对应的预约订单任务,无法完成同城配送下单: orderId={}", orderId); + } + + return result; + } catch (Exception e) { + log.error("完成预约订单任务的同城配送下单时发生异常: orderId={}", orderId, e); + return false; + } + } + + /** + * 同步预约订单任务到 redis 定时任务 + * + * @return 同步数量 + */ + @Override + public Integer syncOrderBooking2RedisTask() { + List list = shopOrderBookingMapper.selectValidBookingList(200); + if (CollUtil.isEmpty(list)) { + log.debug("未找到有效的预约订单任务"); + return 0; + } + + int count = 0; + for (ShopOrderBooking shopOrderBooking : list) { + String redisKey = RedisConstant.Order_Booking_Task_Key + shopOrderBooking.getOrder_id(); + if (redisService.get(redisKey) != null) { + log.debug("已存在对应的Redis定时任务键: {},不需要同步", redisKey); + continue; + } + + redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), shopOrderBooking.getRun_at()); + + count++; + } + + return count; + } + + } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java index ff6f2335..64e415ec 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java @@ -35,6 +35,7 @@ import com.suisung.mall.shop.base.service.AccountBaseConfigService; import com.suisung.mall.shop.base.service.ShopBaseStateCodeService; import com.suisung.mall.shop.distribution.service.ShopDistributionUserCommissionService; import com.suisung.mall.shop.distribution.service.ShopDistributionUserWithdrawService; +import com.suisung.mall.shop.message.service.PushMessageService; import com.suisung.mall.shop.order.mapper.ShopOrderInfoMapper; import com.suisung.mall.shop.order.service.*; import com.suisung.mall.shop.plantform.service.ShopPlantformActivityItemService; @@ -57,6 +58,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadPoolExecutor; @@ -120,10 +122,18 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl 0) { - // 已打印过,无需打印 - logger.info("####以前生成的订单{}的取单号####", orderInfoOld.getOrder_pickup_num()); - return orderInfoOld.getOrder_pickup_num(); - } + logger.info("####开始处理订单{}的取单号生成和打印####", orderId); - ShopOrderInfo orderInfo = new ShopOrderInfo(); - orderInfo.setOrder_id(orderId); - // 生成取单号,写入order_info - Long orderPickupNum = genTodayPickupNum(storeId); - orderInfo.setOrder_pickup_num(orderPickupNum); - orderInfo.setOrder_express_print(CommonConstant.Enable); // 打印标记 1-已打印 - if (!edit(orderInfo)) { + // 参数校验 + if (storeId == null || orderId == null) { + logger.warn("参数校验失败: storeId或orderId为空"); return 0L; } - // 订单状态处理成功之后,打印小票 - shopStorePrinterService.printShopStoreOrder(storeId, orderId); - logger.info("####新生成订单{}的取单号####", orderPickupNum); - return orderPickupNum; + ShopOrderInfo orderInfoOld = get(orderId); + if (orderInfoOld == null) { + logger.warn("未找到订单信息: orderId={}", orderId); + return 0L; + } + + // 检查是否已经生成过取单号并打印 + if (orderInfoOld.getOrder_express_print() != null + && orderInfoOld.getOrder_express_print().equals(CommonConstant.Enable) + && orderInfoOld.getOrder_pickup_num() != null + && orderInfoOld.getOrder_pickup_num() > 0) { + logger.info("订单{}已处理过,取单号: {}", orderId, orderInfoOld.getOrder_pickup_num()); + return orderInfoOld.getOrder_pickup_num(); + } + + try { + // 生成取单号 + Long orderPickupNum = genTodayPickupNum(storeId); + if (orderPickupNum == null || orderPickupNum <= 0) { + logger.error("生成取单号失败: storeId={}", storeId); + return 0L; + } + + // 更新订单信息 + ShopOrderInfo orderInfo = new ShopOrderInfo(); + orderInfo.setOrder_id(orderId); + orderInfo.setOrder_pickup_num(orderPickupNum); + orderInfo.setOrder_express_print(CommonConstant.Enable); // 打印标记 1-已打印 + + if (!edit(orderInfo)) { + logger.error("更新订单取单号失败: orderId={}", orderId); + return 0L; + } + + // 打印小票 + shopStorePrinterService.printShopStoreOrder(storeId, orderId); + logger.info("####成功生成并打印订单{}的取单号: {}####", orderId, orderPickupNum); + return orderPickupNum; + + } catch (Exception e) { + logger.error("处理订单取单号时发生异常: orderId={}", orderId, e); + return 0L; + } } /** @@ -872,14 +909,99 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl= System.currentTimeMillis() / 1000; + } + + /** + * 执行预约订单到点后向顺丰同城订单任务 + * + * @param orderId 订单ID + * @return 处理结果Pair,第一个元素表示是否成功,第二个元素为结果描述 + */ + @Transactional + @Override + public Pair runBookingOrder2CreateSfExpressOrder(String orderId) { + // 参数校验 + if (StrUtil.isBlank(orderId)) { + logger.warn("[预约订单顺丰下单] 订单ID为空"); + return Pair.of(false, "订单ID不能为空"); + } + + try { + // 获取订单信息 + ShopOrderInfo shopOrderInfo = get(orderId); + if (shopOrderInfo == null) { + logger.warn("[预约订单顺丰下单] 订单不存在: {}", orderId); + return Pair.of(false, "订单不存在"); + } + + Integer orderState = shopOrderInfo.getOrder_state_id(); + + // 验证订单是否为有效的预约订单 + if (!isBookingOrder(shopOrderInfo) || CheckUtil.isEmpty(orderState) + || orderState.intValue() >= StateCode.ORDER_STATE_SHIPPED) { + logger.warn("[预约订单顺丰下单] 订单不是预约订单或者状态不正确: orderId: {}, orderState: {}", orderId, orderState); + return Pair.of(false, "订单不是预约订单或者状态不正确"); + } + + // 调用顺丰接口创建订单 + Pair sfResult = sfExpressApiService.innerCreateSfExpressOrder(orderId, 0L); + if (sfResult == null) { + logger.error("[预约订单顺丰下单] 调用顺丰接口无返回值, orderId={}", orderId); + throw new RuntimeException("预约订单顺丰下单失败,接口无响应"); + } + + if (!sfResult.getFirst()) { + logger.error("[预约订单顺丰下单] 调用顺丰接口失败: {}, orderId={}", sfResult.getSecond(), orderId); + throw new RuntimeException("预约订单顺丰下单失败," + sfResult.getSecond()); + } + + // 更新订单预约状态为立即下单状态 + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("order_id", orderId) + .set("booking_state", CommonConstant.Order_Booking_State_LJ); + this.update(updateWrapper); + + // 标记预约任务完成 + shopOrderBookingService.updateOrderBookingFinishState(orderId); + + // 发送推送消息给商家 + String title = "您有一笔新的订单,请注意查收。"; + String content = String.format("这笔新的订单:%s,用户于%s下的单,请注意查收。", + orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); + JSONObject payload = new JSONObject(); + payload.set("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST); + payload.set("orderId", orderId); + pushMessageService.noticeMerchantEmployeeOrderAction(null, orderId, title, content, payload); + + logger.info("[预约订单顺丰下单] 成功, orderId={}", orderId); + return Pair.of(true, "预约订单在顺丰下单成功"); + + } catch (Exception e) { + logger.error("[预约订单顺丰下单] 异常, orderId={}", orderId, e); + throw new RuntimeException("系统异常,预约订单顺丰下单失败", e); + } + } + } 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 db6a17e7..ea46e35d 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 @@ -568,7 +568,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { @Override public Pair innerCreateSfExpressOrder(String shopOrderId, Long orderPickupNum) { logger.info("开始顺丰同城下单"); - if (StrUtil.isBlank(shopOrderId) || orderPickupNum == null || orderPickupNum <= 0) { + if (StrUtil.isBlank(shopOrderId) || orderPickupNum == null) { return Pair.of(false, "顺丰同城下单时,缺少必要参数!"); } @@ -1108,7 +1108,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { String order_id = shopStoreSfOrder.getShop_order_id(); itemQueryWrapper.eq("order_id", order_id); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); - if(picking(order_item_rows)){ + if (picking(order_item_rows)) { logger.info("顺丰发货商品扣减库存成功"); } if (!order_item_rows.isEmpty()) { @@ -1266,7 +1266,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { } Pair pair = wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderBase.getOrder_id()); if (pair.getFirst()) { - Integer orderStatus = StateCode.ORDER_STATE_SHIPPED;//已发货 + Integer orderStatus = StateCode.ORDER_STATE_SHIPPED;//已发货 Integer orderIsOutStatus = StateCode.ORDER_PICKING_STATE_YES; // 已出库 shopOrderInfoService.changeOrderStatus(orderId, orderStatus, orderIsOutStatus, 0); //完成订单后修改订单异常操作流程进入完成订单环节 @@ -1277,7 +1277,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { QueryWrapper itemQueryWrapper = new QueryWrapper<>(); itemQueryWrapper.eq("order_id", orderId); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); - if(picking(order_item_rows)){ + if (picking(order_item_rows)) { logger.info("自行发货商品扣减库存成功"); } if (!order_item_rows.isEmpty()) { @@ -1292,7 +1292,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { return CommonResult.success("操作成功"); //出库扣减思迅库存end } - return CommonResult.failed("操作失败:"+pair.getSecond()); + return CommonResult.failed("操作失败:" + pair.getSecond()); } /** @@ -1309,18 +1309,20 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { // 送达后,发出催促微信用户确认收货通知 (同城配送不能调用微信的确认收货) shopOrderInfoService.changeOrderStatus(shopOrderId, orderStatus, 0, 0); //完成订单后修改订单异常操作流程 - ShopOrderBase updateShopOrderBase=new ShopOrderBase(); + ShopOrderBase updateShopOrderBase = new ShopOrderBase(); updateShopOrderBase.setOrder_id(shopOrderId); updateShopOrderBase.setOperate_flag("2"); shopOrderBaseService.updateById(updateShopOrderBase); return CommonResult.success("操作成功"); } + /** * 根据订单列表扣除商品库存 + * * @param order_item_rows * @return */ - public boolean picking(List order_item_rows){ + public boolean picking(List order_item_rows) { logger.info("出库商品扣除库存--开始"); List updateProductItems = new ArrayList<>(); for (ShopOrderItem shopOrderItem : order_item_rows) { @@ -1329,7 +1331,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { if (productItem == null) { throw new ApiException(String.format(_("SKU【%s】不存在!"), item_id)); } - Integer item_change_quantity=shopOrderItem.getOrder_item_quantity()*-1; + Integer item_change_quantity = shopOrderItem.getOrder_item_quantity() * -1; Integer quantity = productItem.getItem_quantity() + item_change_quantity; if (quantity.compareTo(0) < 0) { quantity = 0; @@ -1343,7 +1345,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { productItem.setItem_quantity_frozen(tmp_quantity); updateProductItems.add(productItem); } - return shopProductItemService.updateBatchById(updateProductItems,updateProductItems.size()); + return shopProductItemService.updateBatchById(updateProductItems, updateProductItems.size()); } // 私有方法 diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml index 4c7e43f6..19e12d17 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml @@ -535,6 +535,7 @@ a.order_id,a.store_id,a.store_name,a.buyer_user_id, a.buyer_user_name,a.order_time,a.order_payment_amount,a.order_product_amount, b.order_title, b.delivery_type_id, b.payment_type_id, b.payment_time, b.order_pickup_num, + CASE WHEN b.booking_state = 2 THEN 2 ELSE 1 END AS booking_state, b.booking_begin_time, c.order_message, c.order_item_amount, c.order_shipping_fee, c.order_shipping_fee_amount, c.delivery_time, (c.order_discount_amount + c.voucher_price + c.order_points_fee + c.order_adjust_fee) as total_discount_amount, c.packing_fee, diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml index 74d140ce..86b216db 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml @@ -5,4 +5,20 @@ * + + diff --git a/mall-shop/src/main/resources/templates/order_printer.txt b/mall-shop/src/main/resources/templates/order_printer.txt index 1e248a80..9014e58f 100644 --- a/mall-shop/src/main/resources/templates/order_printer.txt +++ b/mall-shop/src/main/resources/templates/order_printer.txt @@ -3,6 +3,7 @@ 示例格式化后的: 小发同城
+预约订单
--------------------------------
#00019232
买家备注:不用敲门,放在门口旁边的外卖箱,打个电话告知送达就行,谢谢!!!
@@ -57,4 +58,7 @@ ${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
押金:¥${(yajin!0)?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
会员权益:-¥${(quanyi!0)?string('0.00')}
秒杀:-¥${(miaosha!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
第二版带变量的模版: -${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
\ No newline at end of file +${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
+ +第三版带变量的模版(有预约订单) +${store_name}
<#if is_booking_order>预约订单
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
\ No newline at end of file From ea7af33acba63fd6821c02ecde625101b8493326 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Fri, 24 Oct 2025 11:52:23 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E8=B0=83=E6=95=B4sql=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=EF=BC=8C=E9=A2=84=E7=BA=A6=E4=B8=8B=E5=8D=95=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/RedisKeyExpiredListener.java | 2 +- .../order/service/ShopOrderInfoService.java | 2 +- .../impl/ShopOrderInfoServiceImpl.java | 10 ++++- .../impl/ShopOrderReturnServiceImpl.java | 41 ++++++++++--------- .../mapper/order/ShopOrderBookingMapper.xml | 5 ++- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java index caa742f4..b21fa126 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java @@ -102,7 +102,7 @@ public class RedisKeyExpiredListener implements MessageListener { } // 重要:预约订单向顺丰同城下单 - Pair result = shopOrderInfoService.runBookingOrder2CreateSfExpressOrder(orderId); + Pair result = shopOrderInfoService.runBookingOrder2SfExpressOrder(orderId); if (result == null || !result.getFirst()) { log.error("[预约订单顺丰同城下单Redis过期监听] 顺丰同城下单失败:{}, 订单号: {}", result != null ? result.getSecond() : "", orderId); } else { 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 c4e39042..478a5e93 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 @@ -132,5 +132,5 @@ public interface ShopOrderInfoService extends IBaseService { * @param orderId 订单ID * @return */ - Pair runBookingOrder2CreateSfExpressOrder(String orderId); + Pair runBookingOrder2SfExpressOrder(String orderId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java index 64e415ec..427499e2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java @@ -940,7 +940,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl runBookingOrder2CreateSfExpressOrder(String orderId) { + public Pair runBookingOrder2SfExpressOrder(String orderId) { // 参数校验 if (StrUtil.isBlank(orderId)) { logger.warn("[预约订单顺丰下单] 订单ID为空"); @@ -964,6 +964,14 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl sfResult = sfExpressApiService.innerCreateSfExpressOrder(orderId, 0L); if (sfResult == null) { 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 4751eda3..2a7387e4 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 @@ -1470,7 +1470,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl returnItemQueryWrapper = new QueryWrapper<>(); returnItemQueryWrapper.in("return_id", return_ids); @@ -1508,7 +1508,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl 0) { - log.debug("[是否禁止退货] 订单已超过退货期限 true,order_id: {}", orderId); + log.debug("[是否禁止退货] 订单已超过退货期限,不允许退货,order_id: {}", orderId); return true; } } catch (Exception e) { - log.error("[是否禁止退货] 检查订单退货期限时发生异常 true,order_id: {}", orderId, e); + log.error("[是否禁止退货] 检查订单退货期限时发生异常,不允许退货,order_id: {}", orderId, e); } } @@ -2165,17 +2165,17 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl contractTypeIds = Convert.toList(Integer.class, contractTypeIdsStr); if (contractTypeIds != null && contractTypeIds.contains(StateCode.CONTRACT_TYPE_DENY_RETURN)) { - log.debug("[是否禁止退货] 商品设置了禁止退货标识 true,order_id: {}, product_id: {}", orderId, productId); + log.debug("[是否禁止退货] 商品设置了禁止退货标识,不允许退货,order_id: {}, product_id: {}", orderId, productId); return true; } } catch (Exception e) { - log.error("[是否禁止退货] 解析商品保障类型失败 true,order_id: {}, product_id: {}", orderId, productId, e); + log.error("[是否禁止退货] 解析商品保障类型失败,不允许退货,order_id: {}, product_id: {}", orderId, productId, e); + return true; } } // 默认允许退货 - log.debug("[是否禁止退货] false}"); + log.debug("[是否禁止退货] 允许退货}"); return false; } @@ -2243,7 +2244,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl UNIX_TIMESTAMP() AND b.booking_state = 2 - AND b.booking_begin_time is not null + AND b.booking_begin_time IS NOT NULL AND b.order_state_id < 2040 + AND c.shop_order_id IS NULL ORDER BY a.run_at ASC LIMIT #{limit_count} + From e847381bde06b7026d78d3550e985689eca133f2 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Fri, 24 Oct 2025 12:04:05 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/controller/admin/ShopBaseProductCategoryController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseProductCategoryController.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseProductCategoryController.java index 55280dba..9dac3ce7 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseProductCategoryController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseProductCategoryController.java @@ -117,6 +117,7 @@ public class ShopBaseProductCategoryController { oldShopBaseProductCategory.setCategory_order(shopBaseProductCategory.getCategory_order()); }else { oldShopBaseProductCategory.setCategory_order(shopBaseProductCategory.getCategory_order()); + oldShopBaseProductCategory.setType_id(shopBaseProductCategory.getType_id()); oldShopBaseProductCategory.setCategory_is_enable(shopBaseProductCategory.getCategory_is_enable()); oldShopBaseProductCategory.setCategory_image(shopBaseProductCategory.getCategory_image()); } From 865799670594c2785bbd3ae176b6b42985b969ac Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Fri, 24 Oct 2025 14:01:46 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E6=96=B9=E6=B3=95=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../quartz/job/UpdateOrderSeparateJob.java | 4 + .../controller/mobile/LakalaController.java | 2 +- .../shop/lakala/service/LakalaApiService.java | 30 ++- .../lakala/service/LklOrderDrawService.java | 24 ++ .../service/impl/LakalaApiServiceImpl.java | 253 ++++++++++++++++-- .../service/impl/LklOrderDrawServiceImpl.java | 109 +++++++- 6 files changed, 400 insertions(+), 22 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 8fea6e7c..d3ce8b57 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 @@ -30,6 +30,10 @@ public class UpdateOrderSeparateJob extends QuartzJobBean { Integer processedCount = lakalaApiService.fixUnSuccessSeparateStatusJob(); logger.info("[分账状态修复定时任务] 执行完成,共处理 {} 条记录", processedCount); + + processedCount = lakalaApiService.fixUnSuccessDrawedStatusJob(); + logger.info("[提现状态修复定时任务] 执行完成,共处理 {} 条记录", processedCount); + } catch (Exception e) { logger.error("[分账状态修复定时任务] 执行过程中发生异常", e); throw new JobExecutionException(e); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java index 9d0a5539..77ecfd50 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java @@ -222,7 +222,7 @@ public class LakalaController extends BaseControllerImpl { @ApiOperation(value = "拉卡拉提现结果通知", notes = "拉卡拉提现结果通知") @RequestMapping(value = "/ewallet/drawNotify", method = RequestMethod.POST) public ResponseEntity ewalletWithDrawNotify(HttpServletRequest request) { - JSONObject resp = lakalaPayService.ewalletWithDrawNotify(request); + JSONObject resp = lakalaPayService.ewalletWithDrawNotify(request, "", ""); if (resp != null && "SUCCESS".equals(resp.get("code"))) { return ResponseEntity.ok(resp); } 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 9f7631f3..ed0e94de 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 @@ -272,6 +272,17 @@ public interface LakalaApiService { */ JSONObject sacsQuery(String merchantNo, String separateNo); + /** + * 提现结果查询 + *

+ * 参考:https://o.lakala.com/#/home/document/detail?id=366 + * + * @param merchantNo 分账方商户号 + * @param drawJnl 提款流水号 + * @return 提现结果数据 + */ + JSONObject ewalletWithdrawQuery(String merchantNo, String drawJnl); + /** * 商户分账业务信息查询 @@ -304,6 +315,15 @@ public interface LakalaApiService { */ Integer fixUnSuccessSeparateStatusJob(); + /** + * 更改已经提现的状态(定时任务用途) + * 该方法用于处理3天前未成功提现的订单记录,通过主动查询拉卡拉提现状态来更新本地记录 + * 每条记录最多处理5次,避免无限重试 + * + * @return 成功处理的记录数量 + */ + Integer fixUnSuccessDrawedStatusJob(); + /** * 检测修复补全商户的商户分账业务信息及分账接收方绑定关系(分账业务申请异步通知的补偿机制) * @@ -346,13 +366,15 @@ public interface LakalaApiService { Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String orderId, String summary); /** - * 拉卡拉账户D1提现结果通知 + * 拉卡拉账户D1提现结果通知处理(有补偿机制参数) * 参考:https://o.lakala.com/#/home/document/detail?id=367 * - * @param request - * @return + * @param request HTTP请求对象,包含拉卡拉提现结果通知的参数 + * @param merchantNo 商户号 (补偿必选) + * @param drawJnl 提现流水号 (补偿必选) + * @return JSONObject 响应结果对象 */ - JSONObject ewalletWithDrawNotify(HttpServletRequest request); + JSONObject ewalletWithDrawNotify(HttpServletRequest request, String merchantNo, String drawJnl); /** * 商户分账参数计算及评估 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java index f98153b5..a8e0fc56 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java @@ -11,6 +11,9 @@ package com.suisung.mall.shop.lakala.service; import com.suisung.mall.common.modules.lakala.LklOrderDraw; import com.suisung.mall.core.web.service.IBaseService; +import java.util.Date; +import java.util.List; + public interface LklOrderDrawService extends IBaseService { /** @@ -30,6 +33,18 @@ public interface LklOrderDrawService extends IBaseService { */ LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo); + + /** + * 分页获取未成功提现的记录 + * + * @param beginDate 开始时间(必须) + * @param endDate 结束时间(可选,为空时默认为当前时间) + * @param page 页码(从1开始) + * @param pageSize 每页大小 + * @return 未成功提现的记录列表,参数无效时返回空列表而非null + */ + List getUnSuccessDrawedList(Date beginDate, Date endDate, Integer page, Integer pageSize); + /** * 判断订单是否已经提现完成 * @@ -37,4 +52,13 @@ public interface LklOrderDrawService extends IBaseService { * @return */ Boolean isOrderDrawed(String orderId); + + /** + * 判断订单是否已经提现完成 + * + * @param merchantNo 商户号 + * @param drawJnl 提现流水号 + * @return 如果订单已提现完成返回true,否则返回false + */ + Boolean isOrderDrawed(String merchantNo, String drawJnl); } 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 ba7fe700..c56ca586 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 @@ -2110,6 +2110,87 @@ public class LakalaApiServiceImpl implements LakalaApiService { } } + /** + * 提现结果查询 + *

+ * 参考:https://o.lakala.com/#/home/document/detail?id=366 + * + * @param merchantNo 分账方商户号 + * @param drawJnl 提款流水号 + * @return 提现结果数据 + */ + @Override + public JSONObject ewalletWithdrawQuery(String merchantNo, String drawJnl) { + // 1. 参数校验 + if (StrUtil.isBlank(merchantNo) || StrUtil.isBlank(drawJnl)) { + log.warn("[提现结果查询] 参数校验失败:缺少必要参数, merchantNo={}, drawJnl={}", merchantNo, drawJnl); + return null; + } + + try { + // 查询提现表lkl_order_draw是否已经提现完成? + Boolean isDrawed = lklOrderDrawService.isOrderDrawed(merchantNo, drawJnl); + if (isDrawed != null && isDrawed) { + log.warn("[提现结果查询] 已提现无效更新提现数据, merchantNo={}, drawJnl={}", merchantNo, drawJnl); + return null; + } + + // 2. 配置初始化 + log.debug("[提现结果查询] 初始化拉卡拉SDK"); + initLKLSDK(); + + // 3. 装配数据 + log.debug("[提现结果查询] 装配请求参数,merchantNo={}, drawJnl={}", merchantNo, drawJnl); + V2LaepIndustryEwalletWithdrawQueryRequest req = new V2LaepIndustryEwalletWithdrawQueryRequest(); + req.setOrgNo(orgCode); + req.setMerchantNo(merchantNo); + req.setDrawJnl(drawJnl); + + // 4. 发送请求 + log.info("[提现结果查询] 开始查询提现结果,merchantNo={}, drawJnl={}", merchantNo, drawJnl); + String responseStr = LKLSDK.httpPost(req); + + if (StrUtil.isBlank(responseStr)) { + log.error("[提现结果查询] 服务器无返回值, merchantNo={}, drawJnl={}", merchantNo, drawJnl); + return null; + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (lakalaRespJSON == null) { + log.error("[提现结果查询] 响应数据解析失败, merchantNo={}, drawJnl={}, response={}", + merchantNo, drawJnl, responseStr); + return null; + } + + // 5. 检查业务状态码 + String retCode = lakalaRespJSON.getStr("retCode"); + if (!lklSuccessCode.equals(retCode)) { + log.warn("[提现结果查询] 业务处理失败, merchantNo={}, drawJnl={}, retCode={}, retMsg={}", + merchantNo, drawJnl, retCode, lakalaRespJSON.getStr("retMsg")); + return null; + } + + // 6. 检查响应数据 + JSONObject respData = lakalaRespJSON.getJSONObject("respData"); + if (respData == null) { + log.warn("[提现结果查询] 响应数据为空, merchantNo={}, drawJnl={}, response={}", + merchantNo, drawJnl, responseStr); + return null; + } + + log.info("[提现结果查询] 查询成功, merchantNo={}, drawJnl={}", merchantNo, drawJnl); + return respData; + + } catch (SDKException e) { + log.error("[提现结果查询] SDK调用异常, merchantNo={}, drawJnl={}", merchantNo, drawJnl, e); + return null; + } catch (Exception e) { + log.error("[提现结果查询] 查询过程中发生未知异常, merchantNo={}, drawJnl={}", merchantNo, drawJnl, e); + return null; + } + } + + /** * 商户分账业务信息查询 * 参考:https://o.lakala.com/#/home/document/detail?id=381 @@ -3160,6 +3241,126 @@ public class LakalaApiServiceImpl implements LakalaApiService { } + /** + * 更改已经提现的状态(定时任务用途) + * 该方法用于处理3天前未成功提现的订单记录,通过主动查询拉卡拉提现状态来更新本地记录 + * 每条记录最多处理5次,避免无限重试 + * + * @return 成功处理的记录数量 + */ + @Override + public Integer fixUnSuccessDrawedStatusJob() { + log.info("[提现状态修复任务] 开始执行未成功提现记录的状态修复任务"); + + // 获取3天前提现状态未成功的记录 + Date endDate = new Date(); + Date beginDate = DateUtils.addDays(endDate, -3); // 3天前 + + // 分页参数 + int pageSize = 200; + int currentPage = 1; + int totalSuccessCount = 0; + int totalProcessed = 0; + + // 记录处理开始时间 + long startTime = System.currentTimeMillis(); + String redisPrefKey = "lkl:draw:status:retry:"; + + try { + List lklOrderDraws; + do { + // 分页获取未成功提现的记录 + lklOrderDraws = lklOrderDrawService.getUnSuccessDrawedList(beginDate, endDate, currentPage, pageSize); + + if (CollectionUtil.isEmpty(lklOrderDraws)) { + break; + } + + log.info("[提现状态修复任务] 获取到第{}页数据,共{}条记录", currentPage, lklOrderDraws.size()); + + // 按处理次数排序,从未处理过的记录优先处理,处理次数越多优先级越低 + lklOrderDraws.sort((o1, o2) -> { + String redisKey1 = redisPrefKey + o1.getDraw_jnl(); + String redisKey2 = redisPrefKey + o2.getDraw_jnl(); + + int retryCount1 = Convert.toInt(redisService.get(redisKey1), 0); + int retryCount2 = Convert.toInt(redisService.get(redisKey2), 0); + + return Integer.compare(retryCount1, retryCount2); + }); + + // 处理当前页中的记录 + for (LklOrderDraw record : lklOrderDraws) { + // 检查该记录的处理次数是否已达到上限(5次) + String redisKey = redisPrefKey + record.getDraw_jnl(); + int retryCount = Convert.toInt(redisService.get(redisKey), 0); + + if (retryCount >= 5) { + log.warn("[提现状态修复任务] 记录已达到最大重试次数,跳过处理: merchantNo={}, drawJnl={}", + record.getMerc_id(), record.getDraw_jnl()); + continue; + } + + totalProcessed++; + // 每处理10条记录才输出一次详细日志,减少日志量 + if (totalProcessed % 10 == 1) { + log.info("[提现状态修复任务] 正在处理第 {} 条记录: merchantNo={}, drawJnl={}, 已重试{}次", + totalProcessed, record.getMerc_id(), record.getDraw_jnl(), retryCount); + } + + try { + // 增加处理次数计数并设置3天过期时间 + redisService.incr(redisKey, 1); + redisService.expire(redisKey, 3 * 24 * 60 * 60); + + // 调用拉卡拉提现查询接口进行状态补偿 + JSONObject drawResult = ewalletWithDrawNotify(null, record.getMerc_id(), record.getDraw_jnl()); + + // 检查处理结果 + if (drawResult != null && "SUCCESS".equals(drawResult.getStr("code", ""))) { + totalSuccessCount++; + log.debug("[提现状态修复任务] 记录处理成功: merchantNo={}, drawJnl={}", + record.getMerc_id(), record.getDraw_jnl()); + } else { + log.warn("[提现状态修复任务] 记录处理失败: merchantNo={}, drawJnl={}", + record.getMerc_id(), record.getDraw_jnl()); + } + } catch (Exception e) { + log.error("[提现状态修复任务] 处理记录时发生异常: merchantNo={}, drawJnl={}", + record.getMerc_id(), record.getDraw_jnl(), e); + } + } + + log.info("[提现状态修复任务] 第{}页处理完成,已处理 {} 条记录,总成功 {} 条", + currentPage, totalProcessed, totalSuccessCount); + + // 如果当前页数据少于页面大小,说明已经是最后一页 + if (lklOrderDraws.size() < pageSize) { + break; + } + + currentPage++; + + // 添加短暂延迟,避免对系统造成过大压力 + Thread.sleep(100); + + } while (!CollectionUtil.isEmpty(lklOrderDraws)); + + } catch (InterruptedException e) { + log.warn("[提现状态修复任务] 任务被中断"); + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("[提现状态修复任务] 任务执行过程中发生异常", e); + } + + long endTime = System.currentTimeMillis(); + log.info("[提现状态修复任务] 任务执行完成,总共处理 {} 条记录,成功处理 {} 条记录,耗时 {} ms", + totalProcessed, totalSuccessCount, (endTime - startTime)); + + return totalSuccessCount; + } + + /** * 检测修复补全商户的商户分账业务信息及分账接收方绑定关系(分账业务申请异步通知的补偿机制) * @@ -3629,29 +3830,40 @@ public class LakalaApiServiceImpl implements LakalaApiService { /** - * 拉卡拉账户D1提现结果通知处理 + * 拉卡拉账户D1提现结果通知处理(有补偿机制参数) * 参考:https://o.lakala.com/#/home/document/detail?id=367 * - * @param request HTTP请求对象,包含拉卡拉提现结果通知的参数 + * @param request HTTP请求对象,包含拉卡拉提现结果通知的参数 + * @param merchantNo 商户号 (补偿必选) + * @param drawJnl 提现流水号 (补偿必选) * @return JSONObject 响应结果对象 */ @Override - public JSONObject ewalletWithDrawNotify(HttpServletRequest request) { + public JSONObject ewalletWithDrawNotify(HttpServletRequest request, String merchantNo, String drawJnl) { log.debug("[拉卡拉D1提现结果通知] 开始处理拉卡拉提现结果通知"); try { - // 1. 验签处理 - 验证通知来源的合法性 - Pair signCheckResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath, false); - if (!signCheckResult.getFirst()) { - log.warn("[拉卡拉D1提现结果通知] 验签失败: {}", signCheckResult.getSecond()); - return JSONUtil.createObj() - .set("code", "FAIL") - .set("message", signCheckResult.getSecond()); + JSONObject paramsJSON; + Pair signCheckResult = null; + if (request != null) { + // 1. 验签处理 - 验证通知来源的合法性 + signCheckResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath, false); + if (signCheckResult != null && !signCheckResult.getFirst()) { + log.warn("[拉卡拉D1提现结果通知] 验签失败: {}", signCheckResult.getSecond()); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", signCheckResult.getSecond()); + } + + // 2. 解析回调参数 + paramsJSON = JSONUtil.parseObj(signCheckResult.getSecond()); + } else { + // 补偿机制。主动从拉卡拉上获取提现结果 + paramsJSON = ewalletWithdrawQuery(merchantNo, drawJnl); } - // 2. 解析回调参数 - JSONObject paramsJSON = JSONUtil.parseObj(signCheckResult.getSecond()); - if (paramsJSON == null) { + + if (JSONUtil.isNull(paramsJSON)) { log.warn("[拉卡拉D1提现结果通知] 回调参数解析失败"); return JSONUtil.createObj() .set("code", "FAIL") @@ -3661,16 +3873,25 @@ public class LakalaApiServiceImpl implements LakalaApiService { String drawState = paramsJSON.getStr("drawState"); String mercId = paramsJSON.getStr("mercId"); String merOrderNo = paramsJSON.getStr("merOrderNo"); + String drawJnlRemote = paramsJSON.getStr("drawJnl"); - log.info("[拉卡拉D1提现结果通知] 提现通知参数: drawState={}, mercId={}, merOrderNo={}", drawState, mercId, merOrderNo); + log.info("[拉卡拉D1提现结果通知] 提现通知参数: drawState={}, mercId={}, merOrderNo={},drawJnlRemote={}", drawState, mercId, merOrderNo, drawJnlRemote); - if (StrUtil.hasBlank(mercId, merOrderNo, drawState)) { - log.warn("[拉卡拉提现结果通知] 回调参数缺失: drawState={}, mercId={}, merOrderNo={}", drawState, mercId, merOrderNo); + if (StrUtil.hasBlank(mercId, merOrderNo, drawJnl, drawState)) { + log.warn("[拉卡拉提现结果通知] 回调参数缺失: drawState={}, mercId={}, merOrderNo={}, drawJnlRemote={}", drawState, mercId, merOrderNo, drawJnlRemote); return JSONUtil.createObj() .set("code", "FAIL") .set("message", "回调参数错误"); } + Boolean isCompensate = lklOrderDrawService.isOrderDrawed(mercId, drawJnl); + if (isCompensate) { + log.warn("[拉卡拉D1提现结果通知] 提现结果通知已处理,忽略处理: mercId={}, merOrderNo={}, drawJnl={}", mercId, merOrderNo, drawJnl); + return JSONUtil.createObj() + .set("code", "SUCCESS") + .set("message", "提现结果通知已处理"); + } + // 只处理成功的提现状态 if (!"DRAW.SUCCESS".equals(drawState)) { log.debug("[拉卡拉D1提现结果通知] 提现状态未成功,忽略处理: drawState={}", drawState); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java index d676e913..a1a1606a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java @@ -2,6 +2,7 @@ package com.suisung.mall.shop.lakala.service.impl; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.modules.lakala.LklOrderDraw; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.lakala.mapper.LklOrderDrawMapper; @@ -9,6 +10,10 @@ import com.suisung.mall.shop.lakala.service.LklOrderDrawService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.Date; +import java.util.List; + @Slf4j @Service public class LklOrderDrawServiceImpl extends BaseServiceImpl implements LklOrderDrawService { @@ -103,6 +108,70 @@ public class LklOrderDrawServiceImpl extends BaseServiceImpl getUnSuccessDrawedList(Date beginDate, Date endDate, Integer page, Integer pageSize) { + // 1. 参数校验 + if (beginDate == null) { + log.warn("[分页查询未成功提现记录] 开始时间不能为空"); + return Collections.emptyList(); + } + + if (page == null || page < 1) { + log.warn("[分页查询未成功提现记录] 页码必须大于0,当前页码: {}", page); + return Collections.emptyList(); + } + + if (pageSize == null || pageSize <= 0) { + log.warn("[分页查询未成功提现记录] 页面大小必须大于0,当前大小: {}", pageSize); + return Collections.emptyList(); + } + + // 2. 处理结束时间默认值 + Date actualEndDate = (endDate != null) ? endDate : new Date(); + + // 3. 参数合理性校验 + if (beginDate.after(actualEndDate)) { + log.warn("[分页查询未成功提现记录] 开始时间{}不能晚于结束时间{}", beginDate, actualEndDate); + return Collections.emptyList(); + } + + try { + // 4. 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("id", "merc_id", "draw_jnl", "draw_state", "created_at") + .isNotNull("draw_jnl") + .isNotNull("merc_id") + .ne("draw_state", "DRAW.SUCCESS") + .ge("created_at", beginDate) + .le("created_at", actualEndDate) + .orderByDesc("id"); + + // 5. 执行分页查询 + Page resultPage = lists(queryWrapper, page, pageSize); + List result = resultPage.getRecords(); + + log.info("[分页查询未成功提现记录] 查询完成,开始时间={},结束时间={},页码={},页面大小={},结果数量={}", + beginDate, actualEndDate, page, pageSize, (result != null ? result.size() : 0)); + + return result != null ? result : Collections.emptyList(); + } catch (Exception e) { + log.error("[分页查询未成功提现记录] 查询过程中发生异常,开始时间={},结束时间={},页码={},页面大小={}", + beginDate, endDate, page, pageSize, e); + return Collections.emptyList(); + } + } + + /** * 判断订单是否已经提现完成 * @@ -121,7 +190,7 @@ public class LklOrderDrawServiceImpl extends BaseServiceImpl() .eq("order_id", orderId) - .eq("draw_state", "DRAW.SUCCESS") +// .eq("draw_state", "DRAW.SUCCESS") .orderByDesc("id")); // 根据查询结果判断是否已提现 @@ -137,4 +206,42 @@ public class LklOrderDrawServiceImpl extends BaseServiceImpl() + .eq("merc_id", merchantNo) + .eq("draw_jnl", drawJnl) + .eq("draw_state", "DRAW.SUCCESS") + .orderByDesc("id")); + + boolean isDrawed = lklOrderDraw != null; + + log.debug("[LklOrderDraw] 订单提现状态查询完成, merchantNo={}, drawJnl={}, isDrawed={}", + merchantNo, drawJnl, isDrawed); + + return isDrawed; + } catch (Exception e) { + log.error("[LklOrderDraw] 查询订单提现状态异常, merchantNo={}, drawJnl={}", merchantNo, drawJnl, e); + return false; + } + } + } From 72740a37c8d145a28176ec041f3f68c4ac0420e0 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Fri, 24 Oct 2025 15:05:52 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=BA=93=E5=AD=98=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/impl/ShopOrderReturnServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 2a7387e4..0afcbb35 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 @@ -1512,9 +1512,11 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl