预约订单槽点列表

This commit is contained in:
Jack 2025-10-25 16:18:15 +08:00
parent 9b5c618c40
commit 4ff892d09b
5 changed files with 312 additions and 2 deletions

View File

@ -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<BookingArgItem> 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;
}
}

View File

@ -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 * 将多种日期格式转换为 yyyy-MM-dd
@ -473,6 +490,7 @@ public class DateTimeUtils {
} }
} }
/** /**
* 判断时间字符串是否与格式模式匹配 * 判断时间字符串是否与格式模式匹配
* *

View File

@ -12,6 +12,7 @@ import com.suisung.mall.common.api.StateCode;
import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.domain.UserDto;
import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.exception.ApiException;
import com.suisung.mall.common.modules.order.*; 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.service.impl.BaseControllerImpl;
import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.shop.order.service.*; import com.suisung.mall.shop.order.service.*;
@ -321,6 +322,13 @@ public class UserOrderController extends BaseControllerImpl {
return CommonResult.success(data); 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<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_id);
return CommonResult.success(list);
}
} }

View File

@ -2,6 +2,7 @@ package com.suisung.mall.shop.order.service;
import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.modules.order.ShopOrderInfo; 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 com.suisung.mall.core.web.service.IBaseService;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
@ -133,4 +134,13 @@ public interface ShopOrderInfoService extends IBaseService<ShopOrderInfo> {
* @return * @return
*/ */
Pair<Boolean, String> runBookingOrder2SfExpressOrder(String orderId); Pair<Boolean, String> runBookingOrder2SfExpressOrder(String orderId);
/**
* 根据店铺的营业时间范围生成可预约下单的参数
*
* @param storeId 店铺ID
* @return
*/
List<BookingArgDTO> genBookingOrderArgList(Integer storeId);
} }

View File

@ -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.plantform.ShopPlantformFeedback;
import com.suisung.mall.common.modules.product.ShopProductComment; import com.suisung.mall.common.modules.product.ShopProductComment;
import com.suisung.mall.common.modules.store.ShopStoreInfo; 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.pojo.res.ThirdApiRes;
import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.common.utils.CommonUtil; import com.suisung.mall.common.utils.CommonUtil;
@ -137,6 +138,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
@Autowired @Autowired
private ThreadPoolExecutor executor; private ThreadPoolExecutor executor;
@Override @Override
public Map dashboard() { public Map dashboard() {
@ -197,7 +199,6 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
return Convert.toList(String.class, findKey(queryWrapper)); return Convert.toList(String.class, findKey(queryWrapper));
} }
/** /**
* 获取能自动确认收货的订单号 * 获取能自动确认收货的订单号
* *
@ -826,7 +827,6 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
return data; return data;
} }
@Override @Override
public List<String> getAutoCancelOrderIdByPage(Integer pageNum, Integer pageSize) { public List<String> getAutoCancelOrderIdByPage(Integer pageNum, Integer pageSize) {
QueryWrapper<ShopOrderInfo> queryWrapper = new QueryWrapper<>(); QueryWrapper<ShopOrderInfo> queryWrapper = new QueryWrapper<>();
@ -1014,5 +1014,201 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
} }
} }
/**
* 根据店铺的营业时间范围生成可预约下单的参数
*
* @param storeId 店铺ID
* @return 预约参数列表
*/
@Override
public List<BookingArgDTO> 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<BookingArgDTO> 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<BookingArgDTO.BookingArgItem> 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<BookingArgDTO.BookingArgItem> 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<BookingArgDTO.BookingArgItem> 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();
}
}
} }