# TaCompareCreditCardServiceImpl.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.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.bo.ta.creditcard.TaCreditCardBO;
    import cloud.wconnect.finance.pojo.bo.ta.creditcard.TaCreditCardListBO;
    import cloud.wconnect.finance.pojo.dto.ta.compare.TaCompareFilterRoomNoConfigDTO;
    import cloud.wconnect.finance.pojo.dto.ta.compare.TaRoomNoRangeDTO;
    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 org.springframework.transaction.annotation.Transactional;

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

    /**
     * @author zc
     * @date 2023/11/18
     */
    @Service
    @DS("finance")
    public class TaCompareCreditCardServiceImpl implements ITaCompareCreditCardService {
        @Resource
        private ITaCompareReportCommonService compareReportCommonService;
        @Resource
        private ITaRecordsStatusService recordsStatusService;
        @Resource
        private ITaCreditCardService creditCardService;
        @Resource
        private ITaPortalService portalService;
        @Resource
        private ITaOtaService otaService;
        @Resource
        private ITaCompareFilterRoomNoConfigService filterRoomNoConfigService;
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private BeanMapper beanMapper;

        /**
         * 处理对比逻辑 for Credit Card
         *
         * @param property     资产
         * @param taCreditCard Credit Card 数据
         * @author zc
         * @date 2023/7/20
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleCreditCardReport(PropertyDTO property, TaCreditCardBO taCreditCard) {
            // 目标日期
            long targetDate = DateTimeUtils.dateStrToEpochSecond(taCreditCard.getDataDate(),
                    BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone());

            // 格式化 Credit Card 报告数据
            List<TaCreditCard> creditCardListByReport = formatCreditCardList(property, targetDate,
                    taCreditCard.getList());

            // 移除需要忽略房号的数据
            filterRoomNo(property, targetDate, creditCardListByReport);

            // 前置处理（移除已存在的数据，保存剩余的数据）
            preprocessingForCreditCard(property, targetDate, creditCardListByReport);
            if (CollectionUtils.isEmpty(creditCardListByReport)) {
                return;
            }

            // 获取 Credit Card 对应的 Portal 数据
            List<TaPortal> portalList = portalService.listByCreditCardList(creditCardListByReport);

            // 获取指定日期范围的比对记录数据
            long startArrivalDate = creditCardListByReport.stream().map(TaCreditCard::getArrivalDate).min(Long::compareTo).get();
            long endArrivalDate = creditCardListByReport.stream().map(TaCreditCard::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, creditCardListByReport,
                    portalList, recordsStatusList, needSaveRecordsList);

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

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

            // Credit Card 比对 OTA 数据，处理 OTA 合约价
            compareReportCommonService.creditCardCompareOta(property, targetDate, creditCardListByReport,
                    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 creditCardList 数据
         * @author zc
         * @date 2023/7/19
         */
        private void filterRoomNo(PropertyDTO property, long targetDate, List<TaCreditCard> creditCardList) {
            if (CollectionUtils.isEmpty(creditCardList)) {
                return;
            }
            // 处理忽略房间号的数据
            TaCompareFilterRoomNoConfigDTO effectiveRoomNo = filterRoomNoConfigService.getCurrent(property.getTenantId(),
                    property.getId(), targetDate);
            if (Objects.isNull(effectiveRoomNo)) {
                return;
            }

            List<Object> roomNoObjectList = JsonUtils.convertStringToList(objectMapper, effectiveRoomNo.getRoomNo(), Object.class);

            List<String> roomNoList = Lists.newArrayList();
            List<TaRoomNoRangeDTO> roomNoRangeList = Lists.newArrayList();
            roomNoObjectList.forEach(roomNoObject -> {
                if (roomNoObject instanceof String) {
                    roomNoList.add(roomNoObject.toString());
                } else {
                    String roomNoRangeStr = JsonUtils.convertObjToString(objectMapper, roomNoObject);
                    TaRoomNoRangeDTO taRoomNoRangeDTO = JsonUtils.convertStringToObject(objectMapper,
                            roomNoRangeStr, TaRoomNoRangeDTO.class);
                    roomNoRangeList.add(taRoomNoRangeDTO);
                }
            });

            List<TaCreditCard> cardsToRemove = Lists.newArrayList();
            for (TaCreditCard item : creditCardList) {
                String roomNo = item.getRoomNo();
                if (roomNoList.contains(roomNo)) {
                    cardsToRemove.add(item);
                    continue;
                }
                for (TaRoomNoRangeDTO range : roomNoRangeList) {
                    if (roomNo.compareTo(range.getStart()) >= 0 && roomNo.compareTo(range.getEnd()) <= 0) {
                        cardsToRemove.add(item);
                        break;
                    }
                }
            }
            creditCardList.removeAll(cardsToRemove);
        }

        /**
         * 格式化 Credit Card 数据
         *
         * @param property               资产
         * @param targetDate             目标日期
         * @param taCreditCardListBoList 需要格式化的数据
         * @return TaCreditCard
         * @author zc
         * @date 2023/7/26
         */
        private List<TaCreditCard> formatCreditCardList(PropertyDTO property, long targetDate,
                                                        List<TaCreditCardListBO> taCreditCardListBoList) {
            // 转换日期字段
            long nowTimestamp = DateTimeUtils.nowTimestamp();
            List<TaCreditCard> creditCardList = beanMapper.mapList(taCreditCardListBoList, TaCreditCard.class,
                    (bo, item) -> {
                        item.setPropertyId(property.getId());
                        item.setArrivalDate(DateTimeUtils.dateStrToEpochSecond(bo.getArrivalDay(),
                                BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone()));
                        item.setDepartureDate(DateTimeUtils.dateStrToEpochSecond(bo.getDepartureDay(),
                                BaseConstant.DATE_FORMAT_YYYY_MM_DD, property.getTimezone()));
                        item.setCardNo(bo.getLastFourDigits());
                        item.setDataDate(targetDate);
                        item.setCreatedTime(nowTimestamp);
                        item.setCanChoose(Boolean.TRUE);
                        item.setInfoSign(DigestUtils.sha256Hex(StringUtils.join(item.getRoomNo(),
                                item.getName(), item.getArrivalDate(), item.getDepartureDate(),
                                item.getCardType(), item.getCardNo(), item.getRoomRevenue(),
                                item.getFbRevenue(), item.getNonRevenue(), item.getTotalCharged())));
                    });
            return creditCardList.stream().distinct().collect(Collectors.toList());
        }

        /**
         * 前置处理
         *
         * @param property               资产
         * @param targetDate             目标日期
         * @param creditCardListByReport 报告数据
         * @author zc
         * @date 2023/11/19
         */
        private void preprocessingForCreditCard(PropertyDTO property, long targetDate, List<TaCreditCard> creditCardListByReport) {
            List<TaCreditCard> creditCardListByDb = creditCardService.listByDataDate(property.getId(), targetDate);
            // 获取 DB 中目标日期的 Credit Card 数据
            List<String> infoSignListByDb = creditCardListByDb.stream().map(TaCreditCard::getInfoSign).toList();
            // 获取相同的 Credit Card By Report 数据签名
            List<String> sameInfoSignList = creditCardListByReport.stream().map(TaCreditCard::getInfoSign)
                    .filter(infoSignListByDb::contains).toList();
            // 移除已存在的数据
            creditCardListByReport.removeIf(item -> sameInfoSignList.contains(item.getInfoSign()));
            if (CollectionUtils.isNotEmpty(creditCardListByReport)) {
                // 保存报告中剩余的数据
                creditCardService.insertOrUpdateBatchByUnique(creditCardListByReport);
                // 查询 id
                Map<String, Long> infoSign2CreditCardId = creditCardService.listByInfoSign(property.getId(),
                                creditCardListByReport.stream().map(TaCreditCard::getInfoSign).toList()).stream()
                        .collect(Collectors.toMap(TaCreditCard::getInfoSign, TaCreditCard::getId));
                creditCardListByReport.forEach(item ->
                        item.setId(infoSign2CreditCardId.get(item.getInfoSign())));
            }
        }
    }