From 9974c22c79674ba80c72ddd3dc8341482215be40 Mon Sep 17 00:00:00 2001 From: wangfukang <15630117759@163.com> Date: Wed, 11 Nov 2026 15:40:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=8B=BC=E5=9B=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hiver-admin/test-output/test-report.html | 16 +-- .../mall/controller/MallOrderController.java | 24 +++- .../cc/hiver/mall/mq/OrderDelayConsumer.java | 2 +- .../service/mybatis/MallOrderService.java | 7 +- .../mybatis/MallOrderGroupServiceImpl.java | 120 +++++++++++++----- .../mybatis/MallOrderServiceImpl.java | 112 ++++++++++------ .../mall/utils/DaQuReceiptRenderUtil.java | 27 +++- 7 files changed, 225 insertions(+), 83 deletions(-) diff --git a/hiver-admin/test-output/test-report.html b/hiver-admin/test-output/test-report.html index 702986d8..14b1cb3a 100644 --- a/hiver-admin/test-output/test-report.html +++ b/hiver-admin/test-output/test-report.html @@ -35,7 +35,7 @@ Hiver
  • -ʮһÔÂ 10, 2026 14:35:16 +ʮһÔÂ 11, 2026 00:26:01
  • @@ -84,7 +84,7 @@

    passTest

    -

    14:35:17 ÏÂÎç / 0.03 secs

    +

    00:26:02 ÉÏÎç / 0.033 secs

    @@ -92,9 +92,9 @@
    #test-id=1
    passTest
    -11.10.2026 14:35:17 -11.10.2026 14:35:17 -0.03 secs +11.11.2026 00:26:02 +11.11.2026 00:26:02 +0.033 secs
    @@ -104,7 +104,7 @@ Pass - 14:35:17 + 0:26:02 Test passed @@ -128,13 +128,13 @@

    Started

    -

    ʮһÔÂ 10, 2026 14:35:16

    +

    ʮһÔÂ 11, 2026 00:26:01

    Ended

    -

    ʮһÔÂ 10, 2026 14:35:17

    +

    ʮһÔÂ 11, 2026 00:26:02

    diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java index fe58f403..274c52d8 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java @@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; import java.util.List; import java.util.Map; @@ -239,15 +242,32 @@ public class MallOrderController { */ @PostMapping("/userRequireMake") @ApiOperation(value = "用户点击出餐") - public Result userRequireMake(@ApiParam("订单ID") @RequestParam String orderId) { + public Result userRequireMake(@ApiParam("订单ID") @RequestParam String orderId, + @ApiParam("预约用餐时间") @RequestParam(required = false) String userRequireMakeTime) { try { - mallOrderService.userRequireMake(orderId); + mallOrderService.userRequireMake(orderId, parseUserRequireMakeTime(userRequireMakeTime)); return ResultUtil.success("通知商家成功"); } catch (Exception e) { log.error("出餐失败: {}", e.getMessage(), e); return ResultUtil.error(e.getMessage()); } } + + private Date parseUserRequireMakeTime(String value) { + if (value == null || value.trim().isEmpty()) { + return null; + } + String text = value.trim().replace(' ', 'T'); + if (text.length() == 16) { + text = text + ":00"; + } + try { + return Date.from(LocalDateTime.parse(text).atZone(ZoneId.systemDefault()).toInstant()); + } catch (Exception e) { + throw new RuntimeException("用餐时间格式不正确"); + } + } + /** * 商家拒单 */ diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java index 218a64ce..e04a540c 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java @@ -275,7 +275,6 @@ public class OrderDelayConsumer { MallOrder order = mallOrderMapper.selectById(orderId); if (order == null || !Integer.valueOf(2).equals(order.getDeliveryType()) - || Integer.valueOf(1).equals(order.getUserRequireMake()) || order.getUserRequireMakeTime() == null || order.getShopMakeTime() != null || order.getStatus() == null @@ -288,6 +287,7 @@ public class OrderDelayConsumer { jPushService.sendPushNotification(shop.getClientId(), "您有一笔预约到店订单可以出餐啦!", orderId); } orderAsyncProducer.sendCloudPrint(orderId, "SHOP_APPOINTMENT_MAKE"); + mallOrderService.scheduleStorePickupCookTimeout(orderId); } private void handleTransferArriveTimeout(String orderId) { diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java index 3f3df6d0..08439f1b 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java @@ -85,7 +85,12 @@ public interface MallOrderService extends IService { /** * 用户要求出餐 */ - void userRequireMake(String orderId); + void userRequireMake(String orderId, java.util.Date userRequireMakeTime); + + /** + * 到店订单通知商家出餐后,按取餐时间和商家出餐时长设置出餐超时提醒。 + */ + void scheduleStorePickupCookTimeout(String orderId); void updateSaleCacheIncrementalViaMq(String shopId, String regionId, java.util.List items, Integer shopSaleCount); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java index ddf5b78f..9fbc34c0 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java @@ -161,13 +161,19 @@ public class MallOrderGroupServiceImpl extends ServiceImpl orderIdList = Arrays.asList(group.getGroupOrderIds().split(",")); + List orderIdList = parseGroupOrderIds(group); List goodsList = new ArrayList<>(); LambdaQueryWrapper oqw = new LambdaQueryWrapper<>(); - oqw.eq(MallOrder::getShopId, group.getShopId()) - .eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP) + oqw.eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP) .in(MallOrder::getId, orderIdList); - List waitingOrders = mallOrderService.list(oqw); + List waitingOrders = orderIdList.isEmpty() + ? new ArrayList<>() + : mallOrderService.list(oqw); + if (waitingOrders.size() < orderIdList.size()) { + log.warn("成团时有订单未处于待成团,groupId={},groupOrderIds={},命中={}", + groupId, group.getGroupOrderIds(), + waitingOrders.stream().map(MallOrder::getId).collect(java.util.stream.Collectors.toList())); + } boolean isFace2Face = group.getIsFace() != null && group.getIsFace() == 1; List deliveryOrders = new ArrayList<>(); @@ -177,9 +183,10 @@ public class MallOrderGroupServiceImpl extends ServiceImpl ouw = new LambdaUpdateWrapper<>(); + String latestSeq = null; //代表该拼团子订单订单选择到店 if(targetStatus == 3){ - String latestSeq = String.format("%s-%s", + latestSeq = String.format("%s-%s", LocalDate.now().format(DateTimeFormatter.ofPattern("MMdd")), merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2)); ouw.eq(MallOrder::getId, order.getId()) @@ -192,12 +199,21 @@ public class MallOrderGroupServiceImpl extends ServiceImpl 0) { - cookTimeMins = cachedCookTime; - } - } catch (Exception e) { - log.error("获取店铺出餐时间缓存异常: {}", e.getMessage()); + if (order == null || StringUtils.isBlank(order.getId())) { + return; + } + long delayMillis = resolveStorePickupCookTimeoutDelay(order); + log.info("到店出餐超时提醒 orderId={} userRequireMakeTime={} delayMillis={}", + order.getId(), order.getUserRequireMakeTime(), delayMillis); + orderAsyncProducer.sendDelayMessage(order.getId(), "Shop_Cook_Timeout", delayMillis); + } + + /** + * 超时提醒时刻 = 取餐时间 - 商家出餐时长。 + * 这个时刻已经过了时,从当前起按商家出餐时长计时,避免立刻判超时。 + * 若商家出餐时长长于通知后的剩余时间,则等通知后再给足出餐时长。 + */ + private long resolveStorePickupCookTimeoutDelay(MallOrder order) { + long cookMillis = 15L * 60 * 1000L; + try { + int cachedCookTime = safeMinutes(getCookingTime(order.getRegionId(), order.getShopId())); + if (cachedCookTime > 0) { + cookMillis = cachedCookTime * 60L * 1000L; } - orderAsyncProducer.sendDelayMessage(order.getId(), "Shop_Cook_Timeout", cookTimeMins * 60 * 1000L); + } catch (Exception e) { + log.error("获取店铺出餐时间缓存异常: {}", e.getMessage()); + } + long now = System.currentTimeMillis(); + Date pickupTime = order.getUserRequireMakeTime(); + if (pickupTime == null) { + return cookMillis; + } + long pickupAt = pickupTime.getTime(); + long startAt = Math.max(now, pickupAt - STORE_PICKUP_NOTIFY_BEFORE_MILLIS); + long timeoutAt = pickupAt - cookMillis; + if (timeoutAt <= now) { + timeoutAt = now + cookMillis; + } else { + timeoutAt = Math.max(timeoutAt, startAt + cookMillis); } + return Math.max(1000L, timeoutAt - now); } /** diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index 2f655974..649df517 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -805,7 +805,7 @@ public class MallOrderServiceImpl extends ServiceImpl guw = new LambdaUpdateWrapper<>(); guw.eq(MallOrderGroup::getId, group.getId()) .set(MallOrderGroup::getCurrentMembers, newMembers); - mallOrderGroupMapper.update(group,guw); + mallOrderGroupMapper.update(null, guw); //缓存更新 group.setCurrentMembers(newMembers); shopGroupOrderCacheUtil.update(group.getShopId(), group); @@ -816,7 +816,7 @@ public class MallOrderServiceImpl extends ServiceImpl guw = new LambdaUpdateWrapper<>(); guw.eq(MallOrderGroup::getId, group.getId()) .set(MallOrderGroup::getStatus, GROUP_STATUS_FORMING).set(MallOrderGroup::getCurrentMembers, newMembers); - mallOrderGroupMapper.update(group,guw); + mallOrderGroupMapper.update(null, guw); //普通拼团团长发起拼团进缓存 if(order.getOrderType() == ORDER_TYPE_GROUP){ group.setCurrentMembers(newMembers); @@ -831,7 +831,7 @@ public class MallOrderServiceImpl extends ServiceImpl guw = new LambdaUpdateWrapper<>(); guw.eq(MallOrderGroup::getId, group.getId()) .set(MallOrderGroup::getCurrentMembers, newMembers); - mallOrderGroupMapper.update(group,guw); + mallOrderGroupMapper.update(null, guw); } } if (group != null) { @@ -918,20 +918,51 @@ public class MallOrderServiceImpl extends ServiceImpl 0) { - cookTimeMins = cachedCookTime; - } - } catch (Exception e) { - log.error("获取店铺出餐时间缓存异常: {}", e.getMessage()); + if (order == null) { + return; + } + long delayMillis = resolveStorePickupCookTimeoutDelay(order); + log.info("到店出餐超时提醒 orderId={} userRequireMakeTime={} delayMillis={}", + orderId, order.getUserRequireMakeTime(), delayMillis); + orderAsyncProducer.sendDelayMessage(orderId, "Shop_Cook_Timeout", delayMillis); + } + + /** + * 超时提醒时刻 = 取餐时间 - 商家出餐时长。 + * 这个时刻已经过了时,从当前起按商家出餐时长计时,避免立刻判超时。 + * 若商家出餐时长长于通知后的剩余时间,则等通知后再给足出餐时长。 + */ + private long resolveStorePickupCookTimeoutDelay(MallOrder order) { + long cookMillis = 15L * 60 * 1000L; + try { + int cachedCookTime = safeMinutes(getCookingTime(order.getRegionId(), order.getShopId())); + if (cachedCookTime > 0) { + cookMillis = cachedCookTime * 60L * 1000L; } - orderAsyncProducer.sendDelayMessage(orderId, "Shop_Cook_Timeout", cookTimeMins * 60 * 1000L); + } catch (Exception e) { + log.error("获取店铺出餐时间缓存异常: {}", e.getMessage()); + } + long now = System.currentTimeMillis(); + Date pickupTime = order.getUserRequireMakeTime(); + if (pickupTime == null) { + return cookMillis; + } + long pickupAt = pickupTime.getTime(); + long startAt = Math.max(now, pickupAt - STORE_PICKUP_NOTIFY_BEFORE_MILLIS); + long timeoutAt = pickupAt - cookMillis; + if (timeoutAt <= now) { + timeoutAt = now + cookMillis; + } else { + timeoutAt = Math.max(timeoutAt, startAt + cookMillis); } + return Math.max(1000L, timeoutAt - now); } @Override @@ -1586,19 +1617,26 @@ public class MallOrderServiceImpl extends ServiceImpl uw = new LambdaUpdateWrapper<>(); - uw.eq(MallOrder::getId, orderId).set(MallOrder::getUserRequireMake,1); - this.update(uw); - Shop shop = getShopFromCache(order.getRegionId(), order.getShopId()); - if (shop != null && StringUtils.isNotBlank(shop.getClientId())) { - jPushService.sendPushNotification(shop.getClientId(), "您有一笔新的到店订单",orderId); + uw.eq(MallOrder::getId, orderId).set(MallOrder::getUserRequireMake, 1); + order.setUserRequireMake(1); + if (userRequireMakeTime != null) { + uw.set(MallOrder::getUserRequireMakeTime, userRequireMakeTime); + order.setUserRequireMakeTime(userRequireMakeTime); } - orderAsyncProducer.sendCloudPrint(orderId, "SHOP_INSTANT_MAKE"); - - // 触发商家出餐超时监听 - triggerShopCookTimeoutEvent(orderId); + this.update(uw); + notifyOrScheduleStorePickupMake(order); } @Override @@ -2146,6 +2184,7 @@ public class MallOrderServiceImpl extends ServiceImpl