From 4ff892d09b863be70d525f8ecfe2afbccd401e05 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 25 Oct 2025 16:18:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E7=BA=A6=E8=AE=A2=E5=8D=95=E6=A7=BD?= =?UTF-8?q?=E7=82=B9=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/pojo/dto/BookingArgDTO.java | 78 +++++++ .../mall/common/utils/DateTimeUtils.java | 18 ++ .../mobile/UserOrderController.java | 8 + .../order/service/ShopOrderInfoService.java | 10 + .../impl/ShopOrderInfoServiceImpl.java | 200 +++++++++++++++++- 5 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/pojo/dto/BookingArgDTO.java diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/BookingArgDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/BookingArgDTO.java new file mode 100644 index 00000000..8b0457db --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/BookingArgDTO.java @@ -0,0 +1,78 @@ +package com.suisung.mall.common.pojo.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 预订参数DTO实体类 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@ApiModel(value = "预约订单参数DTO", description = "预约订单参数DTO") +public class BookingArgDTO { + + /** + * 日期标题(如"今天(周二)") + */ + @ApiModelProperty(value = "日期标题") + private String date_title; + + /** + * 日期字符串(如"01月09日") + */ + @ApiModelProperty(value = "日期字符串") + private String date_str; + + /** + * 日期(如"2025-01-09") + */ + @ApiModelProperty(value = "日期") + private String date; + + /** + * 时间项列表 + */ + @ApiModelProperty(value = "时间项列表") + private List items; + + /** + * 预订时间项实体类 + */ + @Data + public static class BookingArgItem { + + /** + * 时间标题(如"立即送出"或"09:15-09:45") + */ + @ApiModelProperty(value = "时间标题") + private String time_title; + + /** + * 预订时间戳 + */ + @ApiModelProperty(value = "预订时间戳") + private Long booking_at; + + /** + * 预订状态 + */ + @ApiModelProperty(value = "预订状态:1-立即下单;2-预约下单;") + private Integer booking_state; + + /** + * 预订开始时间 + */ + @ApiModelProperty(value = "预订开始时间") + private String booking_begin_time; + + /** + * 预订结束时间 + */ + @ApiModelProperty(value = "预订结束时间") + private String booking_end_time; + } +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java index f6f0de8e..c94b058c 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java @@ -178,6 +178,23 @@ public class DateTimeUtils { } } + /** + * 根据日期获取星期几 + * + * @param date 日期 + * @return 星期几 + */ + public static String getWeekOfDate(Date date) { + String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) { + w = 0; + } + return weekDays[w]; + } + /** * 将多种日期格式转换为 yyyy-MM-dd @@ -473,6 +490,7 @@ public class DateTimeUtils { } } + /** * 判断时间字符串是否与格式模式匹配 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java index d08db1b1..6935b3a7 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java @@ -12,6 +12,7 @@ import com.suisung.mall.common.api.StateCode; import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.modules.order.*; +import com.suisung.mall.common.pojo.dto.BookingArgDTO; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.shop.order.service.*; @@ -321,6 +322,13 @@ public class UserOrderController extends BaseControllerImpl { return CommonResult.success(data); } + @ApiOperation(value = "可预约订单的时间槽", notes = "可预约订单的时间槽") + @RequestMapping(value = "/booking_time_args", method = RequestMethod.GET) + public CommonResult listInvoice(@RequestParam(name = "store_id", defaultValue = "0") Integer store_id) { + List list = shopOrderInfoService.genBookingOrderArgList(store_id); + return CommonResult.success(list); + } + } 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 478a5e93..9f400d2a 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 @@ -2,6 +2,7 @@ package com.suisung.mall.shop.order.service; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.order.ShopOrderInfo; +import com.suisung.mall.common.pojo.dto.BookingArgDTO; import com.suisung.mall.core.web.service.IBaseService; import org.springframework.data.util.Pair; @@ -133,4 +134,13 @@ public interface ShopOrderInfoService extends IBaseService { * @return */ Pair runBookingOrder2SfExpressOrder(String orderId); + + + /** + * 根据店铺的营业时间范围生成可预约下单的参数 + * + * @param storeId 店铺ID + * @return + */ + List genBookingOrderArgList(Integer storeId); } 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 26eec971..fe5b78be 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 @@ -26,6 +26,7 @@ import com.suisung.mall.common.modules.pay.PayPlantformResource; import com.suisung.mall.common.modules.plantform.ShopPlantformFeedback; import com.suisung.mall.common.modules.product.ShopProductComment; import com.suisung.mall.common.modules.store.ShopStoreInfo; +import com.suisung.mall.common.pojo.dto.BookingArgDTO; import com.suisung.mall.common.pojo.res.ThirdApiRes; import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.common.utils.CommonUtil; @@ -137,6 +138,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl getAutoCancelOrderIdByPage(Integer pageNum, Integer pageSize) { QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -1014,5 +1014,201 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl genBookingOrderArgList(Integer storeId) { + // 初始化默认营业时间 + String openingHours = "09:00"; + String closeHours = "18:00"; + + // 如果storeId不为空,则尝试获取店铺信息 + if (storeId != null) { + try { + ShopStoreInfo shopStoreInfo = shopStoreInfoService.getShopStoreInfoByStoreId(storeId); + if (shopStoreInfo != null) { + // 检查并使用店铺设置的营业时间 + if (StrUtil.isNotBlank(shopStoreInfo.getStore_opening_hours())) { + openingHours = shopStoreInfo.getStore_opening_hours(); + } + if (StrUtil.isNotBlank(shopStoreInfo.getStore_close_hours())) { + closeHours = shopStoreInfo.getStore_close_hours(); + } + logger.debug("[生成预约参数] 使用店铺营业时间,storeId: {}, opening: {}, close: {}", storeId, openingHours, closeHours); + } else { + logger.warn("[生成预约参数] 未找到店铺信息,使用默认营业时间,storeId: {}", storeId); + } + } catch (Exception e) { + logger.error("[生成预约参数] 获取店铺信息异常,使用默认营业时间,storeId: {}", storeId, e); + } + } else { + logger.warn("[生成预约参数] 店铺ID为空,使用默认营业时间"); + } + + List result = new ArrayList<>(); + + // 生成未来7天的数据 + Date now = new Date(); + for (int i = 0; i < 7; i++) { + Date currentDate = DateUtil.offsetDay(now, i); + BookingArgDTO bookingArgDTO = new BookingArgDTO(); + + // 设置日期相关信息 + String dateStr = DateUtil.format(currentDate, "yyyy-MM-dd"); + String displayDateStr = DateUtil.format(currentDate, "MM月dd日"); + + // 安全获取星期信息 + String weekStr = "未知"; + try { + weekStr = DateTimeUtils.getWeekOfDate(currentDate); + } catch (Exception e) { + logger.warn("[生成预约参数] 获取星期信息异常,使用默认值,date: {}", dateStr); + } + + // 设置date_title + if (i == 0) { + bookingArgDTO.setDate_title("今天(" + weekStr + ")"); + } else if (i == 1) { + bookingArgDTO.setDate_title("明天(" + weekStr + ")"); + } else { + bookingArgDTO.setDate_title(displayDateStr + "(" + weekStr + ")"); + } + + bookingArgDTO.setDate_str(displayDateStr); + bookingArgDTO.setDate(dateStr); + + // 生成时间项 + List items = generateTimeSlots(dateStr, openingHours, closeHours, i == 0); + bookingArgDTO.setItems(items != null ? items : new ArrayList<>()); + + result.add(bookingArgDTO); + } + + logger.debug("[生成预约参数] 成功生成预约参数,storeId: {}, opening: {}, close: {}, 参数数量: {}", + storeId, openingHours, closeHours, result.size()); + return result; + } + + + /** + * 生成时间槽列表 + * + * @param dateStr 日期字符串 yyyy-MM-dd + * @param openingHours 开始营业时间 HH:mm + * @param closeHours 结束营业时间 HH:mm + * @param isToday 是否是今天 + * @return 时间槽列表 + */ + private List generateTimeSlots(String dateStr, String openingHours, String closeHours, boolean isToday) { + // 参数校验 + if (StrUtil.isBlank(dateStr) || StrUtil.isBlank(openingHours) || StrUtil.isBlank(closeHours)) { + logger.warn("[生成时间槽] 参数为空,dateStr: {}, openingHours: {}, closeHours: {}", dateStr, openingHours, closeHours); + return Collections.emptyList(); + } + + try { + List items = new ArrayList<>(); + + // 添加"立即送出"选项(仅限今天) + if (isToday) { + BookingArgDTO.BookingArgItem immediateItem = new BookingArgDTO.BookingArgItem(); + immediateItem.setTime_title("立即送出"); + immediateItem.setBooking_at(0L); + immediateItem.setBooking_state(1); + immediateItem.setBooking_begin_time(""); + immediateItem.setBooking_end_time(""); + items.add(immediateItem); + } + + // 解析营业时间 + Date openTime = DateUtil.parse(dateStr + " " + openingHours + ":00", "yyyy-MM-dd HH:mm:ss"); + Date closeTime = DateUtil.parse(dateStr + " " + closeHours + ":00", "yyyy-MM-dd HH:mm:ss"); + + // 如果结束时间早于开始时间,说明跨天了,需要调整到第二天 + if (closeTime.before(openTime)) { + closeTime = DateUtil.offsetDay(closeTime, 1); + } + + // 验证营业时间有效性 + if (openTime.after(closeTime)) { + logger.warn("[生成时间槽] 营业时间无效,openTime: {}, closeTime: {}", openTime, closeTime); + return items; + } + + // 对于今天,需要特殊处理:从当前时间+5分钟开始生成时间槽 + Date startTime = openTime; + if (isToday) { + // 当前时间+5分钟作为第二个时间槽的开始时间 + Date nowPlusFive = DateUtil.offsetMinute(new Date(), 5); + // 如果当前时间+5分钟在营业时间范围内,则从该时间开始 + if (nowPlusFive.after(openTime) && nowPlusFive.before(closeTime)) { + startTime = nowPlusFive; + } + // 如果当前时间+5分钟已经超过营业结束时间,则不生成后续时间槽 + else if (nowPlusFive.after(closeTime)) { + logger.debug("[生成时间槽] 当前时间+5分钟已超过营业结束时间,不生成后续时间槽"); + return items; + } + } + + // 生成30分钟间隔的时间段 + Date currentTimeSlot = startTime; + int slotCount = 0; + final int MAX_SLOTS = 48; // 最多生成48个时间段,防止异常循环 + + while (currentTimeSlot.before(closeTime) && slotCount < MAX_SLOTS) { + Date endTimeSlot = DateUtil.offsetMinute(currentTimeSlot, 30); + + // 如果结束时间超过了营业结束时间,则使用营业结束时间 + if (endTimeSlot.after(closeTime)) { + endTimeSlot = closeTime; + } + + // 创建时间段项(跳过"立即送出"项,因为已经添加过了) + if (!(isToday && slotCount == 0 && items.size() > 0 && "立即送出".equals(items.get(0).getTime_title()))) { + BookingArgDTO.BookingArgItem timeItem = new BookingArgDTO.BookingArgItem(); + String beginTimeStr = DateUtil.format(currentTimeSlot, "HH:mm"); + String endTimeStr = DateUtil.format(endTimeSlot, "HH:mm"); + + timeItem.setTime_title(beginTimeStr + "-" + endTimeStr); + + // 修正时间戳计算 + long bookingAt = currentTimeSlot.getTime() / 1000; + timeItem.setBooking_at(bookingAt); + + timeItem.setBooking_state(2); + timeItem.setBooking_begin_time(DateUtil.format(currentTimeSlot, "yyyy-MM-dd HH:mm:ss")); + timeItem.setBooking_end_time(DateUtil.format(DateUtil.offsetMinute(currentTimeSlot, 40), "yyyy-MM-dd HH:mm:ss")); + + items.add(timeItem); + } + + // 移动到下一个时间段 + currentTimeSlot = endTimeSlot; + slotCount++; + + // 防止时间相同导致的死循环 + if (currentTimeSlot.equals(startTime)) { + logger.warn("[生成时间槽] 检测到时间循环,跳出循环"); + break; + } + } + + if (slotCount >= MAX_SLOTS) { + logger.warn("[生成时间槽] 时间段数量超过最大限制,date: {}", dateStr); + } + + logger.debug("[生成时间槽] 成功生成时间槽,date: {}, 时间段数量: {}", dateStr, items.size()); + return items; + } catch (Exception e) { + logger.error("[生成时间槽] 生成时间槽异常,date: {}, openingHours: {}, closeHours: {}", dateStr, openingHours, closeHours, e); + return Collections.emptyList(); + } + } + }