|
|
@@ -11,14 +11,13 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import shop.alien.entity.store.*;
|
|
|
import shop.alien.entity.store.dto.UserReservationDTO;
|
|
|
+import shop.alien.entity.store.vo.StoreMainInfoVo;
|
|
|
import shop.alien.entity.store.vo.UserReservationVo;
|
|
|
import shop.alien.mapper.UserReservationMapper;
|
|
|
import shop.alien.mapper.UserReservationTableMapper;
|
|
|
-import shop.alien.store.service.StoreBookingCategoryService;
|
|
|
-import shop.alien.store.service.StoreBookingSettingsService;
|
|
|
-import shop.alien.store.service.StoreBookingTableService;
|
|
|
-import shop.alien.store.service.UserReservationService;
|
|
|
+import shop.alien.store.service.*;
|
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -42,7 +41,14 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
|
|
|
|
|
|
private final StoreBookingCategoryService storeBookingCategoryService;
|
|
|
|
|
|
+ private final StoreInfoService storeInfoService;
|
|
|
+
|
|
|
+ /** 预约状态:待确认 */
|
|
|
private static final int STATUS_PENDING = 0;
|
|
|
+ /** 预约状态:已取消(不参与约满统计与展示) */
|
|
|
+ private static final int STATUS_CANCELLED = 3;
|
|
|
+ /** 查找首个未约满日期时,最多往后检查的天数 */
|
|
|
+ private static final int MAX_DAYS_TO_CHECK = 366;
|
|
|
|
|
|
@Override
|
|
|
public Integer add(UserReservationDTO dto) {
|
|
|
@@ -145,9 +151,76 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
|
|
|
list.put("storeBookingTables", storeBookingTables);
|
|
|
List<StoreBookingCategory> storeBookingCategorys = storeBookingCategoryService.list(new LambdaQueryWrapper<StoreBookingCategory>().eq(StoreBookingCategory::getStoreId, storeId));
|
|
|
list.put("storeBookingCategorys", storeBookingCategorys);
|
|
|
+ StoreMainInfoVo storeInfo = storeInfoService.getStoreInfo(storeId);
|
|
|
+ list.put("storeInfo", storeInfo);
|
|
|
return list;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从今天起查找第一个未约满的日期,并返回该日的预约数据。
|
|
|
+ * 判断逻辑:若当天已预约人数(guest_count 之和)>= 门店单时段最大容纳人数,则视为约满,顺延到下一天再判断。
|
|
|
+ *
|
|
|
+ * @param storeId 门店ID
|
|
|
+ * @return Map:date 为 yyyy-MM-dd 格式的日期,reservations 为该日的预约 VO 列表;未配置或 storeId 为空时 date 可能为 null、reservations 为空列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> findFirstAvailableDayReservations(Integer storeId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ if (storeId == null) {
|
|
|
+ result.put("date", null);
|
|
|
+// result.put("reservations", List.of());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ // 取门店预订设置中的单时段最大容纳人数,未配置或为 0 则视为不设上限
|
|
|
+ int maxCapacity = Integer.MAX_VALUE;
|
|
|
+ List<StoreBookingSettings> settingsList = storeBookingSettingsService.list(
|
|
|
+ new LambdaQueryWrapper<StoreBookingSettings>().eq(StoreBookingSettings::getStoreId, storeId));
|
|
|
+ if (!settingsList.isEmpty() && settingsList.get(0).getMaxCapacityPerSlot() != null
|
|
|
+ && settingsList.get(0).getMaxCapacityPerSlot() > 0) {
|
|
|
+ maxCapacity = settingsList.get(0).getMaxCapacityPerSlot();
|
|
|
+ }
|
|
|
+ // 从今天 00:00:00 开始,按天往后检查
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
+ for (int i = 0; i < MAX_DAYS_TO_CHECK; i++) {
|
|
|
+ Date dayStart = cal.getTime();
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
+ Date dayEnd = cal.getTime();
|
|
|
+ // 统计该日该门店下非取消状态的预约总人数
|
|
|
+ LambdaQueryWrapper<UserReservation> countWrapper = new LambdaQueryWrapper<>();
|
|
|
+ countWrapper.eq(UserReservation::getStoreId, storeId)
|
|
|
+ .ne(UserReservation::getStatus, STATUS_CANCELLED)
|
|
|
+ .ge(UserReservation::getReservationDate, dayStart)
|
|
|
+ .lt(UserReservation::getReservationDate, dayEnd);
|
|
|
+ List<UserReservation> dayList = this.list(countWrapper);
|
|
|
+ int totalGuests = dayList.stream()
|
|
|
+ .mapToInt(r -> r.getGuestCount() == null ? 0 : r.getGuestCount())
|
|
|
+ .sum();
|
|
|
+ // 未约满:总人数小于上限,返回该日及该日全部预约数据
|
|
|
+ if (totalGuests < maxCapacity) {
|
|
|
+ LambdaQueryWrapper<UserReservation> listWrapper = new LambdaQueryWrapper<>();
|
|
|
+ listWrapper.eq(UserReservation::getStoreId, storeId)
|
|
|
+ .ge(UserReservation::getReservationDate, dayStart)
|
|
|
+ .lt(UserReservation::getReservationDate, dayEnd)
|
|
|
+ .orderByAsc(UserReservation::getReservationDate)
|
|
|
+ .orderByAsc(UserReservation::getStartTime);
|
|
|
+ List<UserReservation> reservations = this.list(listWrapper);
|
|
|
+ List<UserReservationVo> voList = reservations.stream().map(this::toVoWithTableIds).collect(Collectors.toList());
|
|
|
+ result.put("date", new SimpleDateFormat("yyyy-MM-dd").format(dayStart));
|
|
|
+ result.put("reservations", voList);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 连续 MAX_DAYS_TO_CHECK 天都约满时,返回最后检查的日期,预约列表为空
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, -1);
|
|
|
+ result.put("date", new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime()));
|
|
|
+// result.put("reservations", List.of());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
private LambdaQueryWrapper<UserReservation> buildListWrapper(Integer userId, Integer storeId, Integer status,
|
|
|
Date dateFrom, Date dateTo) {
|
|
|
LambdaQueryWrapper<UserReservation> wrapper = new LambdaQueryWrapper<>();
|