# WeeklyMonthlyRevenueRoomController.java

    package cloud.wconnect.rateshop.controller.weeklymonthlyrevenue;

    import cloud.wconnect.commonlib.annotation.ApiId;
    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.CommonFieldI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    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.cons.weeklymonthlyrevenue.MonthlyRevenueI18nConstant;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenuePhysicalCapacityDTO;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenueRoomDTO;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenueRoomTypeDTO;
    import cloud.wconnect.rateshop.pojo.query.weeklymonthlyrevenue.WMRevenueVersionDataQuery;
    import cloud.wconnect.rateshop.pojo.vo.monthlyrevenue.WMRevenueVersionVO;
    import cloud.wconnect.rateshop.service.weeklymonthlyrevenue.IWeeklyMonthlyRevenueRoomService;
    import cloud.wconnect.rateshop.service.weeklymonthlyrevenue.IWeeklyMonthlyRevenueRoomTypeService;
    import cloud.wconnect.rateshop.service.weeklymonthlyrevenue.IWeeklyMonthlyRevenueVersionDataService;
    import jakarta.annotation.Resource;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;

    import java.time.LocalDate;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;

    /**
     * <p>
     * Weekly Monthly Revenue Room 前端控制器
     * </p>
     *
     * @author zc
     * @since 2024-05-13
     */
    @Validated
    @RestController
    @RequestMapping("/weekly-monthly-revenue")
    public class WeeklyMonthlyRevenueRoomController {

        @Resource
        private IWeeklyMonthlyRevenueRoomService revenueRoomService;
        @Resource
        private IWeeklyMonthlyRevenueVersionDataService versionDataService;
        @Resource
        private IWeeklyMonthlyRevenueRoomTypeService revenueRoomTypeService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取指定年月的 Room Revenue
         *
         * @param property 资产标识
         * @param theMonth 年月（yyyyMM）
         * @return WeeklyMonthlyRevenueRoomDTO
         */
        @ApiId(1160)
        @GetMapping("/room-revenue")
        public WeeklyMonthlyRevenueRoomDTO getRoomRevenue(@RequestParam String property, @RequestParam long theMonth) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            return revenueRoomService.getRoomRevenue(propertyDTO, theMonth);
        }

        /**
         * 保存指定年月的 Room Type
         *
         * @param property    资产标识
         * @param roomTypeDTO 保存内容 DTO
         * @return empty map
         * @author zc
         * @date 2024/5/14
         */
        @ApiId(1161)
        @PostMapping("/room-type")
        public Map<Object, Object> saveRoomType(@RequestParam String property,
                                                @RequestBody @Valid WeeklyMonthlyRevenueRoomTypeDTO roomTypeDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            revenueRoomTypeService.saveRoomType(propertyId, roomTypeDTO, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 保存 Physical capacity
         *
         * @param property                资产标识
         * @param physicalCapacityDTOList Physical capacity DTO 集合
         * @return empty map
         * @author zc
         * @date 2024/5/14
         */
        @ApiId(1162)
        @PostMapping("/physical-capacity")
        public Map<Object, Object> savePhysicalCapacity(@RequestParam String property,
                                                        @RequestBody List<@Valid WeeklyMonthlyRevenuePhysicalCapacityDTO> physicalCapacityDTOList) {
            if (CollectionUtils.isEmpty(physicalCapacityDTOList)) {
                return Collections.emptyMap();
            }
            if (physicalCapacityDTOList.stream().map(WeeklyMonthlyRevenuePhysicalCapacityDTO::getTheDate).distinct().count()
                    != physicalCapacityDTOList.size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.DATE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The physicalCapacityDTOList cannot contain duplicate theDate");
            }
            if (physicalCapacityDTOList.size() > 31) {
                throw new BadRequestException(CommonErrorI18nConstant.HANDLE_DATA_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(31),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The physicalCapacityDTOList cannot contain more than 31 items");
            }
            int propertyId = sessionContext.getPropertyId(property);
            revenueRoomService.savePhysicalCapacity(propertyId, physicalCapacityDTOList, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 获取指定资产指定时间段的版本数据
         *
         * @param property 资产标识
         * @param query    查询参数
         * @return ListPagedDTO<WMRevenueVersionVO>
         * @author zc
         * @date 2025/5/22
         */
        @ApiId(1519)
        @GetMapping("/version-data")
        public ListPagedDTO<WMRevenueVersionVO> listVersionData(@RequestParam String property,
                                                                @Valid WMRevenueVersionDataQuery query) {
            // startDate，endDate 最大日期跨度为31天
            LocalDate startLocalDate = DateTimeUtils.strToLocalDate(query.getStartDate().toString(),
                    BaseConstant.DATE_FORMAT_YYYYMMDD);
            LocalDate endLocalDate = DateTimeUtils.strToLocalDate(query.getEndDate().toString(),
                    BaseConstant.DATE_FORMAT_YYYYMMDD);
            if (DateTimeUtils.numberOfDays(startLocalDate, endLocalDate) >= 31) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(31),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The time span cannot exceed 31 days");
            }

            // startDate 与 dataAsOf 最小日期间隔不能超过 100 天；dataAsOf 最大日期不能超过 startDate
            if (CollectionUtils.isNotEmpty(query.getDataAsOf())) {
                validateQueryDate(startLocalDate, Collections.min(query.getDataAsOf()), Collections.max(query.getDataAsOf()));
            } else {
                validateQueryDate(startLocalDate, query.getDataAsOfStartDate(), query.getDataAsOfEndDate());
            }

            int propertyId = sessionContext.getPropertyId(property);
            return versionDataService.list(propertyId, query);
        }

        /**
         * 校验查询日期范围是否合法
         *
         * @param startDate         数据日期
         * @param dataAsOfStartDate 版本开始日期（yyyyMMdd）
         * @param dataAsOfEndDate   版本结束日期（yyyyMMdd）
         * @author zc
         * @date 2025/5/22
         */
        private void validateQueryDate(LocalDate startDate, Long dataAsOfStartDate, Long dataAsOfEndDate) {
            LocalDate dataAsOfStartLocalDate = DateTimeUtils.strToLocalDate(dataAsOfStartDate.toString(),
                    BaseConstant.DATE_FORMAT_YYYYMMDD);
            if (DateTimeUtils.numberOfDays(dataAsOfStartLocalDate, startDate) > 100) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(100),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The time span between dataAsOfStartDate and startDate cannot exceed 100 days");
            }
            LocalDate dataAsOfEndLocalDate = DateTimeUtils.strToLocalDate(dataAsOfEndDate.toString(),
                    BaseConstant.DATE_FORMAT_YYYYMMDD);
            if (dataAsOfEndLocalDate.isAfter(startDate)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_GREATER_THAN,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(MonthlyRevenueI18nConstant.DATA_AS_OF_END_DATE,
                                CommonFieldI18nConstant.START_DATE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The dataAsOfEndDate cannot be greater than startDate");
            }
        }
    }