# TaComparePortalServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    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.mapper.ta.TaPortalMapper;
    import cloud.wconnect.finance.pojo.bo.ta.portal.TaPortalBO;
    import cloud.wconnect.finance.pojo.bo.ta.portal.TaPortalListBO;
    import cloud.wconnect.finance.pojo.dto.ta.TaPortalJsonDTO;
    import cloud.wconnect.finance.service.ta.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Service;

    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    /**
     * @author zc
     * @date 2023/11/18
     */
    @Service
    @DS("finance")
    public class TaComparePortalServiceImpl implements ITaComparePortalService {
        @Resource
        private ITaCompareReportCommonService compareReportCommonService;
        @Resource
        private ITaRecordsStatusService recordsStatusService;
        @Resource
        private ITaRecordNotesService recordNotesService;
        @Resource
        private ITaCreditCardService creditCardService;
        @Resource
        private ITaPortalService portalService;
        @Resource
        private TaPortalMapper portalMapper;
        @Resource
        private ITaOtaService otaService;
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private BeanMapper beanMapper;

        /**
         * 处理对比逻辑 for Portal
         *
         * @param property 资产
         * @param taPortal Portal 数据
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public void handlePortalReport(PropertyDTO property, TaPortalBO taPortal) {
            // 目标日期
            long targetDate = DateTimeUtils.dateStrToEpochSecond(taPortal.getDataDate(),
                    BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone());

            // Portal 报告数据
            List<TaPortal> portalListByReport = formatPortalList(property, targetDate, taPortal.getList());

            // 前置处理
            preprocessingForPortal(property, targetDate, portalListByReport);
            if (CollectionUtils.isEmpty(portalListByReport)) {
                return;
            }

            // 获取 Portal 对应的 Credit Card 数据
            List<TaCreditCard> creditCardList = creditCardService.listByPortalList(portalListByReport);

            // 获取指定日期范围的比对记录数据
            long startArrivalDate = portalListByReport.stream().map(TaPortal::getArrivalDate).min(Long::compareTo).get();
            long endArrivalDate = portalListByReport.stream().map(TaPortal::getArrivalDate).max(Long::compareTo).get();
            List<TaRecordsStatus> recordsStatusList = recordsStatusService.listByArrivalDateRange(property.getId(), startArrivalDate, endArrivalDate);
            // 组装 Records 关联的 CreditCard 数据
            recordsStatusService.relateWithCreditCards(recordsStatusList);

            // 需要保存的比对记录数据
            List<TaRecordsStatus> needSaveRecordsList = Lists.newArrayList();

            // Credit Card 比对 Portal 数据
            compareReportCommonService.creditCardComparePortal(property, creditCardList,
                    portalListByReport, recordsStatusList, needSaveRecordsList);

            // 处理 Portal 合约价
            compareReportCommonService.handlePortalContractPrice(property, targetDate,
                    creditCardList, recordsStatusList, needSaveRecordsList);

            // 获取 Credit Card 对应的 OTA 数据
            List<TaOta> otaList = otaService.listByCreditCardList(creditCardList);

            // Credit Card 比对 OTA 数据，处理 OTA 合约价
            compareReportCommonService.creditCardCompareOta(property, targetDate, creditCardList,
                    otaList, recordsStatusList, needSaveRecordsList);

            // 保存比对数据
            if (CollectionUtils.isNotEmpty(needSaveRecordsList)) {
                long nowTimestamp = DateTimeUtils.nowTimestamp();
                needSaveRecordsList.forEach(item -> item.setUpdateTime(nowTimestamp));
                recordsStatusService.saveOrUpdateBatch(needSaveRecordsList);
            }

            // 更新 Credit Card CanChoose
            compareReportCommonService.updateCreditCardCanChoose(needSaveRecordsList);
        }

        /**
         * 前置处理
         *
         * @param property           资产
         * @param targetDate         目标日期
         * @param portalListByReport portal 数据
         * @author zc
         * @date 2023/11/19
         */
        private void preprocessingForPortal(PropertyDTO property, long targetDate, List<TaPortal> portalListByReport) {
            // 获取 DB 中目标日期的 Portal 数据
            List<TaPortal> portalListByDb = portalService.listByDataDate(property.getId(), targetDate);
            List<String> infoSignListByDb = portalListByDb.stream().map(TaPortal::getInfoSign).toList();
            // 获取相同的 Portal By Report 数据签名
            List<String> sameInfoSignList = portalListByReport.stream().map(TaPortal::getInfoSign)
                    .filter(infoSignListByDb::contains).toList();
            // 移除已存在的数据
            portalListByReport.removeIf(taPortal -> sameInfoSignList.contains(taPortal.getInfoSign()));
            if (CollectionUtils.isEmpty(portalListByReport)) {
                return;
            }
            // 保存报告中剩余的数据
            portalService.insertOrUpdateBatchByUnique(portalListByReport);
            Map<String, Long> infoSign2PortalId = portalService.listByInfoSign(property.getId(),
                            portalListByReport.stream().map(TaPortal::getInfoSign).toList()).stream()
                    .collect(Collectors.toMap(TaPortal::getInfoSign, TaPortal::getId));
            portalListByReport.forEach(item ->
                    item.setId(infoSign2PortalId.get(item.getInfoSign())));

            // 获取 Portal By DB 关联的 Records Status 数据
            List<Long> portalIdsByDb = portalListByDb.stream().map(TaPortal::getId).toList();
            List<TaRecordsStatus> taRecordsStatusList = recordsStatusService.listByPortalIds(portalIdsByDb, Collections.emptyList());

            // 移除相同的数据，剩下的是需要删除的 Portal By DB 数据
            portalListByDb.removeIf(portal -> sameInfoSignList.contains(portal.getInfoSign()));
            // 获取需要删除的 Portal Id By DB 集合
            List<Long> needDeletePortalIdsByDb = portalListByDb.stream().map(TaPortal::getId).toList();
            // 删除需要删除的 Portal 数据
            portalService.removeByIds(needDeletePortalIdsByDb);

            // 移除不需要处理的 Records Status 数据，剩下的是需要更新或删除的 Records Status 数据
            taRecordsStatusList.removeIf(record -> !needDeletePortalIdsByDb.contains(record.getPId()));
            if (CollectionUtils.isEmpty(taRecordsStatusList)) {
                return;
            }
            // 处理需要更新或删除的 Records Status 数据
            List<TaRecordsStatus> recordsStatusList = taRecordsStatusList.stream().map(item -> {
                TaRecordsStatus recordsStatus = new TaRecordsStatus();
                recordsStatus.setId(item.getId());
                recordsStatus.setPId(BaseConstant.LONG_ZERO);
                recordsStatus.setOIds(item.getOIds());
                if (Lists.newArrayList(TaRecordsStatusEnum.MATCHED.getCode(),
                        TaRecordsStatusEnum.PRICE_VARIANCE.getCode()).contains(item.getStatus())) {
                    recordsStatus.setStatus(TaRecordsStatusEnum.MISS_TA.getCode());
                }
                return recordsStatus;
            }).toList();
            // 删除未关联 Credit Card 和 Portal 的 Records Status 数据
            Map<Boolean, List<TaRecordsStatus>> needDelete2Records = recordsStatusList.stream()
                    .collect(Collectors.partitioningBy(item -> StringUtils.isBlank(item.getOIds())
                            && item.getPId().equals(BaseConstant.LONG_ZERO)));
            List<Long> needDeleteRecordsStatusIds = needDelete2Records.getOrDefault(Boolean.TRUE, Collections.emptyList()).stream()
                    .map(TaRecordsStatus::getId).toList();
            recordsStatusService.removeByIds(needDeleteRecordsStatusIds);
            // 删除 Records Status Note 数据
            recordNotesService.removeByRecordsIds(needDeleteRecordsStatusIds);
            // 更新剩余的数据
            recordsStatusService.updateBatchById(needDelete2Records.getOrDefault(Boolean.FALSE, Collections.emptyList()));
        }

        /**
         * 格式化 Portal 数据
         *
         * @param property     资产
         * @param targetDate   目标日期
         * @param taPortalList 需要格式化的数据
         * @return TaOta
         * @author zc
         * @date 2023/7/27
         */
        private List<TaPortal> formatPortalList(PropertyDTO property, long targetDate,
                                                List<TaPortalListBO> taPortalList) {
            // 转换日期字段
            long nowTimestamp = DateTimeUtils.nowTimestamp();
            List<TaPortal> portalList = beanMapper.mapList(taPortalList, TaPortal.class, (bo, item) -> {
                item.setPropertyId(property.getId());
                item.setType(TaRecordsStatus.PLATFORM_TYPE_TA);
                item.setArrivalDate(DateTimeUtils.dateStrToEpochSecond(bo.getArrivalDay(),
                        BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone()));
                item.setCardNo(bo.getLastFourDigits());

                String details = JsonUtils.convertObjToString(objectMapper, TaPortalJsonDTO.builder()
                        .airline(bo.getAirline())
                        .voucher(bo.getVoucher())
                        .departDate(DateTimeUtils.dateStrToEpochSecond(bo.getDepartureDay(),
                                BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone()))
                        .customerNumber(bo.getCustomerNumber().toString()).build());
                item.setDetails(details);
                item.setDataDate(targetDate);
                item.setCreatedTime(nowTimestamp);
                item.setInfoSign(DigestUtils.sha256Hex(StringUtils.join(item.getType(),
                        item.getArrivalDate(), item.getName(), item.getRooms(),
                        item.getCardNo(), item.getRate(), item.getTotalCharged(),
                        item.getNights(), item.getDetails())));
            });
            return portalList.stream().distinct().collect(Collectors.toList());
        }
    }