|
@@ -12,14 +12,21 @@ import shop.alien.entity.store.UserReservation;
|
|
|
import shop.alien.entity.store.UserReservationTable;
|
|
import shop.alien.entity.store.UserReservationTable;
|
|
|
import shop.alien.entity.store.UserReservationOrder;
|
|
import shop.alien.entity.store.UserReservationOrder;
|
|
|
import shop.alien.entity.store.vo.StoreReservationListVo;
|
|
import shop.alien.entity.store.vo.StoreReservationListVo;
|
|
|
|
|
+import shop.alien.entity.store.StoreBookingBusinessHours;
|
|
|
|
|
+import shop.alien.entity.store.StoreBookingSettings;
|
|
|
|
|
+import shop.alien.entity.store.EssentialHolidayComparison;
|
|
|
|
|
+import shop.alien.entity.store.vo.StoreBusinessInfoVo;
|
|
|
import shop.alien.mapper.StoreReservationMapper;
|
|
import shop.alien.mapper.StoreReservationMapper;
|
|
|
import shop.alien.mapper.UserReservationTableMapper;
|
|
import shop.alien.mapper.UserReservationTableMapper;
|
|
|
|
|
+import shop.alien.mapper.EssentialHolidayComparisonMapper;
|
|
|
import shop.alien.store.config.BaseRedisService;
|
|
import shop.alien.store.config.BaseRedisService;
|
|
|
import shop.alien.store.listener.RedisKeyExpirationHandler;
|
|
import shop.alien.store.listener.RedisKeyExpirationHandler;
|
|
|
import shop.alien.store.service.StoreBookingTableService;
|
|
import shop.alien.store.service.StoreBookingTableService;
|
|
|
import shop.alien.store.service.StoreInfoService;
|
|
import shop.alien.store.service.StoreInfoService;
|
|
|
import shop.alien.store.service.StoreReservationService;
|
|
import shop.alien.store.service.StoreReservationService;
|
|
|
import shop.alien.store.service.UserReservationOrderService;
|
|
import shop.alien.store.service.UserReservationOrderService;
|
|
|
|
|
+import shop.alien.store.service.StoreBookingBusinessHoursService;
|
|
|
|
|
+import shop.alien.store.service.StoreBookingSettingsService;
|
|
|
import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
|
|
import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategy;
|
|
|
import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategyFactory;
|
|
import shop.alien.store.strategy.merchantPayment.MerchantPaymentStrategyFactory;
|
|
|
import shop.alien.store.util.ali.AliSms;
|
|
import shop.alien.store.util.ali.AliSms;
|
|
@@ -61,6 +68,9 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
|
|
|
private final LifeNoticeMapper lifeNoticeMapper;
|
|
private final LifeNoticeMapper lifeNoticeMapper;
|
|
|
private final LifeUserMapper lifeUserMapper;
|
|
private final LifeUserMapper lifeUserMapper;
|
|
|
private final MerchantPaymentStrategyFactory merchantPaymentStrategyFactory;
|
|
private final MerchantPaymentStrategyFactory merchantPaymentStrategyFactory;
|
|
|
|
|
+ private final StoreBookingBusinessHoursService storeBookingBusinessHoursService;
|
|
|
|
|
+ private final StoreBookingSettingsService storeBookingSettingsService;
|
|
|
|
|
+ private final EssentialHolidayComparisonMapper essentialHolidayComparisonMapper;
|
|
|
|
|
|
|
|
/** 预约状态:已取消 */
|
|
/** 预约状态:已取消 */
|
|
|
private static final int STATUS_CANCELLED = 3;
|
|
private static final int STATUS_CANCELLED = 3;
|
|
@@ -572,6 +582,9 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
|
|
|
// 校验:不能超过下一个已确认预约的开始时间
|
|
// 校验:不能超过下一个已确认预约的开始时间
|
|
|
validateAddTimeNotExceedNextReservation(reservation, normalizedAddTimeStart, newEndTime);
|
|
validateAddTimeNotExceedNextReservation(reservation, normalizedAddTimeStart, newEndTime);
|
|
|
|
|
|
|
|
|
|
+ // 校验:不能超过营业时间的结束时间
|
|
|
|
|
+ validateAddTimeNotExceedBusinessHours(reservation, newEndTime);
|
|
|
|
|
+
|
|
|
// 更新预约结束时间
|
|
// 更新预约结束时间
|
|
|
reservation.setEndTime(newEndTime);
|
|
reservation.setEndTime(newEndTime);
|
|
|
boolean updateResult = this.updateById(reservation);
|
|
boolean updateResult = this.updateById(reservation);
|
|
@@ -855,6 +868,234 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
|
+ * 校验加时不能超过营业时间的结束时间
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param reservation 预约信息
|
|
|
|
|
+ * @param newEndTime 新的结束时间(yyyy-MM-dd HH:mm:ss格式)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void validateAddTimeNotExceedBusinessHours(UserReservation reservation, String newEndTime) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (reservation == null || reservation.getStoreId() == null || reservation.getReservationDate() == null) {
|
|
|
|
|
+ log.warn("预约信息不完整,跳过营业时间校验");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询门店的预订设置
|
|
|
|
|
+ StoreBookingSettings settings = storeBookingSettingsService.getByStoreId(reservation.getStoreId());
|
|
|
|
|
+ if (settings == null || settings.getId() == null) {
|
|
|
|
|
+ log.warn("门店未配置预订设置,跳过营业时间校验,storeId={}", reservation.getStoreId());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询营业时间列表
|
|
|
|
|
+ List<StoreBookingBusinessHours> businessHoursList = storeBookingBusinessHoursService.getListBySettingsId(settings.getId());
|
|
|
|
|
+ if (businessHoursList == null || businessHoursList.isEmpty()) {
|
|
|
|
|
+ log.warn("门店未配置营业时间,跳过营业时间校验,storeId={}", reservation.getStoreId());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 解析预约日期
|
|
|
|
|
+ Date reservationDate = reservation.getReservationDate();
|
|
|
|
|
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
|
+ String reservationDateStr = dateFormat.format(reservationDate);
|
|
|
|
|
+
|
|
|
|
|
+ // 查找匹配的营业时间
|
|
|
|
|
+ StoreBookingBusinessHours matchedBusinessHours = null;
|
|
|
|
|
+
|
|
|
|
|
+ // 先查找特殊营业时间(business_type=2,兼容旧值1)
|
|
|
|
|
+ // 特殊营业时间的特征:有 holidayDate 或 essentialId 或 holidayType 不为空
|
|
|
|
|
+ for (StoreBookingBusinessHours businessHours : businessHoursList) {
|
|
|
|
|
+ Integer businessType = businessHours.getBusinessType();
|
|
|
|
|
+ // 判断是否为特殊营业时间:新值2,或旧值1且有关联的节假日信息
|
|
|
|
|
+ boolean isSpecialBusinessHours = false;
|
|
|
|
|
+ if (businessType != null) {
|
|
|
|
|
+ if (businessType == 2) {
|
|
|
|
|
+ // 新值:2 表示特殊营业时间
|
|
|
|
|
+ isSpecialBusinessHours = true;
|
|
|
|
|
+ } else if (businessType == 1) {
|
|
|
|
|
+ // 旧值:1 可能是特殊营业时间,需要检查是否有节假日信息
|
|
|
|
|
+ if (businessHours.getEssentialId() != null ||
|
|
|
|
|
+ businessHours.getHolidayDate() != null ||
|
|
|
|
|
+ (businessHours.getHolidayType() != null && !businessHours.getHolidayType().trim().isEmpty())) {
|
|
|
|
|
+ isSpecialBusinessHours = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isSpecialBusinessHours) {
|
|
|
|
|
+ // 特殊营业时间:需要匹配 essential_holiday_comparison 表中的日期范围
|
|
|
|
|
+ if (businessHours.getEssentialId() != null) {
|
|
|
|
|
+ EssentialHolidayComparison holiday = essentialHolidayComparisonMapper.selectById(businessHours.getEssentialId());
|
|
|
|
|
+ if (holiday != null && holiday.getStartTime() != null && holiday.getEndTime() != null) {
|
|
|
|
|
+ // 判断预约日期是否在节假日的日期范围内
|
|
|
|
|
+ Date startTime = holiday.getStartTime();
|
|
|
|
|
+ Date endTime = holiday.getEndTime();
|
|
|
|
|
+ if (!reservationDate.before(startTime) && !reservationDate.after(endTime)) {
|
|
|
|
|
+ matchedBusinessHours = businessHours;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (businessHours.getHolidayDate() != null) {
|
|
|
|
|
+ // 如果没有 essentialId,使用 holidayDate 匹配
|
|
|
|
|
+ if (reservationDateStr.equals(businessHours.getHolidayDate())) {
|
|
|
|
|
+ matchedBusinessHours = businessHours;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有匹配到特殊营业时间,使用正常营业时间(business_type=1,兼容旧值0)
|
|
|
|
|
+ if (matchedBusinessHours == null) {
|
|
|
|
|
+ matchedBusinessHours = businessHoursList.stream()
|
|
|
|
|
+ .filter(h -> {
|
|
|
|
|
+ Integer businessType = h.getBusinessType();
|
|
|
|
|
+ // 兼容旧值:0 和新值:1 都表示正常营业时间
|
|
|
|
|
+ return businessType != null && (businessType == 1 || businessType == 0);
|
|
|
|
|
+ })
|
|
|
|
|
+ .findFirst()
|
|
|
|
|
+ .orElse(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果 store_booking_business_hours 中没有找到匹配的营业时间,则从 store_business_info 查询
|
|
|
|
|
+ if (matchedBusinessHours == null) {
|
|
|
|
|
+ log.info("store_booking_business_hours 中未找到匹配的营业时间,尝试从 store_business_info 查询,storeId={}", reservation.getStoreId());
|
|
|
|
|
+ matchedBusinessHours = findBusinessHoursFromStoreBusinessInfo(reservation, reservationDate, reservationDateStr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (matchedBusinessHours == null) {
|
|
|
|
|
+ log.warn("未找到匹配的营业时间,跳过营业时间校验");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是全天营业(booking_time_type=1),不限制结束时间
|
|
|
|
|
+ if (matchedBusinessHours.getBookingTimeType() != null && matchedBusinessHours.getBookingTimeType() == 1) {
|
|
|
|
|
+ log.info("全天营业,不限制结束时间");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取营业时间的结束时间
|
|
|
|
|
+ String businessEndTime = matchedBusinessHours.getEndTime();
|
|
|
|
|
+ if (businessEndTime == null || businessEndTime.trim().isEmpty()) {
|
|
|
|
|
+ log.warn("营业时间结束时间为空,跳过校验");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 标准化新的结束时间,提取日期部分
|
|
|
|
|
+ String normalizedNewEndTime = normalizeDateTime(newEndTime);
|
|
|
|
|
+ if (normalizedNewEndTime == null) {
|
|
|
|
|
+ log.warn("新的结束时间格式错误,跳过校验,newEndTime={}", newEndTime);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 从新的结束时间中提取日期部分(用于构建营业时间的完整日期时间)
|
|
|
|
|
+ // normalizedNewEndTime 格式:yyyy-MM-dd HH:mm:ss
|
|
|
|
|
+ String newEndTimeDateStr = normalizedNewEndTime.substring(0, 10); // 提取 yyyy-MM-dd
|
|
|
|
|
+
|
|
|
|
|
+ // 将营业时间的结束时间转换为完整的日期时间格式
|
|
|
|
|
+ // businessEndTime 是 HH:mm 格式,需要结合新结束时间的日期
|
|
|
|
|
+ String businessEndDateTimeStr = newEndTimeDateStr + " " + businessEndTime.trim() + ":00";
|
|
|
|
|
+ String normalizedBusinessEndTime = normalizeDateTime(businessEndDateTimeStr);
|
|
|
|
|
+ if (normalizedBusinessEndTime == null) {
|
|
|
|
|
+ log.warn("营业时间结束时间格式错误,跳过校验,businessEndTime={}", businessEndTime);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 比较新的结束时间和营业时间的结束时间
|
|
|
|
|
+ int comparison = compareTime(normalizedNewEndTime, normalizedBusinessEndTime);
|
|
|
|
|
+ if (comparison > 0) {
|
|
|
|
|
+ // 新的结束时间超过了营业时间的结束时间
|
|
|
|
|
+ throw new RuntimeException("加时后结束时间不能超过营业时间,营业结束时间为:" + businessEndTime);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("加时校验通过,新的结束时间={},营业结束时间={}", normalizedNewEndTime, normalizedBusinessEndTime);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ if (e instanceof RuntimeException) {
|
|
|
|
|
+ throw e;
|
|
|
|
|
+ }
|
|
|
|
|
+ log.error("校验加时是否超过营业时间失败", e);
|
|
|
|
|
+ // 校验失败时,抛出异常以阻止加时操作
|
|
|
|
|
+ throw new RuntimeException("校验营业时间失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从 store_business_info 表中查找匹配的营业时间
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param reservation 预约信息
|
|
|
|
|
+ * @param reservationDate 预约日期
|
|
|
|
|
+ * @param reservationDateStr 预约日期字符串(yyyy-MM-dd格式)
|
|
|
|
|
+ * @return 匹配的营业时间对象(包装为 StoreBookingBusinessHours 格式),未找到返回 null
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreBookingBusinessHours findBusinessHoursFromStoreBusinessInfo(
|
|
|
|
|
+ UserReservation reservation, Date reservationDate, String reservationDateStr) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 查询门店的营业时间列表
|
|
|
|
|
+ List<StoreBusinessInfoVo> storeBusinessInfoList = storeInfoService.getStoreInfoBusinessHours(reservation.getStoreId());
|
|
|
|
|
+ if (storeBusinessInfoList == null || storeBusinessInfoList.isEmpty()) {
|
|
|
|
|
+ log.warn("门店未配置 store_business_info 营业时间,storeId={}", reservation.getStoreId());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ StoreBusinessInfoVo matchedBusinessInfo = null;
|
|
|
|
|
+
|
|
|
|
|
+ // 先查找特殊营业时间(business_type=2)
|
|
|
|
|
+ for (StoreBusinessInfoVo businessInfo : storeBusinessInfoList) {
|
|
|
|
|
+ if (businessInfo.getBusinessType() != null && businessInfo.getBusinessType() == 2) {
|
|
|
|
|
+ // 特殊营业时间:需要匹配 essential_holiday_comparison 表中的日期范围
|
|
|
|
|
+ if (businessInfo.getEssentialId() != null && !businessInfo.getEssentialId().trim().isEmpty()) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Integer essentialId = Integer.parseInt(businessInfo.getEssentialId().trim());
|
|
|
|
|
+ EssentialHolidayComparison holiday = essentialHolidayComparisonMapper.selectById(essentialId);
|
|
|
|
|
+ if (holiday != null && holiday.getStartTime() != null && holiday.getEndTime() != null) {
|
|
|
|
|
+ // 判断预约日期是否在节假日的日期范围内
|
|
|
|
|
+ Date startTime = holiday.getStartTime();
|
|
|
|
|
+ Date endTime = holiday.getEndTime();
|
|
|
|
|
+ if (!reservationDate.before(startTime) && !reservationDate.after(endTime)) {
|
|
|
|
|
+ matchedBusinessInfo = businessInfo;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ log.warn("门店营业时间关联的节假日ID格式错误,storeId={}, essentialId={}",
|
|
|
|
|
+ reservation.getStoreId(), businessInfo.getEssentialId());
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (businessInfo.getBusinessDate() != null) {
|
|
|
|
|
+ // 如果没有 essentialId,使用 businessDate 匹配
|
|
|
|
|
+ if (reservationDateStr.equals(businessInfo.getBusinessDate())) {
|
|
|
|
|
+ matchedBusinessInfo = businessInfo;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有匹配到特殊营业时间,使用正常营业时间(business_type=1)
|
|
|
|
|
+ if (matchedBusinessInfo == null) {
|
|
|
|
|
+ matchedBusinessInfo = storeBusinessInfoList.stream()
|
|
|
|
|
+ .filter(h -> h.getBusinessType() != null && h.getBusinessType() == 1)
|
|
|
|
|
+ .findFirst()
|
|
|
|
|
+ .orElse(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (matchedBusinessInfo == null) {
|
|
|
|
|
+ log.warn("store_business_info 中未找到匹配的营业时间,storeId={}", reservation.getStoreId());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将 StoreBusinessInfoVo 转换为 StoreBookingBusinessHours 格式(仅用于返回 endTime)
|
|
|
|
|
+ StoreBookingBusinessHours result = new StoreBookingBusinessHours();
|
|
|
|
|
+ result.setEndTime(matchedBusinessInfo.getEndTime());
|
|
|
|
|
+ result.setBookingTimeType(null); // store_business_info 没有 booking_time_type,设为 null 表示非全天
|
|
|
|
|
+ log.info("从 store_business_info 找到匹配的营业时间,endTime={}", matchedBusinessInfo.getEndTime());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("从 store_business_info 查询营业时间失败", e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
* 校验预约是否过期
|
|
* 校验预约是否过期
|
|
|
* 判断当前时间是否超过预约日期 + 结束时间
|
|
* 判断当前时间是否超过预约日期 + 结束时间
|
|
|
*/
|
|
*/
|