# RateShopHotelPriceHandle.java

    package cloud.wconnect.rateshop.springlistener.rateshop;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.exception.ListenerException;
    import cloud.wconnect.rateshop.entity.rateshop.RoomInfo;
    import cloud.wconnect.rateshop.mapper.rateshop.RoomInfoMapper;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.LatestRateDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.PlatformDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.PriceMessage;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.RoomDTO;
    import cloud.wconnect.rateshop.service.ILatestRateByRateTypeService;
    import cloud.wconnect.rateshop.service.rateshop.IPriceMessagePostProcessor;
    import cloud.wconnect.rateshop.service.rateshop.IRoomInfoService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionStatus;

    import java.math.BigDecimal;
    import java.util.*;
    import java.util.concurrent.Executor;
    import java.util.stream.Collectors;

    import static java.util.stream.Collectors.toSet;

    /**
     * 酒店房价 消费方
     *
     * @author lichen
     * @date 2023/6/6
     */
    @DS("rate_shop")
    @Component
    public class RateShopHotelPriceHandle {

        @Resource
        private RoomInfoMapper roomInfoMapper;
        @Resource
        private IRoomInfoService roomInfoService;
        @Resource
        private PlatformTransactionManager transactionManager;
        @Resource
        private TransactionDefinition transactionDefinition;
        @Resource
        private Executor executor;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private IPriceMessagePostProcessor priceMessagePostProcessor;

        /**
         * 处理房价消息
         *
         * @param priceMessage  房价消息
         * @param saveRoomInfos 新增的房型集合
         * @author lichen
         * @date 2024/4/3
         */
        public void handle(PriceMessage priceMessage, List<RoomInfo> saveRoomInfos) {
            //获取平台的房价
            List<PlatformDTO> platformDTOList = priceMessage.getData().stream().flatMap(priceDTO -> priceDTO.getPlatform().stream()).toList();

            //保存实际房间信息
            if (CollectionUtils.isNotEmpty(saveRoomInfos)) {
                TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
                try {
                    roomInfoService.saveBatch(saveRoomInfos);
                    transactionManager.commit(transaction);
                } catch (Exception e) {
                    transactionManager.rollback(transaction);
                    throw e;
                }
            }

            Map<String, Integer> hotelIdPlatformRoom2Id = matchRoomId(priceMessage.getHotelId(), platformDTOList, priceMessage.getPos());
            //PriceMessage -> List<LatestRate>
            List<LatestRateDTO> latestRateDTOS = convertToLatestRates(priceMessage, hotelIdPlatformRoom2Id);
            if (CollectionUtils.isEmpty(latestRateDTOS)) {
                return;
            }

            boolean isSavePrice = false;
            Collection<ILatestRateByRateTypeService> latestRateByRateTypeServices = applicationContext.getBeansOfType(ILatestRateByRateTypeService.class).values();
            for (ILatestRateByRateTypeService latestRateByRateTypeService : latestRateByRateTypeServices) {
                if (latestRateByRateTypeService.supports(priceMessage.getRateType())) {
                    //持久化房价
                    latestRateByRateTypeService.saveBatchDynamicTableName(latestRateDTOS);
                    isSavePrice = true;
                    break;
                }
            }

            if (!isSavePrice) {
                throw new ListenerException("processing class was not found, rateType=" + priceMessage.getRateType());
            }

            //执行房价后置处理器
            executor.execute(() -> priceMessagePostProcessor.process(priceMessage));
        }

        /**
         * 把PriceMessage 转化成 List<LatestRateDTO>
         *
         * @param priceMessage           房价消息
         * @param hotelIdPlatformRoom2Id roomId信息
         * @return java.util.List<cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.LatestRateDTO>
         * @author lichen
         * @date 2023/6/6
         */
        private List<LatestRateDTO> convertToLatestRates(PriceMessage priceMessage, Map<String, Integer> hotelIdPlatformRoom2Id) {
            List<LatestRateDTO> latestRates = new ArrayList<>();

            //PriceMessage -> List<LatestRate>
            priceMessage.getData().forEach(priceData -> {
                Long checkInDate = priceData.getCheckInDate();
                List<PlatformDTO> platformDtos = priceData.getPlatform();
                if (CollectionUtils.isNotEmpty(platformDtos)) {
                    //非No data
                    platformDtos.forEach(platformDTO -> {
                        Integer platformId = platformDTO.getId();
                        List<RoomDTO> roomDtos = platformDTO.getRoom();
                        if (CollectionUtils.isNotEmpty(roomDtos)) {
                            roomDtos.forEach(roomDto -> {
                                //正常
                                LatestRateDTO latestRate = new LatestRateDTO();
                                latestRate.setCheckInDate(checkInDate);
                                latestRate.setCurrencyId(priceMessage.getCurrencyId());
                                latestRate.setHotelId(priceMessage.getHotelId());
                                latestRate.setPlatformId(platformId);
                                latestRate.setQueryDate(priceMessage.getQueryDate());
                                latestRate.setRate(roomDto.getRate());
                                latestRate.setAfterTaxRate(roomDto.getAfterTaxRate());
                                if (StringUtils.isNotBlank(roomDto.getTitle())) {
                                    String key = new RoomInfo()
                                            .setHotelId(priceMessage.getHotelId())
                                            .setPlatformId(platformId)
                                            .setRoom(roomDto.getTitle())
                                            .setPos(priceMessage.getPos())
                                            .generateKey();

                                    latestRate.setRoomId(hotelIdPlatformRoom2Id.get(key));
                                } else {
                                    //房型名称为空时，记为0
                                    latestRate.setRoomId(BaseConstant.ZERO);
                                }
                                latestRate.setDatasourceId(priceMessage.getDatasourceId());
                                latestRate.setPos(priceMessage.getPos());
                                latestRates.add(latestRate);
                            });
                        } else {
                            //sold out
                            LatestRateDTO latestRate = new LatestRateDTO();
                            latestRate.setCheckInDate(checkInDate);
                            latestRate.setCurrencyId(priceMessage.getCurrencyId());
                            latestRate.setHotelId(priceMessage.getHotelId());
                            latestRate.setPlatformId(platformId);
                            latestRate.setQueryDate(priceMessage.getQueryDate());
                            //sold out时，房型和价格记录为0
                            latestRate.setRate(BigDecimal.ZERO);
                            latestRate.setAfterTaxRate(BigDecimal.ZERO);
                            latestRate.setRoomId(BaseConstant.ZERO);
                            latestRate.setDatasourceId(priceMessage.getDatasourceId());
                            latestRate.setPos(priceMessage.getPos());
                            latestRates.add(latestRate);
                        }
                    });
                }
            });
            return latestRates;
        }

        /**
         * 匹配roomId
         *
         * @param hotelId         酒店id
         * @param platformDTOList 平台集合
         * @param pos             销售地区
         * @return java.util.Map<java.lang.String, java.lang.Integer>
         * @author lichen
         * @date 2023/6/6
         */
        private Map<String, Integer> matchRoomId(Integer hotelId, List<PlatformDTO> platformDTOList, String pos) {
            //刷选出roomTitle
            Map<Integer, Set<String>> platformId2RoomTitle = platformDTOList.stream()
                    .collect(Collectors.toMap(
                            PlatformDTO::getId,
                            p -> p.getRoom().stream().map(RoomDTO::getTitle).collect(toSet()),
                            (v1, v2) -> {
                                v1.addAll(v2);
                                return v1;
                            })
                    );
            //消息中的roomInfo信息
            List<RoomInfo> msgRoomInfos = new ArrayList<>();

            //platformId2RoomTitle -> roomInfos
            platformId2RoomTitle.forEach((platformId, titles) ->
                    titles.forEach(title -> {
                        if (StringUtils.isNotBlank(title)) {
                            msgRoomInfos.add(new RoomInfo()
                                    .setPlatformId(platformId)
                                    .setRoom(title)
                                    .setHotelId(hotelId)
                                    .setPos(pos));
                        }
                    })
            );

            if (CollectionUtils.isEmpty(msgRoomInfos)) {
                return Collections.emptyMap();
            }

            //List<RoomInfo> -> Map<String, Integer>
            return roomInfoMapper.selectByPlatformIdAndRoomList(hotelId, msgRoomInfos)
                    .stream().collect(Collectors.toMap(RoomInfo::generateKey, RoomInfo::getId));
        }

    }