# BookingController.java

    package cloud.wconnect.rateshop.controller.booking;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.rateshop.entity.booking.Booking;
    import cloud.wconnect.rateshop.entity.booking.BookingImportLog;
    import cloud.wconnect.rateshop.pojo.dto.booking.BookingDTO;
    import cloud.wconnect.rateshop.pojo.dto.booking.BookingImportLogDTO;
    import cloud.wconnect.rateshop.pojo.dto.booking.ImportBookingDTO;
    import cloud.wconnect.rateshop.pojo.query.booking.BatchDeleteQuery;
    import cloud.wconnect.rateshop.pojo.query.booking.BookingImportLogQuery;
    import cloud.wconnect.rateshop.pojo.query.booking.BookingQuery;
    import cloud.wconnect.rateshop.service.booking.IBookingImportLogService;
    import cloud.wconnect.rateshop.service.booking.IBookingService;
    import cloud.wconnect.rateshop.service.booking.report.IBookingFileParse;
    import jakarta.annotation.Resource;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;

    import java.util.*;

    /**
     * <p>
     *  前端控制器
     * </p>
     *
     * @author yyt
     * @since 2024-07-22
     */
    @RestController
    @RequestMapping("/booking")
    public class BookingController {

        @Resource
        private IBookingService bookingService;
        @Resource
        private IBookingImportLogService bookingImportLogService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 1238 - 查询 booking 数据
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return booking 数据
         * @author yyt
         * @date 2024/7/22
         **/
        @ApiId(1238)
        @GetMapping
        public ListPagedDTO<BookingDTO> list(String property, @Valid BookingQuery query) {
            return bookingService.listBookingDTO(sessionContext.getPropertyId(property), query);
        }

        /**
         * 1239 - 新增 / 编辑 booking
         *
         * @param id         数据 id
         * @param property   数据 id
         * @param bookingDTO booking 数据
         * @return {}
         * @author yyt
         * @date 2024/7/22
         **/
        @ApiId(1239)
        @PostMapping("/{id:\\d+}")
        public Map save(@PathVariable long id, String property, @Valid @RequestBody BookingDTO bookingDTO) {
            Integer propertyId = sessionContext.getPropertyId(property);
            int userId = sessionContext.getLoginUserId();

            if (bookingDTO.getStartDate() > bookingDTO.getEndDate()) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The start date must be less than or equal to the end date");
            }

            if (id == 0) {
                bookingService.save(propertyId, bookingDTO, userId);
            } else {
                Booking booking = bookingService.checkExist(id);

                checkBelong(booking, propertyId);

                bookingService.update(booking, bookingDTO, userId);
            }

            return Collections.emptyMap();
        }

        /**
         * 1240 - 删除 booking
         *
         * @param id       booking id
         * @param property 资产标识
         * @return {}
         * @author yyt
         * @date 2024/7/23
         **/
        @ApiId(1240)
        @DeleteMapping("/{id:\\d+}")
        public Map delete(@PathVariable long id, String property) {
            Booking booking = bookingService.checkExist(id);

            checkBelong(booking, sessionContext.getPropertyId(property));

            bookingService.removeRewrite(booking);

            return Collections.emptyMap();
        }

        /**
         * 1246 - 手动导入 booking
         *
         * @param property  资产标识
         * @param file      导入文件
         * @param importDTO 选择时间范围
         * @return {}
         * @author yyt
         * @date 2024/7/23
         **/
        @ApiId(1246)
        @PostMapping("/file")
        public Map importBooking(String property, MultipartFile file, @Valid ImportBookingDTO importDTO) {
            if (Objects.isNull(file)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the file must not be null");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            checkDateRange(importDTO.getStartDate(), importDTO.getToDate(), propertyDTO.getTimezone());

            int userId = sessionContext.getLoginUserId();

            Map<String, IBookingFileParse> beans = applicationContext.getBeansOfType(IBookingFileParse.class);
            for (IBookingFileParse bean : beans.values()) {
                if (bean.supports(SystemPlatformEnum.getSourceForOpera(importDTO.getType()))) {
                    bean.checkFile(propertyDTO, file);

                    BookingImportLog importLog = bean.handleImportLog(propertyDTO, importDTO.getStartDate(),
                            importDTO.getToDate(), file, userId);

                    bean.handleFile(propertyDTO, importLog, userId);
                    return Collections.emptyMap();
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 检查日期区间
         *
         * @param startDate 开始日期
         * @param toDate    结束日期
         * @param timeZone  时区
         * @author yyt
         * @date 2024/7/30
         **/
        private void checkDateRange(Long startDate, Long toDate, String timeZone) {
            if (startDate > toDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The start date must be less than or equal to the end date");
            }

            if (DateTimeUtils.longToLocalDate(toDate, timeZone)
                    .isAfter(DateTimeUtils.longToLocalDate(startDate, timeZone).plusYears(1))) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT_YEAR,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(1),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The selection date cannot exceed 1 year");
            }
        }

        /**
         * 检查日期区间
         *
         * @param startDate 开始日期
         * @param toDate    结束日期
         * @author yyt
         * @date 2024/7/30
         **/
        private void checkDateRange(Integer startDate, Integer toDate) {
            if (startDate > toDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The start date must be less than or equal to the end date");
            }

            if (DateTimeUtils.strToLocalDate(toDate.toString(), BaseConstant.DATE_FORMAT_YYYYMMDD)
                    .isAfter(DateTimeUtils.strToLocalDate(startDate.toString(), BaseConstant.DATE_FORMAT_YYYYMMDD).plusYears(1))) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT_YEAR,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(1),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The selection date cannot exceed 1 year");
            }
        }

        /**
         * 1247 - 获取 booking 导入记录
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return booking 导入记录
         * @author yyt
         * @date 2024/7/24
         **/
        @ApiId(1247)
        @GetMapping("/import-log")
        public ListPagedDTO<BookingImportLogDTO> listImportLog(String property, @Valid BookingImportLogQuery query) {
            return bookingImportLogService.listImportLog(sessionContext.getPropertyId(property), query);
        }

        /**
         * 1248 - 批量删除
         *
         * @param property 资产标识
         * @param query    删除条件
         * @return {}
         * @author yyt
         * @date 2024/8/2
         **/
        @ApiId(1248)
        @DeleteMapping("/batch")
        public Map deleteBatch(String property, @RequestBody BatchDeleteQuery query) {
            List<Integer> ids = query.getIds();
            Integer startDate = query.getStartDate();
            Integer endDate = query.getEndDate();
            if (CollectionUtils.isEmpty(ids) && Objects.isNull(startDate) && Objects.isNull(endDate)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("ids", "startDate", "endDate"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Parameter missing");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));

            if (CollectionUtils.isNotEmpty(ids)) {
                checkCount(ids);

                List<Booking> bookings = bookingService.listByIds(ids);
                if (bookings.size() != ids.size()) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("booking"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the booking is not found");
                }

                checkBelong(bookings, propertyDTO.getId());

                bookingService.removeBatchRewrite(propertyDTO, bookings);
            } else {
                checkDateRange(startDate, endDate);

                bookingService.removeBatchByDateRange(propertyDTO, startDate, endDate);
            }

            return Collections.emptyMap();
        }

        /**
         * 检查数量
         *
         * @param ids ids
         * @author yyt
         * @date 2024/8/2
         **/
        private void checkCount(List<Integer> ids) {
            if (ids.size() > 100) {
                throw new BadRequestException(CommonErrorI18nConstant.HANDLE_DATA_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(100),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "A maximum of 100 entries can be deleted at a time");
            }
        }

        /**
         * 检查 booking 属于当前资产
         *
         * @param booking    booking
         * @param propertyId 当前资产
         * @author yyt
         * @date 2024/7/23
         **/
        private void checkBelong(Booking booking, Integer propertyId) {
            if (!booking.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the booking;propertyId:" + propertyId +
                        ",bookingPropertyId:" + booking.getPropertyId() + ",bookingId:" + booking.getId());
            }
        }

        /**
         * 检查 booking 属于当前资产
         *
         * @param bookings   booking
         * @param propertyId 当前资产
         * @author yyt
         * @date 2024/8/2
         **/
        private void checkBelong(List<Booking> bookings, Integer propertyId) {
            Optional<Booking> optional = bookings.stream().filter(item -> !item.getPropertyId().equals(propertyId)).findAny();
            if (optional.isPresent()) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the bookings;propertyId:" + propertyId +
                        ",bookingPropertyId:" + optional.get().getPropertyId() + ",bookingId:" + optional.get().getId());
            }
        }

    }