# TaCompareReportCommonServiceImpl.java

    package cloud.wconnect.finance.service.ta.impl;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.finance.cons.ta.TaRecordsStatusEnum;
    import cloud.wconnect.finance.entity.ta.TaCreditCard;
    import cloud.wconnect.finance.entity.ta.TaOta;
    import cloud.wconnect.finance.entity.ta.TaPortal;
    import cloud.wconnect.finance.entity.ta.TaRecordsStatus;
    import cloud.wconnect.finance.pojo.dto.ta.TaPortalJsonDTO;
    import cloud.wconnect.finance.pojo.dto.ta.compare.TaCompareOtaContractPriceConfigDTO;
    import cloud.wconnect.finance.pojo.dto.ta.compare.TaComparePortalContractPriceConfigDTO;
    import cloud.wconnect.finance.service.ta.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Service;

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

    /**
     * @author zc
     * @date 2023/11/19
     */
    @Service
    @DS("finance")
    public class TaCompareReportCommonServiceImpl implements ITaCompareReportCommonService {
        @Resource
        private ITaRecordsStatusService recordsStatusService;
        @Resource
        private ITaCreditCardService creditCardService;
        @Resource
        private ITaComparePortalContractPriceConfigService portalContractPriceConfigService;
        @Resource
        private ITaCompareOtaContractPriceConfigService otaContractPriceConfigService;
        @Resource
        private ObjectMapper objectMapper;

        /**
         * Credit Card 比对 Portal 数据
         *
         * @param property            资产
         * @param creditCardList      creditCard
         * @param portalList          portal
         * @param recordsStatusList   records
         * @param needSaveRecordsList 需要保存的比对记录数据
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void creditCardComparePortal(PropertyDTO property, List<TaCreditCard> creditCardList, List<TaPortal> portalList,
                                            List<TaRecordsStatus> recordsStatusList, List<TaRecordsStatus> needSaveRecordsList) {
            if (CollectionUtils.isEmpty(portalList)) {
                return;
            }

            Map<String, TaRecordsStatus> contrastStr2Records = recordsStatusList.stream()
                    .collect(Collectors.toMap(TaRecordsStatus::getContrastStr, Function.identity(), (v1, v2) -> v1));

            Map<String, List<TaCreditCard>> contrastStr2CreditCards = creditCardList.stream()
                    .filter(item -> StringUtils.isNotBlank(item.getCardNo()))
                    .collect(Collectors.groupingBy(TaCreditCard::getContrastPortalStr));

            // 需要删除的 CreditCard 数据，这里是总结出来入住多天的数据，相同的 CreditCard 数据中离开日期最小的数据，这部分数据需要删除
            List<TaCreditCard> needDeleteCreditCardList = Lists.newArrayList();
            for (TaPortal portal : portalList) {
                // 获取已存在的 Records 数据
                TaRecordsStatus record = contrastStr2Records.get(portal.getContrastStr());

                // 获取对应的 CreditCard 数据
                List<TaCreditCard> creditCardListByPortal = contrastStr2CreditCards.getOrDefault(portal.getContrastStr(), Collections.emptyList());

                if (Objects.isNull(record)) {
                    // 生成需要新增的 Records Status 数据
                    needSaveRecordsList.add(generateNewRecords(portal, creditCardListByPortal));
                } else {
                    if (CollectionUtils.isEmpty(creditCardListByPortal)) {
                        continue;
                    }
                    // 生成需要更新的 Records Status 数据
                    needSaveRecordsList.add(generateUpdateRecords(portal, creditCardListByPortal, record, needDeleteCreditCardList));

                    // 移除有效的 Record 数据，剩下的是需要删除的无用的 Record 数据
                    recordsStatusList.remove(record);
                }

                // 将匹配到的 Credit Card 数据从文件中清除，目的是循环结束后，文件中保留的是未占用的数据
                creditCardList.removeAll(creditCardListByPortal);
            }
            // 删除需要删除的 Credit Card 数据
            creditCardService.removeByIds(needDeleteCreditCardList.stream().map(TaCreditCard::getId).toList());
        }

        /**
         * 生成需要更新的 Records Status 数据
         *
         * @param portal                   Portal 数据
         * @param creditCardList           Credit Card 数据
         * @param record                   Records Status 数据
         * @param needDeleteCreditCardList 需要删除的 CreditCard 数据
         * @return TaRecordsStatus
         * @author zc
         * @date 2023/11/19
         */
        private TaRecordsStatus generateUpdateRecords(TaPortal portal, List<TaCreditCard> creditCardList,
                                                      TaRecordsStatus record, List<TaCreditCard> needDeleteCreditCardList) {
            TaPortalJsonDTO portalJsonDTO = JsonUtils.convertStringToObject(objectMapper, portal.getDetails(), TaPortalJsonDTO.class);
            // 汇总已存在的 Credit Card 和报告中的 Credit Card 数据
            summaryRecordCreditCard(creditCardList, record, needDeleteCreditCardList);

            record.setPId(portal.getId());
            record.setName(portal.getName());
            // oIds
            String oIds = StringUtils.join(record.getCreditCards().stream()
                    .map(TaCreditCard::getId).sorted().toList(), ",");
            record.setOIds(oIds);
            record.setAirline(portalJsonDTO.getAirline());

            // 计算 RoomRevenueTotal
            BigDecimal roomRevenueTotal = record.getCreditCards().stream().map(TaCreditCard::getRoomRevenue)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            if (roomRevenueTotal.compareTo(portal.getRate().multiply(new BigDecimal(portal.getRooms().toString()))
                    .multiply(new BigDecimal(portal.getNights().toString()))) != 0) {
                record.setStatus(TaRecordsStatusEnum.PRICE_VARIANCE.getCode());
            } else {
                record.setStatus(TaRecordsStatusEnum.MATCHED.getCode());
            }

            // 汇总 RoomsNo
            String roomsNo = StringUtils.join(record.getCreditCards().stream()
                    .map(TaCreditCard::getRoomNo).toList(), ",");
            record.setRoomsNo(roomsNo);

            return record;
        }

        /**
         * 汇总已存在的 Credit Card 和报告中的 Credit Card 数据
         *
         * @param creditCardList           Credit Card 数据
         * @param record                   Records Status 数据
         * @param needDeleteCreditCardList 需要删除的 CreditCard 数据
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void summaryRecordCreditCard(List<TaCreditCard> creditCardList, TaRecordsStatus record, List<TaCreditCard> needDeleteCreditCardList) {
            List<TaCreditCard> creditCardListByRecord = Lists.newArrayList(record.getCreditCards());
            Map<String, TaCreditCard> existStr2CreditCardByRecord = creditCardListByRecord.stream()
                    .collect(Collectors.toMap(TaCreditCard::getExistStr, v -> v, (v1, v2) -> v2));

            for (TaCreditCard creditCard : creditCardList) {
                TaCreditCard existCreditCard = existStr2CreditCardByRecord.get(creditCard.getExistStr());
                if (Objects.isNull(existCreditCard)) {
                    creditCardListByRecord.add(creditCard);
                } else {
                    if (existCreditCard.getId().equals(creditCard.getId())) {
                        continue;
                    }
                    // 判断已存在记录的数据日期，如果是未来日期的话，不能修改
                    if (existCreditCard.getDataDate() > creditCard.getDataDate()) {
                        needDeleteCreditCardList.add(creditCard);
                    } else {
                        needDeleteCreditCardList.add(existCreditCard);
                        creditCardListByRecord.remove(existCreditCard);
                        creditCardListByRecord.add(creditCard);
                    }
                }
            }
            record.setCreditCards(creditCardListByRecord);
        }

        /**
         * 生成需要新增的 Records Status 数据
         *
         * @param portal         Portal 数据
         * @param creditCardList Credit Card 数据
         * @return TaRecordsStatus
         * @author zc
         * @date 2023/11/19
         */
        private TaRecordsStatus generateNewRecords(TaPortal portal, List<TaCreditCard> creditCardList) {
            TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                    portal.getDetails(), TaPortalJsonDTO.class);

            TaRecordsStatus newRecords = new TaRecordsStatus()
                    .setPropertyId(portal.getPropertyId())
                    .setPId(portal.getId())
                    .setStatusDetail(StringUtils.EMPTY)
                    .setCategory(TaRecordsStatus.CATEGORY_OTHER)
                    .setCreateId(BaseConstant.ZERO)
                    .setOperatorId(BaseConstant.ZERO)
                    .setArrivalDate(portal.getArrivalDate())
                    .setName(portal.getName())
                    .setAirline(taPortalJsonDTO.getAirline())
                    .setType(TaRecordsStatus.PLATFORM_TYPE_TA)
                    .setCanChoose(Boolean.FALSE)
                    .setConfirmation(StringUtils.EMPTY)
                    .setCreditCards(creditCardList);
            if (CollectionUtils.isEmpty(creditCardList)) {
                newRecords.setOIds(StringUtils.EMPTY)
                        .setStatus(TaRecordsStatusEnum.MISS_PMS.getCode())
                        .setRoomsNo(StringUtils.EMPTY)
                        .setCardNo(portal.getCardNo());
            } else {
                List<Long> creditCardIdsByReport = creditCardList.stream()
                        .map(TaCreditCard::getId).sorted().toList();
                newRecords.setOIds(StringUtils.join(creditCardIdsByReport, ","));

                BigDecimal roomRevenueTotal = creditCardList.stream().map(TaCreditCard::getRoomRevenue)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                if (roomRevenueTotal.compareTo(portal.getRate().multiply(new BigDecimal(portal.getRooms().toString()))
                        .multiply(new BigDecimal(portal.getNights().toString()))) != 0) {
                    newRecords.setStatus(TaRecordsStatusEnum.PRICE_VARIANCE.getCode());
                } else {
                    newRecords.setStatus(TaRecordsStatusEnum.MATCHED.getCode());
                }

                List<String> roomNoListByCreditCardReport = creditCardList.stream()
                        .map(TaCreditCard::getRoomNo).toList();
                newRecords.setRoomsNo(StringUtils.join(roomNoListByCreditCardReport, ","))
                        .setCardNo(creditCardList.get(0).getCardNo());
            }
            return newRecords;
        }

        /**
         * 处理 Portal 合约价
         *
         * @param property               资产
         * @param targetDate             目标日期
         * @param creditCardListByReport Credit Card By Report 数据
         * @param needSaveRecordsList    需要保存的比对记录信息
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void handlePortalContractPrice(PropertyDTO property, long targetDate,
                                              List<TaCreditCard> creditCardListByReport,
                                              List<TaRecordsStatus> recordsStatusList,
                                              List<TaRecordsStatus> needSaveRecordsList) {
            if (CollectionUtils.isEmpty(creditCardListByReport)) {
                return;
            }
            // 获取当前有效的 Portal 合约价集合
            TaComparePortalContractPriceConfigDTO effectiveContractPrice =
                    portalContractPriceConfigService.getCurrent(property.getTenantId(),
                            property.getId(), targetDate);
            if (Objects.isNull(effectiveContractPrice)) {
                // 若没有有效的 Portal 合约价信息，则直接 return，后续逻辑不再处理
                return;
            }
            List<BigDecimal> portalContractPriceList = JsonUtils.convertStringToList(objectMapper,
                            effectiveContractPrice.getContractPrice(), String.class).stream()
                    .map(BigDecimal::new).toList();

            // 过滤出确认的 Credit Card 数据
            List<TaCreditCard> confirmTaCreditCardListByReport = Lists.newArrayList(creditCardListByReport.stream()
                    .filter(item -> {
                        BigDecimal roomRevenue = item.getRoomRevenue();
                        if (roomRevenue.compareTo(BigDecimal.ZERO) != 0) {
                            long count = portalContractPriceList.stream()
                                    .filter(contractPrice ->
                                            roomRevenue.remainder(contractPrice)
                                                    .compareTo(BigDecimal.ZERO) == 0).count();
                            return count > 0L;
                        }
                        return false;
                    }).toList());
            if (CollectionUtils.isEmpty(confirmTaCreditCardListByReport)) {
                return;
            }

            // 处理确认的 Credit Card 数据
            handleConfirmTaCreditCards(property, TaRecordsStatus.CATEGORY_OTHER, confirmTaCreditCardListByReport,
                    recordsStatusList, needSaveRecordsList);

            // 将匹配到的 Credit Card 数据从报告中清除
            creditCardListByReport.removeAll(confirmTaCreditCardListByReport);
        }

        /**
         * 处理确认的 Credit Card 数据
         *
         * @param property                        资产
         * @param category                        数据种类 0：其他；1：OTA
         * @param confirmTaCreditCardListByReport 确认的 Credit Card 数据
         * @param needSaveRecordsList             需要保存的比对记录信息
         * @author zc
         * @date 2023/11/19
         */
        private void handleConfirmTaCreditCards(PropertyDTO property, int category,
                                                List<TaCreditCard> confirmTaCreditCardListByReport,
                                                List<TaRecordsStatus> recordsStatusList,
                                                List<TaRecordsStatus> needSaveRecordsList) {
            // 需要删除的 CreditCard 数据，这里是总结出来入住多天的数据，相同的 CreditCard 数据中离开日期最小的数据，这部分数据需要删除
            List<TaCreditCard> needDeleteCreditCardList = Lists.newArrayList();
            Map<String, List<TaCreditCard>> contrastStr2CreditCards = confirmTaCreditCardListByReport.stream()
                    .filter(item -> StringUtils.isNotBlank(item.getCardNo()))
                    .collect(Collectors.groupingBy(TaCreditCard::getContrastPortalStr));
            for (TaRecordsStatus record : recordsStatusList) {
                List<TaCreditCard> creditCardList = contrastStr2CreditCards.get(record.getContrastStr());
                if (CollectionUtils.isNotEmpty(creditCardList)) {
                    // 汇总已存在的 Credit Card 和报告中的 Credit Card 数据
                    summaryRecordCreditCard(creditCardList, record, needDeleteCreditCardList);
                    // oIds
                    String oIds = StringUtils.join(record.getCreditCards().stream()
                            .map(TaCreditCard::getId).sorted().toList(), ",");
                    record.setOIds(oIds);

                    // roomNos
                    String roomNos = StringUtils.join(record.getCreditCards().stream()
                            .map(TaCreditCard::getRoomNo).collect(Collectors.toSet()), ",");
                    record.setRoomsNo(roomNos);

                    // names
                    String names = StringUtils.join(record.getCreditCards().stream()
                            .map(TaCreditCard::getName).collect(Collectors.toSet()), ",");
                    record.setName(names);
                    needSaveRecordsList.add(record);
                    // 将匹配到的 Credit Card 数据从文件中清除，目的是循环结束后，文件中保留的是未占用的数据
                    confirmTaCreditCardListByReport.removeAll(creditCardList);
                }
            }
            // 删除需要删除的 Credit Card 数据
            creditCardService.removeByIds(needDeleteCreditCardList.stream().map(TaCreditCard::getId).toList());

            contrastStr2CreditCards = confirmTaCreditCardListByReport.stream()
                    .filter(item -> StringUtils.isNotBlank(item.getCardNo()))
                    .collect(Collectors.groupingBy(TaCreditCard::getContrastPortalStr));
            for (Map.Entry<String, List<TaCreditCard>> entry : contrastStr2CreditCards.entrySet()) {
                List<TaCreditCard> creditCardsByEntry = entry.getValue();
                TaCreditCard creditCardByEntry = creditCardsByEntry.get(0);
                // oIds
                String oIds = StringUtils.join(creditCardsByEntry.stream()
                        .map(TaCreditCard::getId).sorted().toList(), ",");
                // roomNos
                String roomNos = StringUtils.join(creditCardsByEntry.stream()
                        .map(TaCreditCard::getRoomNo).collect(Collectors.toSet()), ",");
                // names
                String names = StringUtils.join(creditCardsByEntry.stream()
                        .map(TaCreditCard::getName).collect(Collectors.toSet()), ",");
                needSaveRecordsList.add(TaRecordsStatus.builder()
                        .propertyId(property.getId())
                        .pId(BaseConstant.LONG_ZERO)
                        .oIds(oIds)
                        .status(TaRecordsStatusEnum.MISS_TA.getCode())
                        .statusDetail(StringUtils.EMPTY)
                        .category(category)
                        .createId(BaseConstant.ZERO)
                        .operatorId(BaseConstant.ZERO)
                        .arrivalDate(creditCardByEntry.getArrivalDate())
                        .name(names)
                        .roomsNo(roomNos)
                        .airline(StringUtils.EMPTY)
                        .type(TaRecordsStatus.PLATFORM_TYPE_TA)
                        .canChoose(Boolean.TRUE)
                        .confirmation(StringUtils.EMPTY)
                        .cardNo(creditCardByEntry.getCardNo())
                        .creditCards(creditCardsByEntry)
                        .build());
            }
        }

        /**
         * 比对 OTA 数据
         *
         * @param property               资产
         * @param targetDate             目标日期
         * @param creditCardListByReport Credit Card By Report 数据
         * @param otaList                OTA 数据
         * @param needSaveRecordsList    需要保存的比对记录信息
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void creditCardCompareOta(PropertyDTO property, long targetDate,
                                         List<TaCreditCard> creditCardListByReport,
                                         List<TaOta> otaList, List<TaRecordsStatus> recordsStatusList,
                                         List<TaRecordsStatus> needSaveRecordsList) {
            if (CollectionUtils.isEmpty(creditCardListByReport)) {
                return;
            }
            // 获取当前有效的 OTA 合约价集合
            TaCompareOtaContractPriceConfigDTO effectiveContractPrice = otaContractPriceConfigService.getCurrent(property.getTenantId(),
                    property.getId(), targetDate);
            if (Objects.isNull(effectiveContractPrice)) {
                // 若没有有效的 OTA 合约价信息，则直接 return
                return;
            }

            List<BigDecimal> otaContractPriceList = JsonUtils.convertStringToList(objectMapper,
                            effectiveContractPrice.getContractPrice(), String.class).stream()
                    .map(BigDecimal::new).toList();

            Map<String, TaOta> contrastOtaStr2TaOta = otaList.stream()
                    .collect(Collectors.toMap(TaOta::getContrastOtaStr, Function.identity(), (v1, v2) -> v1));

            List<Long> needDeleteRecordOIds = Lists.newArrayList();
            List<TaCreditCard> confirmTaCreditCardListByReport = Lists.newArrayList(creditCardListByReport.stream()
                    .filter(item -> {
                        BigDecimal roomRevenue = item.getRoomRevenue();
                        if (roomRevenue.compareTo(BigDecimal.ZERO) != 0) {
                            long count = otaContractPriceList.stream()
                                    .filter(contractPrice ->
                                            roomRevenue.remainder(contractPrice)
                                                    .compareTo(BigDecimal.ZERO) == 0).count();
                            return count > BaseConstant.LONG_ZERO;
                        }
                        return false;
                    }).filter(item -> {
                        TaOta taOta = contrastOtaStr2TaOta.get(item.getContrastOtaStr());
                        if (Objects.nonNull(taOta)) {
                            needDeleteRecordOIds.add(item.getId());
                        }
                        return Objects.isNull(taOta);
                    }).toList());

            if (CollectionUtils.isNotEmpty(needDeleteRecordOIds)) {
                List<TaRecordsStatus> recordsStatusListByOIds = recordsStatusService.listByCreditCardIds(needDeleteRecordOIds,
                        Lists.newArrayList(TaRecordsStatusEnum.MISS_TA.getCode()));
                List<Long> needDeleteRecordIds = Lists.newArrayList();
                recordsStatusListByOIds.forEach(item -> {
                    List<Long> oIds = Lists.newArrayList(Stream.of(StringUtils.split(item.getOIds(), ",")).map(Long::parseLong).toList());
                    oIds.removeAll(needDeleteRecordOIds);
                    if (oIds.isEmpty()) {
                        needDeleteRecordIds.add(item.getId());
                    } else {
                        item.setOIds(oIds.stream().map(Object::toString).collect(Collectors.joining(",")));

                        // 添加到更新比对记录
                        needSaveRecordsList.add(item);
                    }
                });
                // 删除数据库中已存在的与 OTA 匹配后需要删除的比对记录
                recordsStatusService.removeByIds(needDeleteRecordIds);
            }

            // 处理确认的 Credit Card 数据
            handleConfirmTaCreditCards(property, TaRecordsStatus.CATEGORY_OTA, confirmTaCreditCardListByReport,
                    recordsStatusList, needSaveRecordsList);
        }

        /**
         * 更新 Credit Card CanChoose
         *
         * @param needSaveRecordsList 保存的比对记录
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void updateCreditCardCanChoose(List<TaRecordsStatus> needSaveRecordsList) {
            if (CollectionUtils.isEmpty(needSaveRecordsList)) {
                return;
            }
            // 非 missTA 状态的数据 canChoose = false
            List<Long> creditCardIds = needSaveRecordsList.stream().filter(item ->
                            !item.getStatus().equals(TaRecordsStatusEnum.MISS_TA.getCode())).map(item ->
                            Stream.of(StringUtils.split(item.getOIds(), ","))
                                    .map(Long::parseLong).toList())
                    .flatMap(Collection::stream).toList();
            if (CollectionUtils.isEmpty(creditCardIds)) {
                return;
            }
            creditCardService.update(new UpdateWrapper<TaCreditCard>().lambda()
                    .in(TaCreditCard::getId, creditCardIds)
                    .set(TaCreditCard::getCanChoose, Boolean.FALSE));
        }
    }