时间槽调整

This commit is contained in:
Jack 2025-11-06 00:57:46 +08:00
parent ea0afa5bdb
commit 2ab4495920
6 changed files with 125 additions and 97 deletions

View File

@ -83,6 +83,9 @@ public class ShopStoreBase implements Serializable {
@ApiModelProperty(value = "店铺营业状态1-营业中2-已打烊3-开业(活动)筹备中;") @ApiModelProperty(value = "店铺营业状态1-营业中2-已打烊3-开业(活动)筹备中;")
private Integer store_biz_state; private Integer store_biz_state;
@ApiModelProperty(value = "开业(活动)筹备具体时间 yyyy-MM-dd HH:mm:ss")
private Date store_biz_opening_dtime;
@ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑") @ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑")
private Integer shop_parent_id; private Integer shop_parent_id;

View File

@ -1,5 +1,6 @@
package com.suisung.mall.common.utils; package com.suisung.mall.common.utils;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
@ -10,7 +11,10 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.util.*; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Slf4j @Slf4j
public class DateTimeUtils { public class DateTimeUtils {
@ -375,11 +379,11 @@ public class DateTimeUtils {
* endTimeStr 结束时间字符串支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS * endTimeStr 结束时间字符串支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS
* @return 返回一个Map包含交集的时间段(startTimeStr和endTimeStr)如果无交集则返回空Map * @return 返回一个Map包含交集的时间段(startTimeStr和endTimeStr)如果无交集则返回空Map
*/ */
public static Map<String, String> findTimeInterSection(List<Map<String, String>> timeList) { public static Pair<String, String> findTimeInterSection(List<Pair<String, String>> timeList) {
// 参数校验 // 参数校验
if (timeList == null || timeList.isEmpty()) { if (timeList == null || timeList.isEmpty()) {
log.warn("时间段列表为空或null无法计算交集"); log.warn("时间段列表为空或null无法计算交集");
return new HashMap<>(); return null;
} }
try { try {
@ -387,14 +391,14 @@ public class DateTimeUtils {
LocalTime earliestEndTime = null; LocalTime earliestEndTime = null;
// 遍历所有时间段 // 遍历所有时间段
for (Map<String, String> timeMap : timeList) { for (Pair<String, String> timeMap : timeList) {
if (timeMap == null || !timeMap.containsKey("startTimeStr") || !timeMap.containsKey("endTimeStr")) { if (timeMap == null || StrUtil.hasBlank(timeMap.getFirst(), timeMap.getSecond())) {
log.warn("时间段数据不完整或格式不正确: {}", timeMap); log.warn("时间段数据不完整或格式不正确: {}", timeMap);
continue; continue;
} }
String startTimeStr = timeMap.get("startTimeStr"); String startTimeStr = timeMap.getFirst();
String endTimeStr = timeMap.get("endTimeStr"); String endTimeStr = timeMap.getSecond();
if (startTimeStr == null || endTimeStr == null) { if (startTimeStr == null || endTimeStr == null) {
log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr); log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr);
@ -416,18 +420,15 @@ public class DateTimeUtils {
// 检查是否存在交集 // 检查是否存在交集
if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) { if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) {
Map<String, String> result = new HashMap<>(); return Pair.of(latestStartTime.toString(), earliestEndTime.toString());
result.put("startTimeStr", latestStartTime.toString());
result.put("endTimeStr", earliestEndTime.toString());
return result;
} else { } else {
// 无交集情况 // 无交集情况
log.debug("给定的时间段列表无交集"); log.debug("给定的时间段列表无交集");
return new HashMap<>(); return null;
} }
} catch (Exception e) { } catch (Exception e) {
log.error("计算时间段交集时发生异常", e); log.error("计算时间段交集时发生异常", e);
return new HashMap<>(); return null;
} }
} }
@ -619,38 +620,24 @@ public class DateTimeUtils {
System.out.println("=== 测试 findTimeIntersection ==="); System.out.println("=== 测试 findTimeIntersection ===");
// 测试正常交集情况 // 测试正常交集情况
List<Map<String, String>> timeList1 = new ArrayList<>(); List<Pair<String, String>> timeList1 = new ArrayList<>();
Map<String, String> range1 = new HashMap<>(); timeList1.add(Pair.of("06:00", "17:00"));
range1.put("startTimeStr", "06:00"); timeList1.add(Pair.of("10:00", "18:00"));
range1.put("endTimeStr", "17:00");
timeList1.add(range1);
Map<String, String> range2 = new HashMap<>(); Pair<String, String> intersection1 = findTimeInterSection(timeList1);
range2.put("startTimeStr", "06:00");
range2.put("endTimeStr", "17:00");
timeList1.add(range2);
Map<String, String> intersection1 = findTimeInterSection(timeList1);
System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00 System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
// 测试无交集情况 // 测试无交集情况
List<Map<String, String>> timeList2 = new ArrayList<>(); List<Pair<String, String>> timeList2 = new ArrayList<>();
Map<String, String> range3 = new HashMap<>(); timeList2.add(Pair.of("09:00", "12:00"));
range3.put("startTimeStr", "09:00"); timeList2.add(Pair.of("13:00", "17:00"));
range3.put("endTimeStr", "12:00");
timeList2.add(range3);
Map<String, String> range4 = new HashMap<>(); Pair<String, String> intersection2 = findTimeInterSection(timeList2);
range4.put("startTimeStr", "13:00"); System.out.println("交集结果2: " + intersection2); // 应该是null
range4.put("endTimeStr", "17:00");
timeList2.add(range4);
Map<String, String> intersection2 = findTimeInterSection(timeList2); // 测试空列表
System.out.println("交集结果2: " + intersection2); // 应该是空Map Pair<String, String> intersection3 = findTimeInterSection(null);
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null
// 测试空列表
Map<String, String> intersection3 = findTimeInterSection(null);
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是空Map
} }

View File

@ -324,7 +324,7 @@ public class UserOrderController extends BaseControllerImpl {
@ApiOperation(value = "可预约订单的时间槽", notes = "结算中心页面,可预约订单的时间槽") @ApiOperation(value = "可预约订单的时间槽", notes = "结算中心页面,可预约订单的时间槽")
@RequestMapping(value = "/booking_time_args", method = RequestMethod.GET) @RequestMapping(value = "/booking_time_args", method = RequestMethod.GET)
public CommonResult listInvoice(@RequestParam(name = "store_ids", defaultValue = "") String store_ids) { public CommonResult genBookingOrderArgList(@RequestParam(name = "store_ids", defaultValue = "") String store_ids) {
List<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_ids); List<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_ids);
return CommonResult.success(list); return CommonResult.success(list);
} }

View File

@ -1016,93 +1016,104 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
*/ */
@Override @Override
public List<BookingArgDTO> genBookingOrderArgList(String storeIds) { public List<BookingArgDTO> genBookingOrderArgList(String storeIds) {
// 初始化默认营业时间 // 初始化营业时间对象
Map<String, String> timesMap = new HashMap<>(); Pair<String, String> timesPair = null;
// 如果storeId不为空则尝试获取店铺信息 // 如果storeId不为空则尝试获取店铺信息
if (StrUtil.isNotBlank(storeIds)) { if (StrUtil.isNotBlank(storeIds)) {
List<Map<String, String>> timesMapList = selStoreBizTimeMapList(storeIds); // 多个店铺的营业时间段集合
List<Pair<String, String>> timesMapList = selStoreBizTimeMapList(storeIds);
if (!CollUtil.isEmpty(timesMapList)) { if (!CollUtil.isEmpty(timesMapList)) {
timesMap = DateTimeUtils.findTimeInterSection(timesMapList); // 获取多个店铺的营业时间段的一个交集
timesPair = DateTimeUtils.findTimeInterSection(timesMapList);
} }
} }
if (timesPair == null || StrUtil.isBlank(timesPair.getFirst()) || StrUtil.isBlank(timesPair.getSecond())) {
// 没有具体的营业时间段
logger.info("[生成预约参数] 未找到营业时间段");
return Collections.emptyList();
}
// 显示几天的时间槽默认7天
int daysCnt = 7;
List<BookingArgDTO> result = new ArrayList<>(); List<BookingArgDTO> result = new ArrayList<>();
// 生成未来7天的数据 // 判断今天还能不能立即下单和预约下单就有今天的时间槽不能就没有今天的时间槽
Date now = new Date(); boolean canBookingToday = DateTimeUtils.isTimeInRange(timesPair.getFirst(), timesPair.getSecond(), new Date());
for (int i = 0; i < 7; i++) {
Date currentDate = DateUtil.offsetDay(now, i); // 生成7天的可预约时间槽数据
Date startDate = new Date();
if (!canBookingToday) {
// 如果今天不能下单和预约则从明天开始生成时间槽
startDate = DateUtil.offsetDay(new Date(), 1);
}
for (int i = 0; i < daysCnt; i++) {
startDate = DateUtil.offsetDay(startDate, i);
BookingArgDTO bookingArgDTO = new BookingArgDTO(); BookingArgDTO bookingArgDTO = new BookingArgDTO();
// 设置日期相关信息 // 设置日期相关信息
String dateStr = DateUtil.format(currentDate, "yyyy-MM-dd"); String dateStr = DateUtil.format(startDate, "yyyy-MM-dd");
String displayDateStr = DateUtil.format(currentDate, "MM月dd日"); String displayDateStr = DateUtil.format(startDate, "MM月dd日");
// 安全获取星期信息 // 安全获取星期信息
String weekStr = "星期"; String weekStr = "星期";
try { try {
weekStr = DateTimeUtils.getWeekOfDate(currentDate); weekStr = DateTimeUtils.getWeekOfDate(startDate);
} catch (Exception e) { } catch (Exception e) {
logger.warn("[生成预约参数] 获取星期信息异常使用默认值date: {}", dateStr); logger.warn("[生成预约参数] 获取星期信息异常使用默认值date: {}", dateStr);
} }
// 设置date_title // 设置date_title - 优化后的代码
String dateTitle;
if (i == 0) { if (i == 0) {
bookingArgDTO.setDate_title("今天(" + weekStr + ""); dateTitle = canBookingToday ? "今天" : "明天";
} else if (i == 1) { } else if (i == 1) {
bookingArgDTO.setDate_title("明天(" + weekStr + ""); dateTitle = canBookingToday ? "明天" : "后天";
} else { } else {
bookingArgDTO.setDate_title(displayDateStr + "" + weekStr + ""); dateTitle = displayDateStr;
} }
bookingArgDTO.setDate_title(dateTitle + "" + weekStr + "");
bookingArgDTO.setDate_str(displayDateStr); bookingArgDTO.setDate_str(displayDateStr);
bookingArgDTO.setDate(dateStr); bookingArgDTO.setDate(dateStr);
// 生成时间项 // 生成时间项
List<BookingArgDTO.BookingArgItem> items = new ArrayList<>(); // List<BookingArgDTO.BookingArgItem> items = new ArrayList<>();
// 如果是今天始终添加"立即送出"选项 // 如果是今天且在配送时间内添加"立即送出"选项
if (i == 0) { // if (i == 0 && canBookingToday) {
BookingArgDTO.BookingArgItem immediateItem = new BookingArgDTO.BookingArgItem(); // BookingArgDTO.BookingArgItem immediateItem = new BookingArgDTO.BookingArgItem();
immediateItem.setTime_title("立即送出"); // immediateItem.setTime_title("立即送出");
immediateItem.setBooking_at(0L); // immediateItem.setBooking_at(0L);
immediateItem.setBooking_state(1); // immediateItem.setBooking_state(1);
immediateItem.setBooking_begin_time(""); // immediateItem.setBooking_begin_time("");
immediateItem.setBooking_end_time(""); // immediateItem.setBooking_end_time("");
items.add(immediateItem); // items.add(immediateItem);
} // }
// 只有当 timesMap 不为空时才生成其他时间槽
if (!ObjectUtil.isEmpty(timesMap) && StrUtil.isNotBlank(timesMap.get("startTimeStr")) && StrUtil.isNotBlank(timesMap.get("endTimeStr"))) {
String startTimeStr = timesMap.get("startTimeStr");
String endTimeStr = timesMap.get("endTimeStr");
List<BookingArgDTO.BookingArgItem> timeSlots = generateTimeSlots(dateStr, startTimeStr, endTimeStr, i == 0);
if (i == 0) {
// 对于今天移除除"立即送出"外的所有时间槽
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)); String startTimeStr = timesPair.getFirst();
} else if (i == 0) { String endTimeStr = timesPair.getSecond();
// 如果timesMap为空今天只保留"立即送出"选项 boolean isToday = i == 0 && canBookingToday;
logger.debug("[生成预约参数] timesMap为空今天只生成立即送出选项"); // 生成时间项
} else { List<BookingArgDTO.BookingArgItem> items = generateTimeSlots(dateStr, startTimeStr, endTimeStr, isToday);
// 如果timesMap为空其他日期不生成任何时间槽 // if (i == 0 && canBookingToday) {
logger.debug("[生成预约参数] timesMap为空不生成{}的预约时间槽", dateStr); // items.addAll(timeSlots.stream().filter(item -> item.getBooking_state() != 1).collect(Collectors.toList()));
continue; // 跳过当前日期 // } else {
} // items.addAll(timeSlots);
// }
bookingArgDTO.setWorking_hours(String.format("%s-%s", startTimeStr, endTimeStr));
bookingArgDTO.setItems(items); bookingArgDTO.setItems(items);
result.add(bookingArgDTO); result.add(bookingArgDTO);
} }
logger.debug("[生成预约参数] 成功生成预约参数storeId: {}, timesMap: {}, 参数数量: {}", logger.debug("[生成预约参数] 成功生成预约参数storeId: {}, timesMap: {}, 参数数量: {}",
storeIds, timesMap, result.size()); storeIds, timesPair, result.size());
return result; return result;
} }
@ -1221,12 +1232,14 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
} }
/** /**
* 根据 storeIds一个或多个 storeid 34,23,43,23,先对id去重再获取多个店铺的营业时间 List<map{startTimeStr, endTimeStr}> 列表list * 根据 storeIds一个或多个 storeid 34,23,43,23,先对id去重
* 再获取多个店铺的营业时间 List<map{startTimeStr, endTimeStr}> 列表list
* 启动如果遇到某个店铺是开业活动筹备中的获取他的开始营业的的日期和营业时间段组合出特有营业时间段
* *
* @param storeIds 以逗号分隔的店铺ID字符串 * @param storeIds 以逗号分隔的店铺ID字符串
* @return 包含店铺营业时间信息的列表每个元素为包含opening_hours和close_hours的Map * @return 包含店铺营业时间信息的列表每个元素为包含opening_hours和close_hours的Map
*/ */
private List<Map<String, String>> selStoreBizTimeMapList(String storeIds) { private List<Pair<String, String>> selStoreBizTimeMapList(String storeIds) {
// 参数校验 // 参数校验
if (StrUtil.isBlank(storeIds)) { if (StrUtil.isBlank(storeIds)) {
return Collections.emptyList(); return Collections.emptyList();
@ -1257,9 +1270,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
return shopStoreInfos.stream() return shopStoreInfos.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(storeInfo -> { .map(storeInfo -> {
Map<String, String> timeSlot = new HashMap<>(); Pair<String, String> timeSlot = Pair.of(storeInfo.getStore_opening_hours(), storeInfo.getStore_close_hours());
timeSlot.put("startTimeStr", storeInfo.getStore_opening_hours());
timeSlot.put("endTimeStr", storeInfo.getStore_close_hours());
return timeSlot; return timeSlot;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@ -5,8 +5,8 @@
<sql id="Base_Column_List"> <sql id="Base_Column_List">
store_id store_id
, user_id, store_name, store_grade_id, store_logo, store_slogan, store_domain, store_area, store_district_id, , user_id, store_name, store_grade_id, store_logo, store_slogan, store_domain, store_area, store_district_id,
store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open, store_biz_state, store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open,
ringtone_is_enable, shop_parent_id, store_2nd_category_id, store_biz_state, store_biz_opening_dtime, ringtone_is_enable, shop_parent_id, store_2nd_category_id,
store_category_id, store_state_id, store_time, store_end_time, product_category_ids, store_o2o_tags, store_category_id, store_state_id, store_time, store_end_time, product_category_ids, store_o2o_tags,
store_o2o_flag, store_o2o_merchant_id, store_circle, subsite_id, lkl_merchant_no, lkl_term_no, wx_qrcode, store_o2o_flag, store_o2o_merchant_id, store_circle, subsite_id, lkl_merchant_no, lkl_term_no, wx_qrcode,
split_ratio, packing_fee, created_at, updated_at split_ratio, packing_fee, created_at, updated_at

27
pom.xml
View File

@ -540,12 +540,39 @@
<artifactId>maven-dependency-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
</plugin> </plugin>
<!-- 使用 Docker 命令删除(如果支持远程操作) -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>remove-remote-image</id>
<phase>clean</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>curl</executable>
<arguments>
<argument>-X</argument>
<argument>DELETE</argument>
<argument>${docker.registry}/mall/${project.artifactId}:${project.version}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>com.spotify</groupId> <groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId> <artifactId>docker-maven-plugin</artifactId>
<version>${docker.maven.plugin.version}</version> <version>${docker.maven.plugin.version}</version>
<!--如果想在项目打包时构建镜像添加--> <!--如果想在项目打包时构建镜像添加-->
<executions> <executions>
<!-- 使用 Docker 命令删除(如果支持远程操作) -->
<execution> <execution>
<id>build-image</id> <id>build-image</id>
<phase>package</phase> <phase>package</phase>