# HistoryRateNextNDaysManualServiceImpl.java

    package cloud.wconnect.rateshop.service.revenue.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.pojo.bo.PropertyBO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.UpdateLogUtil;
    import cloud.wconnect.rateshop.cons.revenue.RevenueCons;
    import cloud.wconnect.rateshop.entity.rateshop.Hotel;
    import cloud.wconnect.rateshop.entity.rateshop.PropertyHotel;
    import cloud.wconnect.rateshop.entity.rateshop.Setting;
    import cloud.wconnect.rateshop.entity.revenue.HistoryRateNextNDaysManual;
    import cloud.wconnect.rateshop.mapper.revenue.HistoryRateNextNDaysManualMapper;
    import cloud.wconnect.rateshop.pojo.dto.revenue.HistoryRateNextNDaysManualDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.UpdateRateDTO;
    import cloud.wconnect.rateshop.service.rateshop.IHotelService;
    import cloud.wconnect.rateshop.service.rateshop.IPropertyHotelService;
    import cloud.wconnect.rateshop.service.rateshop.ISettingService;
    import cloud.wconnect.rateshop.service.revenue.IHistoryRateNextNDaysManualService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import jakarta.annotation.Resource;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 酒店当日的未来N天的历史房价 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2024-03-25
     */
    @Service
    @DS("rate_shop")
    public class HistoryRateNextNDaysManualServiceImpl extends ServiceImpl<HistoryRateNextNDaysManualMapper, HistoryRateNextNDaysManual> implements IHistoryRateNextNDaysManualService {

        @Autowired
        private IPropertyHotelService propertyHotelService;
        @Autowired
        private IHotelService hotelService;
        @Autowired
        private ISettingService settingService;
        @Autowired
        private UpdateLogUtil updateLogUtil;
        @Autowired
        private BeanMapper beanMapper;
        @Resource
        private HistoryRateNextNDaysManualMapper historyRateNextNDaysManualMapper;

        /**
         * 修改房价
         *
         * @param propertyDTO             资产
         * @param date                    原点日期（YYYY-MM-DD)
         * @param roomRates               房价
         * @param syncOtherProperty       是否同步到其他资产
         * @param userId                  操作人
         * @param propertiesByCurrentUser 当前操作人所属资产
         * @author yyt
         * @date 2024/3/27
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void updateRates(PropertyDTO propertyDTO, String date, List<UpdateRateDTO> roomRates, Boolean syncOtherProperty,
                                int userId, List<Integer> propertiesByCurrentUser) {
            //原点日期
            Integer targetDate = Integer.valueOf(date.replace("-", ""));
            Setting setting = settingService.getByPropertyId(propertyDTO.getId());
            Integer currencyId = setting.getCurrencyId();
            Integer containTaxFlag = setting.getContainTaxFlag();
            String pos = setting.getPos();

            //酒店集合
            Map<String, Integer> hotelIdBySymbol = hotelService.listEffectiveBySymbols(roomRates.stream()
                            .map(UpdateRateDTO::getHotelSymbol).collect(Collectors.toSet()))
                    .stream().collect(Collectors.toMap(Hotel::getSymbol, Hotel::getId));
            Collection<Integer> hotelIds = hotelIdBySymbol.values();

            //酒店 和 需要同步的资产ID集合
            Map<Integer, List<Integer>> hotelId2PropertyIds = new HashMap<>();
            if (syncOtherProperty) {// 如果勾选了同步其他资产，则需要同步更新其他资产下的当前酒店的房价信息
                //查询酒店在指定币种下的关联关系 且 当前操作人有权限的资产
                List<PropertyHotel> propertyHotelList = propertyHotelService.listByHotelIdsAndCurrencyId(hotelIds, currencyId, pos)
                        .stream().filter(propertyHotel -> propertiesByCurrentUser.contains(propertyHotel.getPropertyId()))
                        .toList();
                hotelId2PropertyIds = propertyHotelList.stream()
                        .collect(Collectors.groupingBy(PropertyHotel::getHotelId, Collectors.mapping(PropertyHotel::getPropertyId, Collectors.toList())));
            } else {
                for (Integer hotelId : hotelIds) {
                    hotelId2PropertyIds.put(hotelId, Collections.singletonList(propertyDTO.getId()));
                }
            }

            //要更新的房价集合
            ArrayList<HistoryRateNextNDaysManualDTO> historyRateNextNDaysManualDTOS = new ArrayList<>();
            for (UpdateRateDTO roomRate : roomRates) {
                //获取资产ID
                Integer hotelId = hotelIdBySymbol.get(roomRate.getHotelSymbol());
                //获取需要同步的资产ID集合
                List<Integer> updatePropertyIds = hotelId2PropertyIds.get(hotelId);
                for (Integer updatePropertyId : updatePropertyIds) {
                    //来源
                    int source = updatePropertyId.equals(propertyDTO.getId()) ? RevenueCons.SELF_PROPERTY_MANUAL : RevenueCons.ELSE_PROPERTY_MANUAL;
                    //入住日期
                    Integer checkInDate = Integer.valueOf(roomRate.getCheckInDate().replace("-", ""));

                    historyRateNextNDaysManualDTOS.add(new HistoryRateNextNDaysManualDTO().setTargetDate(targetDate).setHotelId(hotelId)
                            .setCheckInDate(checkInDate).setPropertyId(updatePropertyId).setPlatformId(roomRate.getPlatformId())
                            .setCurrencyId(currencyId).setRateType(roomRate.getRateType()).setSource(source)
                            .setPrice(roomRate.getPrice()).setPos(pos));
                }
            }

            //根据唯一索引查询手动维护的房价
            List<HistoryRateNextNDaysManual> historyRateNextNDaysManuals = historyRateNextNDaysManualMapper.listByUnique(historyRateNextNDaysManualDTOS);
            Map<String, HistoryRateNextNDaysManual> key2HistoryRateNextNDaysManual = historyRateNextNDaysManuals.stream()
                    .collect(Collectors.toMap(HistoryRateNextNDaysManual::generate, Function.identity()));

            int size = roomRates.size();
            List<HistoryRateNextNDaysManual> insertRates = Lists.newArrayListWithExpectedSize(size);
            List<HistoryRateNextNDaysManual> updateRates = Lists.newArrayListWithExpectedSize(size);

            for (HistoryRateNextNDaysManualDTO historyRateNextNDaysManualDTO : historyRateNextNDaysManualDTOS) {
                String uniqueKey = beanMapper.map(historyRateNextNDaysManualDTO, HistoryRateNextNDaysManual.class).generate();

                HistoryRateNextNDaysManual oriHistoryRateNextNDaysManual = key2HistoryRateNextNDaysManual.get(uniqueKey);
                if(Objects.isNull(oriHistoryRateNextNDaysManual)){//新增
                    HistoryRateNextNDaysManual insertEntity = new HistoryRateNextNDaysManual()
                            .setTenantId(propertyDTO.getTenantId())
                            .setPropertyId(historyRateNextNDaysManualDTO.getPropertyId())
                            .setHotelId(historyRateNextNDaysManualDTO.getHotelId()).setCurrencyId(historyRateNextNDaysManualDTO.getCurrencyId())
                            .setTargetDate(targetDate).setCheckInDate(historyRateNextNDaysManualDTO.getCheckInDate())
                            .setPlatformId(historyRateNextNDaysManualDTO.getPlatformId())
                            .setRateType(historyRateNextNDaysManualDTO.getRateType()).setSource(historyRateNextNDaysManualDTO.getSource())
                            .setPos(pos)
                            .setUpdateLog(updateLogUtil.get(userId));
                    if(containTaxFlag == Setting.BEFORE_TAX_RATE){
                        insertEntity.setRate(historyRateNextNDaysManualDTO.getPrice());
                    }else{
                        insertEntity.setAfterTaxRate(historyRateNextNDaysManualDTO.getPrice());
                    }
                    insertRates.add(insertEntity);
                }else{//修改
                    String updateLog = updateLogUtil.append(oriHistoryRateNextNDaysManual.getUpdateLog(), userId);

                    HistoryRateNextNDaysManual updateEntity = new HistoryRateNextNDaysManual()
                            .setId(oriHistoryRateNextNDaysManual.getId()).setPlatformId(historyRateNextNDaysManualDTO.getPlatformId())
                            .setSource(historyRateNextNDaysManualDTO.getSource()).setUpdateLog(updateLog)
                            //税前和税后房价先设置为原值
                            .setRate(oriHistoryRateNextNDaysManual.getRate())
                            .setAfterTaxRate(oriHistoryRateNextNDaysManual.getAfterTaxRate());

                    if(containTaxFlag == Setting.BEFORE_TAX_RATE){
                        updateEntity.setRate(historyRateNextNDaysManualDTO.getPrice());
                    }else{
                        updateEntity.setAfterTaxRate(historyRateNextNDaysManualDTO.getPrice());
                    }
                    updateRates.add(updateEntity);
                }
            }

            if (CollectionUtils.isNotEmpty(insertRates)) {
                saveBatch(insertRates);
            }
            if (CollectionUtils.isNotEmpty(updateRates)) {
                baseMapper.updateBatchById(updateRates);
            }
        }

        /**
         * 查询指定资产、指定酒店、指定原点日期、指定币种、指定销售地区手动维护的房价
         *
         * @param hotelId    酒店ID
         * @param currencyId 币种ID
         * @param targetDate 原点日期（yyyyMMdd）
         * @param propertyId 资产ID
         * @param pos        销售地区
         * @return
         */
        @Override
        public List<HistoryRateNextNDaysManual> list(Integer hotelId, Integer currencyId, Integer targetDate, Integer propertyId, String pos) {
            return list(new QueryWrapper<HistoryRateNextNDaysManual>().lambda()
                    .eq(HistoryRateNextNDaysManual::getPropertyId, propertyId)
                    .eq(HistoryRateNextNDaysManual::getHotelId, hotelId)
                    .eq(HistoryRateNextNDaysManual::getTargetDate, targetDate)
                    .eq(HistoryRateNextNDaysManual::getCurrencyId, currencyId)
                    .eq(HistoryRateNextNDaysManual::getPos, pos));
        }

    }