# RoomController.java

    package cloud.wconnect.rateshop.controller.rateshop;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.jsonview.Views;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.rateshop.cons.rateshop.PlatformEnum;
    import cloud.wconnect.rateshop.cons.rateshop.RateShopFieldI18nConstant;
    import cloud.wconnect.rateshop.entity.rateshop.*;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.RateConfigDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.roominfo.RoomInfoDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.roomtype.RoomTypeDTO;
    import cloud.wconnect.rateshop.service.rateshop.*;
    import com.fasterxml.jackson.annotation.JsonView;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.util.Assert;
    import org.springframework.web.bind.annotation.*;

    import jakarta.validation.Valid;

    import java.text.MessageFormat;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * 房间类型 控制层
     *
     * @author lichen
     * @date 2023/6/20
     */
    @RestController
    @RequestMapping("/rate-shop/room")
    public class RoomController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IRoomTypeService roomTypeService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IHotelService hotelService;
        @Autowired
        private IPropertyHotelService propertyHotelService;
        @Autowired
        private IRoomInfoService roomInfoService;
        @Autowired
        private IRoomTypeInfoRelService roomTypeInfoRelService;
        @Autowired
        private ISettingService settingService;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询平台下房间类型的信息
         *
         * @param property   资产标识
         * @param platformId 平台id
         * @return java.util.List<cloud.wconnect.rateshop.pojo.dto.rateshop.roomtype.RoomTypeDTO>
         * @author lichen
         * @date 2024/7/10
         */
        @ApiId(353)
        @GetMapping("/type")
        @JsonView({Views.ListView.class})
        public List<RoomTypeDTO> listRoomType(String property, @RequestParam("platform") Integer platformId) {
            int propertyId = sessionContext.getPropertyId(property);
            //查询资产的rate shop配置
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);
            //查询平台下的类型信息
            List<RoomType> roomTypes = roomTypeService.listByPlatformIdsAndPos(propertyId, Collections.singletonList(platformId), setting.getPos());
            //List<RoomType> -> List<RoomTypeDTO>
            return beanMapper.mapList(roomTypes, RoomTypeDTO.class, (item, dto) -> {
                dto.setOrder(item.getSeq());
            });
        }

        /**
         * 保存类型
         *
         * @param property   资产标识
         * @param platformId 平台id
         * @return java.util.Map
         * @author lichen
         * @date 2023/6/20
         */
        @ApiId(354)
        @PostMapping("/type")
        public Map saveRoomType(String property, @RequestParam("platform") Integer platformId,
                                @RequestBody @Valid RoomTypeDTO roomTypeDTO) {
            Integer propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            roomTypeDTO.setPlatformId(platformId);
            roomTypeDTO.setPos(setting.getPos());

            //校验title是否唯一
            roomTypeService.checkTitleSole(propertyId, roomTypeDTO, null);
            //新增roomType
            return roomTypeService.save(propertyId, roomTypeDTO, sessionContext.getLoginUserId());
        }

        /**
         * 修改类型
         *
         * @param id          类型id
         * @param property    资产标识
         * @param platformId  平台id
         * @param roomTypeDTO 类型信息
         * @return java.util.Map
         * @author lichen
         * @date 2023/6/20
         */
        @ApiId(355)
        @PutMapping("/type/{id:\\d+}")
        public Map updateRoomType(@PathVariable Integer id, String property,
                                  @RequestParam("platform") Integer platformId,
                                  @RequestBody @Valid RoomTypeDTO roomTypeDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            //校验类型是否存在
            RoomType roomType = roomTypeService.checkExist(id);
            //校验类型是否归属于指定资产、指定平台、指定销售地区
            roomTypeService.checkBelong(roomType, propertyId, platformId, setting.getPos());

            roomTypeDTO.setPlatformId(platformId);
            roomTypeDTO.setPos(setting.getPos());

            //校验title是否唯一
            roomTypeService.checkTitleSole(propertyId, roomTypeDTO, id);
            //修改roomType的标题
            roomTypeService.updateById(roomType.setTitle(roomTypeDTO.getTitle()));
            return Collections.emptyMap();
        }

        /**
         * 删除类型
         *
         * @param id         类型id
         * @param property   资产标识
         * @param platformId 平台id
         * @return java.util.Map
         * @author lichen
         * @date 2023/6/20
         */
        @ApiId(356)
        @DeleteMapping("/type/{id:\\d+}")
        public Map deleteType(@PathVariable Integer id, String property,
                              @RequestParam("platform") Integer platformId) {
            int propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            //校验类型是否存在
            RoomType roomType = roomTypeService.checkExist(id);
            //校验类型是否归属于指定资产、指定平台、指定销售地区
            roomTypeService.checkBelong(roomType, propertyId, platformId, setting.getPos());

            //删除类型
            roomTypeService.delete(roomType);
            return Collections.emptyMap();
        }

        /**
         * 排序类型
         *
         * @param id         类型id
         * @param type       排序类型 1：上移 2：下移
         * @param property   资产标识
         * @param platformId 平台id
         * @return java.util.Map
         * @author lichen
         * @date 2023/6/21
         */
        @ApiId(367)
        @PostMapping("/type/{id:\\d+}/order/{type:\\d+}")
        public Map orderRoomType(@PathVariable Integer id, @PathVariable Integer type, String property,
                                 @RequestParam("platform") Integer platformId) {
            int propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            //校验类型是否存在
            RoomType roomType = roomTypeService.checkExist(id);
            //校验类型是否归属于指定资产、指定平台、指定销售地区
            roomTypeService.checkBelong(roomType, propertyId, platformId, setting.getPos());

            //校验type
            List<Integer> typeList = Arrays.asList(RoomType.UP, RoomType.DOWN);
            Assert.isTrue(typeList.contains(type), "The type must be 1:up or 2:down");
            //排序
            roomTypeService.order(roomType, type);
            return Collections.emptyMap();
        }

        /**
         * 查询房型信息
         *
         * @param property    资产标识
         * @param hotelSymbol 酒店标识
         * @param platformId  平台id
         * @return java.util.List<cloud.wconnect.rateshop.pojo.dto.roominfo.RoomInfoDTO>
         * @author lichen
         * @date 2023/6/20
         */
        @ApiId(357)
        @GetMapping
        public List<RoomInfoDTO> listRoomInfo(String property, @RequestParam("hotel") String hotelSymbol,
                                              @RequestParam("platform") Integer platformId) {
            int propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            //校验目标酒店是否存在 且 状态正常
            Hotel hotel = hotelService.checkHotel(hotelSymbol);
            //校验酒店是否和资产有关系
            propertyHotelService.checkAffiliation(hotel.getId(), propertyId);
            //查询酒店在指定平台、指定地区的房型信息
            List<RoomInfo> roomInfos = roomInfoService.listByHotelIdAndPlatformIdAndPos(hotel.getId(), platformId, setting.getPos());

            Map<Integer, Integer> roomInfoId2RoomTypeId = Collections.emptyMap();
            List<Integer> roomInfoIds = roomInfos.stream().map(RoomInfo::getId).toList();
            if (CollectionUtils.isNotEmpty(roomInfoIds)) {
                //根据酒店的房型ID，查询在指定资产下关联的类型
                roomInfoId2RoomTypeId = roomTypeInfoRelService.listByRoomInfoId(propertyId, roomInfoIds)
                        .stream()
                        .collect(Collectors.toMap(RoomTypeInfoRel::getRoomInfoId, RoomTypeInfoRel::getRoomTypeId));
            }
            final Map<Integer, Integer> finalRoomInfoId2RoomTypeId = roomInfoId2RoomTypeId;

            //List<RoomInfo> -> List<RoomInfoDTO>
            return beanMapper.mapList(roomInfos, RoomInfoDTO.class, (item, dto) -> {
                dto.setType(finalRoomInfoId2RoomTypeId.getOrDefault(item.getId(), 0));
            });
        }

        /**
         * 房型分配类型
         *
         * @param rid               房型
         * @param tid               类型
         * @param property          资产标识
         * @param targetHotelSymbol 目标酒店标识
         * @param platformId        平台id
         * @return java.util.Map
         * @author lichen
         * @date 2023/6/20
         */
        @ApiId(358)
        @PutMapping("/{rid:\\d+}/type/{tid:\\d+}")
        public Map roomInfoAssignRoomType(@PathVariable Integer rid, @PathVariable Integer tid,
                                          String property, @RequestParam("target") String targetHotelSymbol,
                                          @RequestParam("platform") Integer platformId) {
            int propertyId = sessionContext.getPropertyId(property);
            Setting setting = settingService.getByPropertyId(propertyId);
            //校验资产是否配置指定平台
            checkPropertyIsConfigPlatformId(setting, platformId);

            //校验目标酒店是否存在 且 状态正常
            Hotel hotel = hotelService.checkHotel(targetHotelSymbol);
            //校验酒店是否和资产有关系
            propertyHotelService.checkAffiliation(hotel.getId(), propertyId);

            //校验房型是否存在
            RoomInfo roomInfo = roomInfoService.checkExist(rid);
            //校验房型是否归属于酒店，平台，销售地区
            checkRoomInfoBelong(roomInfo, hotel.getId(), platformId, setting.getPos());

            if (tid != 0) {
                //校验类型是否存在
                RoomType roomType = roomTypeService.checkExist(tid);
                //校验类型是否归属于指定资产、指定平台、指定销售地区
                roomTypeService.checkBelong(roomType, propertyId, platformId, setting.getPos());
            }

            //维护关系
            roomTypeInfoRelService.save(propertyId, tid, rid, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 校验房型是否归属于酒店，校验房型是否归属于平台
         *
         * @param roomInfo   房型信息
         * @param hotelId    酒店id
         * @param platformId 平台id
         * @param pos        销售地区
         * @return void
         * @author lichen
         * @date 2023/6/20
         */
        private void checkRoomInfoBelong(RoomInfo roomInfo, Integer hotelId, Integer platformId, String pos) {
            if (!roomInfo.getPlatformId().equals(platformId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "The roomInfo must belong to the platform, platformId = "
                                + platformId + ", roomInfo platformId = " + roomInfo.getPlatformId());
            }
            if (!roomInfo.getHotelId().equals(hotelId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "The roomInfo must belong to the target hotel, target hotelId = "
                                + hotelId + ", roomInfo hotelId = " + roomInfo.getHotelId());
            }
            if (!roomInfo.getPos().equals(pos)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The roomInfo not belong to pos, roomInfoId:{0}, roomInfoBelongPos:{1}, currentPos:{2}",
                                roomInfo.getId(), roomInfo.getPos(), pos));
            }
        }

        /**
         * 校验资产是否配置指定平台
         *
         * @param setting    资产的rate shop配置
         * @param platformId 平台ID
         * @return void
         * @author lichen
         * @date 2024/7/10
         */
        private void checkPropertyIsConfigPlatformId(Setting setting, Integer platformId) {
            List<RateConfigDTO> rateConfigDTOS = JsonUtils.convertStringToList(objectMapper, setting.getRateConfig(), RateConfigDTO.class);
            //查询资产配置的平台ID集合
            Set<Integer> platformIdList = rateConfigDTOS.stream().map(RateConfigDTO::getPlatformIds)
                    .flatMap(Collection::stream).collect(Collectors.toSet());
            if (!platformIdList.contains(platformId)) {
                List<String> platformI18nCodeList = Arrays.stream(PlatformEnum.values()).filter(item -> platformIdList.contains(item.getCode()))
                        .map(PlatformEnum::getI18nCode).toList();
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(RateShopFieldI18nConstant.PLATFORM, platformI18nCodeList),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The property is not configured platform; propertyConfigPlatformIds:{0}, platformId:{1}",
                                platformIdList, platformId));
            }
        }

    }