From 38b674e05a06a4f30a319894337c29fc8616397b Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 6 Nov 2025 12:38:06 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A0=8D=E4=BB=B7=E6=B4=BB=E5=8A=A8=20?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=A7=BD=20=E4=BF=AE=E6=AD=A3=E6=96=B0?= =?UTF-8?q?=E9=9C=80=E6=B1=82=E3=80=82=20=E5=A2=9E=E5=8A=A0=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/ShopActivityCutprice.java | 3 + .../modules/store/ShopStoreActivityBase.java | 8 + .../mall/common/utils/DateTimeUtils.java | 44 ++- .../impl/ShopOrderInfoServiceImpl.java | 267 ++++++++---------- .../impl/ShopStoreBaseServiceImpl.java | 4 +- .../activity/ShopActivityCutpriceMapper.xml | 2 +- .../store/ShopStoreActivityBaseMapper.xml | 3 +- 7 files changed, 158 insertions(+), 173 deletions(-) diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java b/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java index 10d27744..11e4a8e7 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java @@ -56,6 +56,9 @@ public class ShopActivityCutprice implements Serializable { @ApiModelProperty(value = "砍价人数") private Integer ac_num; + @ApiModelProperty(value = "砍价过期时间戳") + private Long expired_at; + @Version @ApiModelProperty(value = "乐观锁") private Integer version; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java index af686bad..fd127abf 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java @@ -127,4 +127,12 @@ public class ShopStoreActivityBase implements Serializable { @TableField(updateStrategy = NOT_EMPTY) private String flow_no; + @Version + @ApiModelProperty(value = "在活动时间范围内,用户砍价允许的天数") + private Integer cut_days; + + @Version + @ApiModelProperty(value = "活动商品的总数量") + private Integer product_cont; + } 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 490c174f..6f559ab4 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 @@ -439,15 +439,15 @@ public class DateTimeUtils { * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param currentTime 要判断的时间点 - * @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程 + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) { + public static int isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) { try { // 参数校验 if (startTimeStr == null || endTimeStr == null || currentTime == null) { log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: {}", startTimeStr, endTimeStr, currentTime); - return false; + return -1; } // 解析开始时间 - 支持多种时间格式 @@ -459,37 +459,53 @@ public class DateTimeUtils { // 获取当前时间的时间部分 LocalTime nowTime = currentTime.toLocalTime(); + // 处理开始时间等于结束时间的情况 + if (startTime.equals(endTime)) { + return nowTime.equals(startTime) ? 0 : -1; // 精确匹配为时间段内,否则为时间段前 + } + // 处理跨天情况,例如 22:00 - 06:00 if (endTime.isBefore(startTime)) { // 如果结束时间小于开始时间,说明跨越了午夜 - // 当前时间需要满足:大于等于开始时间 或者 小于等于结束时间 - return !nowTime.isBefore(startTime) || !nowTime.isAfter(endTime); + // 跨天时间段: [startTime, 24:00) ∪ [00:00, endTime] + if (!nowTime.isBefore(startTime) || !nowTime.isAfter(endTime)) { + return 0; // 时间段内 + } else { + // 对于跨天情况,不在时间段内的唯一可能就是"时间段前" + // 因为跨天时间段覆盖了从startTime到endTime经过午夜的整个区间 + return -1; // 时间段前 + } } else { // 正常情况(不跨天),当前时间需要在开始时间和结束时间之间(包含边界) - return !nowTime.isBefore(startTime) && !nowTime.isAfter(endTime); + if (nowTime.isBefore(startTime)) { + return -1; // 时间段前 + } else if (nowTime.isAfter(endTime)) { + return 1; // 时间段后 + } else { + return 0; // 时间段内 + } } } catch (Exception e) { // 捕获解析异常,记录日志并返回false,避免影响主流程 log.error("判断时间是否在范围内时发生异常,startTimeStr: {}, endTimeStr: {}, currentTime: {}", startTimeStr, endTimeStr, currentTime, e); - return false; + return -1; } } - - + /** * 判断指定时间是否在两个时间点之间(包含边界) * * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param currentTime 要判断的时间点 - * @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程 + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) { + public static int isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) { if (currentTime == null) { log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: null", startTimeStr, endTimeStr); - return false; + return -1; } // 将Date转换为LocalDateTime并调用已有的方法 LocalDateTime localDateTime = currentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); @@ -501,9 +517,9 @@ public class DateTimeUtils { * * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 - * @return 如果在时间段内返回true,否则返回false + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isCurrentTimeInRange(String startTimeStr, String endTimeStr) { + public static int isCurrentTimeInRange(String startTimeStr, String endTimeStr) { return isTimeInRange(startTimeStr, endTimeStr, LocalDateTime.now()); } 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 28eef942..5ed4d0db 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 @@ -902,7 +902,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl result = new ArrayList<>(); // 判断今天还能不能立即下单和预约下单?能:就有今天的时间槽;不能:就没有今天的时间槽 - boolean canBookingToday = DateTimeUtils.isTimeInRange(timesPair.getFirst(), timesPair.getSecond(), new Date()); + //-1-在时间段之前 0-时间段内,1-在时间段之后 + int inRangeVal = DateTimeUtils.isCurrentTimeInRange(timesPair.getFirst(), timesPair.getSecond()); - // 生成7天的可预约时间槽数据 - Date startDate = new Date(); - if (!canBookingToday) { - // 如果今天不能下单和预约,则从明天开始生成时间槽 - startDate = DateUtil.offsetDay(new Date(), 1); - } + // 确定起始日期 + Date startDate = inRangeVal == 1 ? DateUtil.offsetDay(new Date(), 1) : new Date(); + boolean isTodayAvailable = inRangeVal <= 0; // 今天是否可预约(在营业时间之前或之中) for (int i = 0; i < daysCnt; i++) { - startDate = DateUtil.offsetDay(startDate, i); + Date currentDate = DateUtil.offsetDay(startDate, i); BookingArgDTO bookingArgDTO = new BookingArgDTO(); // 设置日期相关信息 - String dateStr = DateUtil.format(startDate, "yyyy-MM-dd"); - String displayDateStr = DateUtil.format(startDate, "MM月dd日"); + String dateStr = DateUtil.format(currentDate, "yyyy-MM-dd"); + String displayDateStr = DateUtil.format(currentDate, "MM月dd日"); // 安全获取星期信息 String weekStr = "星期"; try { - weekStr = DateTimeUtils.getWeekOfDate(startDate); + weekStr = DateTimeUtils.getWeekOfDate(currentDate); } catch (Exception e) { logger.warn("[生成预约参数] 获取星期信息异常,使用默认值,date: {}", dateStr); } - // 设置date_title - 优化后的代码 + // 设置date_title String dateTitle; if (i == 0) { - dateTitle = canBookingToday ? "今天" : "明天"; + dateTitle = isTodayAvailable ? "今天" : "明天"; } else if (i == 1) { - dateTitle = canBookingToday ? "明天" : "后天"; + dateTitle = isTodayAvailable ? "后天" : "明天"; } else { dateTitle = displayDateStr; } - bookingArgDTO.setDate_title(dateTitle + "(" + weekStr + ")"); + bookingArgDTO.setDate_title(dateTitle + "(" + weekStr + ")"); bookingArgDTO.setDate_str(displayDateStr); bookingArgDTO.setDate(dateStr); - // 生成时间项 -// List items = new ArrayList<>(); - - // 如果是今天且在配送时间内,添加"立即送出"选项 -// if (i == 0 && canBookingToday) { -// 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); -// } - - String startTimeStr = timesPair.getFirst(); String endTimeStr = timesPair.getSecond(); - boolean isToday = i == 0 && canBookingToday; - // 生成时间项 - List items = generateTimeSlots(dateStr, startTimeStr, endTimeStr, isToday); -// if (i == 0 && canBookingToday) { -// items.addAll(timeSlots.stream().filter(item -> item.getBooking_state() != 1).collect(Collectors.toList())); -// } else { -// items.addAll(timeSlots); -// } - bookingArgDTO.setWorking_hours(String.format("%s-%s", startTimeStr, endTimeStr)); + + // 生成时间槽 + List items = new ArrayList<>(); + + // 参数校验 + if (StrUtil.isNotBlank(dateStr) && StrUtil.isNotBlank(startTimeStr) && StrUtil.isNotBlank(endTimeStr)) { + try { + // 解析营业时间 + Date openTime = DateUtil.parse(dateStr + " " + startTimeStr + ":00", "yyyy-MM-dd HH:mm:ss"); + Date closeTime = DateUtil.parse(dateStr + " " + endTimeStr + ":00", "yyyy-MM-dd HH:mm:ss"); + + // 验证营业时间有效性 + if (!openTime.after(closeTime)) { + // 在营业时间段内,且是今天,添加"立即送出"选项 + if (i == 0 && isTodayAvailable && inRangeVal == 0) { + 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 startTime = openTime; + if (i == 0 && isTodayAvailable && inRangeVal <= 0) { + // 当前时间+50分钟作为时间槽的开始时间 + Date nowPlusFifty = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER); + + // 确保开始时间在营业时间范围内 + if (nowPlusFifty.after(openTime)) { + if (nowPlusFifty.before(closeTime)) { + // 如果当前时间+50分钟在营业时间范围内,则从该时间开始 + startTime = nowPlusFifty; + logger.debug("[生成时间槽] 当前时间+50分钟在营业时间范围内,则从该时间开始"); + } else { + // 如果当前时间+50分钟已经超过营业结束时间,则不生成后续时间槽 + logger.debug("[生成时间槽] 当前时间+50分钟已超过营业结束时间,不生成后续时间槽"); + startTime = null; // 标记为不生成时间槽 + } + } + } + + // 生成时间槽 + if (startTime != null) { + Date currentTimeSlot = startTime; + int slotCount = 0; + final int MAX_SLOTS = 96; // 最多生成96个时间段,防止异常循环 + + while (currentTimeSlot.before(closeTime) && slotCount < MAX_SLOTS) { + Date endTimeSlot = DateUtil.offsetMinute(currentTimeSlot, 15); + + // 如果结束时间超过了营业结束时间,则使用营业结束时间 + if (endTimeSlot.after(closeTime)) { + endTimeSlot = closeTime; + } + + // 创建时间段项 + BookingArgDTO.BookingArgItem timeItem = new BookingArgDTO.BookingArgItem(); + String beginTimeStr = DateUtil.format(currentTimeSlot, "HH:mm"); + String endTimeStrItem = DateUtil.format(endTimeSlot, "HH:mm"); + + timeItem.setTime_title(beginTimeStr + "-" + endTimeStrItem); + + // 时间戳计算(秒) + 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(endTimeSlot, "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); + } + } + } else { + logger.warn("[生成时间槽] 营业时间无效,openTime: {}, closeTime: {}", openTime, closeTime); + } + } catch (Exception e) { + logger.error("[生成时间槽] 生成时间槽异常,date: {}, openingHours: {}, closeHours: {}", dateStr, startTimeStr, endTimeStr, e); + } + } + bookingArgDTO.setItems(items); result.add(bookingArgDTO); } @@ -1118,119 +1188,6 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl 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 (openTime.after(closeTime)) { - logger.warn("[生成时间槽] 营业时间无效,openTime: {}, closeTime: {}", openTime, closeTime); - return items; - } - - // 时间槽间隔(分钟) - int slotInterval = 15; - - // 对于今天,需要特殊处理:第二个时间槽从当前时间+50分钟开始 - Date startTime = openTime; - if (isToday) { - // 当前时间+50分钟作为第二个时间槽的开始时间 - Date nowPlusFifty = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER); - - // 如果当前时间+50分钟在营业时间范围内,则从该时间开始 - if (nowPlusFifty.after(openTime) && nowPlusFifty.before(closeTime)) { - startTime = nowPlusFifty; - } - // 如果当前时间+50分钟已经超过营业结束时间,则不生成后续时间槽 - else if (nowPlusFifty.after(closeTime)) { - logger.debug("[生成时间槽] 当前时间+50分钟已超过营业结束时间,不生成后续时间槽"); - return items; - } - } - - // 生成时间槽 - Date currentTimeSlot = startTime; - int slotCount = 0; - final int MAX_SLOTS = 48; // 最多生成48个时间段,防止异常循环 - - while (currentTimeSlot.before(closeTime) && slotCount < MAX_SLOTS) { - Date endTimeSlot = DateUtil.offsetMinute(currentTimeSlot, slotInterval); - - // 如果结束时间超过了营业结束时间,则使用营业结束时间 - if (endTimeSlot.after(closeTime)) { - endTimeSlot = closeTime; - } - - // 创建时间段项 - 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(endTimeSlot, "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(); - } - } - /** * 根据 storeIds(一个或多个 storeid,如 34,23,43,23,),先对id去重, * 再获取多个店铺的营业时间 List 列表list diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java index 9b5667f9..559bbde2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java @@ -4217,7 +4217,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl - ac_id, activity_id, user_id, ac_sale_price, ac_datetime, order_id, ac_mix_limit_price, ac_num + ac_id, activity_id, user_id, ac_sale_price, ac_datetime, order_id, ac_mix_limit_price, ac_num, expired_at, version diff --git a/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml b/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml index a91d358a..87ba1f85 100644 --- a/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml @@ -6,7 +6,8 @@ activity_id, store_id, user_id, activity_name, activity_title, activity_remark, activity_combo_id, activity_type_id, activity_starttime, activity_endtime, activity_state, activity_rule, activity_type, - activity_order, activity_is_finish, item_id, subsite_id + activity_order, activity_is_finish, item_id, subsite_id,activity_use_level, activity_releasetime, + activity_on_is_off, activity_share_num, version, flow_no, cut_days, product_cont