# TaRecordsStatusServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    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.TaPortal;
    import cloud.wconnect.finance.entity.ta.TaRecordsStatus;
    import cloud.wconnect.finance.mapper.ta.TaRecordsStatusMapper;
    import cloud.wconnect.finance.pojo.dto.ta.*;
    import cloud.wconnect.finance.pojo.query.ta.TaCreditCardListQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusExportQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusTotalQuery;
    import cloud.wconnect.finance.pojo.vo.ta.TaExportComparativeDataVO;
    import cloud.wconnect.finance.pojo.vo.ta.TaExportCreditCardDataVO;
    import cloud.wconnect.finance.pojo.vo.ta.TaExportPortalDataVO;
    import cloud.wconnect.finance.service.ta.ITaCreditCardService;
    import cloud.wconnect.finance.service.ta.ITaPortalService;
    import cloud.wconnect.finance.service.ta.ITaRecordNotesService;
    import cloud.wconnect.finance.service.ta.ITaRecordsStatusService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.text.MessageFormat;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author zc
     * @since 2023-07-07
     */
    @Service
    @DS("finance")
    public class TaRecordsStatusServiceImpl extends ServiceImpl<TaRecordsStatusMapper, TaRecordsStatus> implements ITaRecordsStatusService {
        @Resource
        private ITaCreditCardService taCreditCardService;
        @Resource
        private ITaPortalService taPortalService;
        @Resource
        private ITaRecordNotesService taRecordNotesService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private BeanMapper beanMapper;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 Records Status 数据集合
         *
         * @param tenantId             租户 id
         * @param propertyId           资产 id
         * @param taRecordsStatusQuery 查询条件
         * @return TaRecordsStatusDTO 集合
         * @author zc
         * @date 2023/7/10
         */
        @Override
        public ListPagedDTO<TaRecordsStatusDTO> list(int tenantId, int propertyId, TaRecordsStatusQuery taRecordsStatusQuery) {
            // 分页查询数据
            Page<TaRecordsStatus> paged = page(propertyId, taRecordsStatusQuery);
            List<TaRecordsStatus> taRecordsStatusList = paged.getRecords();
            if (CollectionUtils.isEmpty(taRecordsStatusList)) {
                return new ListPagedDTO<>();
            }

            // 获取 CreditCard 数据
            List<TaCreditCard> taCreditCardList = getCreditCardByRecords(taRecordsStatusList);

            // 获取 Portal 数据
            Map<Long, TaPortal> portalId2Portal = getPortalByRecords(taRecordsStatusList).stream()
                    .collect(Collectors.toMap(TaPortal::getId, Function.identity()));

            // 获取 TA Records 备注条数
            List<Long> taRecordsStatusIds = taRecordsStatusList.stream().map(TaRecordsStatus::getId).toList();
            Map<Long, Long> recordsId2NoteCount = taRecordNotesService.getRecordsId2NoteCount(taRecordsStatusIds);

            // 获取用户名称
            Set<Integer> userIds = taRecordsStatusList.stream().map(TaRecordsStatus::getOperatorId)
                    .filter(item -> item > 0).collect(Collectors.toSet());
            Map<Integer, String> userId2Name = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(userIds)) {
                userId2Name = userInternalService.getNameMapByIds(tenantId, userIds);
            }

            List<Integer> statusList = Lists.newArrayList(TaRecordsStatusEnum.MATCHED.getCode(),
                    TaRecordsStatusEnum.MISS_PMS.getCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getCode(),
                    TaRecordsStatusEnum.NOT_IN_PMS.getCode());

            List<TaRecordsStatusDTO> result = Lists.newArrayList();
            for (TaRecordsStatus taRecordsStatus : taRecordsStatusList) {
                // 组装数据
                TaRecordsStatusDTO taRecordsStatusDTO = beanMapper.map(taRecordsStatus, TaRecordsStatusDTO.class);
                taRecordsStatusDTO.setCreateByManual(taRecordsStatus.getCreateId() > BaseConstant.ZERO);
                taRecordsStatusDTO.setNoteCount(recordsId2NoteCount.getOrDefault(taRecordsStatus.getId(), BaseConstant.LONG_ZERO));
                taRecordsStatusDTO.setOperatorName(userId2Name.get(taRecordsStatus.getOperatorId()));
                taRecordsStatusDTO.setCanUndo(StringUtils.isNotBlank(taRecordsStatus.getStatusDetail()));

                // 根据 CreditCard、Portal 组装 Records 展示数据
                packageByCreditCardAndPortal(taRecordsStatus, taCreditCardList, portalId2Portal, statusList, taRecordsStatusDTO);

                result.add(taRecordsStatusDTO);
            }
            return new ListPagedDTO<>(paged.getTotal(), result);
        }

        /**
         * 根据 Credit Card、Portal 组装 Records 展示数据
         *
         * @param taRecordsStatus    比对记录数据
         * @param taCreditCardList   ta Credit Card 数据
         * @param portalId2Portal    ta portal 数据
         * @param statusList         状态集合
         * @param taRecordsStatusDTO 数据封装对象
         * @author zc
         * @date 2023/7/10
         */
        private void packageByCreditCardAndPortal(TaRecordsStatus taRecordsStatus, List<TaCreditCard> taCreditCardList, Map<Long, TaPortal> portalId2Portal,
                                                  List<Integer> statusList, TaRecordsStatusDTO taRecordsStatusDTO) {
            TaCreditCard maxDepartureDateCreditCard = getMaxDepartureDate(taRecordsStatus, taCreditCardList);
            if (statusList.contains(taRecordsStatus.getStatus())) {
                TaPortal taPortal = portalId2Portal.get(taRecordsStatus.getPId());
                if (Objects.nonNull(taPortal)) {
                    taRecordsStatusDTO.setNights(taPortal.getNights());
                    taRecordsStatusDTO.setRoomRate(taPortal.getRate());
                    taRecordsStatusDTO.setRooms(taPortal.getRooms());

                    // 如果状态是 Missing in PMS，那么离店日期需要从 Portal 中取值
                    if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MISS_PMS.getCode())) {
                        if (StringUtils.isNotBlank(taPortal.getDetails())) {
                            TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                                    taPortal.getDetails(), TaPortalJsonDTO.class);
                            taRecordsStatusDTO.setDepartDate(taPortalJsonDTO.getDepartDate());
                        }
                        taRecordsStatusDTO.setLastFourDigits(taPortal.getCardNo());
                    } else {
                        if (Objects.nonNull(maxDepartureDateCreditCard)) {
                            taRecordsStatusDTO.setDepartDate(maxDepartureDateCreditCard.getDepartureDate());
                            taRecordsStatusDTO.setLastFourDigits(maxDepartureDateCreditCard.getCardNo());
                        } else {
                            if (StringUtils.isNotBlank(taPortal.getDetails())) {
                                TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                                        taPortal.getDetails(), TaPortalJsonDTO.class);
                                taRecordsStatusDTO.setDepartDate(taPortalJsonDTO.getDepartDate());
                            }
                            taRecordsStatusDTO.setLastFourDigits(taPortal.getCardNo());
                        }
                    }
                }
            } else {
                // 如果是 2：Missing in TA 5：TBA 6：ignore 8：ILWDP 状态，则应从 PMS Credit Card 中获取
                if (Objects.nonNull(maxDepartureDateCreditCard)) {
                    taRecordsStatusDTO.setDepartDate(maxDepartureDateCreditCard.getDepartureDate());
                    taRecordsStatusDTO.setLastFourDigits(maxDepartureDateCreditCard.getCardNo());

                    // 如果是状态是 5：TBA 需要用到 Credit Card 的价格
                    if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.TBA.getCode())) {
                        taRecordsStatusDTO.setRoomRate(maxDepartureDateCreditCard.getRoomRevenue());
                    }
                }
            }
        }

        /**
         * 获取离开日期最大的 Credit Card 数据
         *
         * @param taRecordsStatus  比对记录数据
         * @param taCreditCardList ta Credit Card 数据
         * @return TaCreditCard
         * @author zc
         * @date 2023/7/20
         */
        @Override
        public TaCreditCard getMaxDepartureDate(TaRecordsStatus taRecordsStatus, List<TaCreditCard> taCreditCardList) {
            TaCreditCard maxDepartureDateCreditCard = null;
            if (StringUtils.isNotBlank(taRecordsStatus.getOIds())) {
                List<Long> creditCardIdsByRecords = Stream.of(StringUtils.split(taRecordsStatus.getOIds(), ","))
                        .map(Long::parseLong).toList();
                maxDepartureDateCreditCard = taCreditCardList.stream()
                        .filter(item -> creditCardIdsByRecords.contains(item.getId()))
                        .max(Comparator.comparing(TaCreditCard::getDepartureDate)).orElse(null);
            }
            return maxDepartureDateCreditCard;
        }

        /**
         * 分页查询数据
         *
         * @param propertyId           资产 id
         * @param taRecordsStatusQuery 查询条件
         * @return TaRecordsStatus 分页对象
         * @author zc
         * @date 2023/7/10
         */
        private Page<TaRecordsStatus> page(int propertyId, TaRecordsStatusQuery taRecordsStatusQuery) {
            LambdaQueryWrapper<TaRecordsStatus> queryWrapper = new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .ge(Objects.nonNull(taRecordsStatusQuery.getFromDate()),
                            TaRecordsStatus::getArrivalDate, taRecordsStatusQuery.getFromDate())
                    .le(Objects.nonNull(taRecordsStatusQuery.getToDate()),
                            TaRecordsStatus::getArrivalDate, taRecordsStatusQuery.getToDate())
                    .in(CollectionUtils.isNotEmpty(taRecordsStatusQuery.getStatus()),
                            TaRecordsStatus::getStatus, taRecordsStatusQuery.getStatus())
                    .like(StringUtils.isNotBlank(taRecordsStatusQuery.getRoomNo()),
                            TaRecordsStatus::getRoomsNo, taRecordsStatusQuery.getRoomNo())
                    .like(StringUtils.isNotBlank(taRecordsStatusQuery.getName()),
                            TaRecordsStatus::getName, taRecordsStatusQuery.getName())
                    .like(StringUtils.isNotBlank(taRecordsStatusQuery.getAirline()),
                            TaRecordsStatus::getAirline, taRecordsStatusQuery.getAirline())
                    .like(StringUtils.isNotBlank(taRecordsStatusQuery.getConfirmation()),
                            TaRecordsStatus::getConfirmation, taRecordsStatusQuery.getConfirmation())
                    .like(StringUtils.isNotBlank(taRecordsStatusQuery.getLastFourDigits()),
                            TaRecordsStatus::getCardNo, taRecordsStatusQuery.getLastFourDigits())
                    .orderByDesc(TaRecordsStatus::getId);
            return page(new Page<>(taRecordsStatusQuery.getPage(), taRecordsStatusQuery.getSize()), queryWrapper);
        }

        /**
         * 获取数据各状态统计
         *
         * @param propertyId 资产 id
         * @param totalQuery 查询条件
         * @return TaRecordsStatusTotalDTO
         * @author zc
         * @date 2023/7/10
         */
        @Override
        public TaRecordsStatusTotalDTO getTotal(int propertyId, TaRecordsStatusTotalQuery totalQuery) {
            List<TaRecordsStatus> recordsStatusList = list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .ge(Objects.nonNull(totalQuery.getFromDate()),
                            TaRecordsStatus::getArrivalDate, totalQuery.getFromDate())
                    .le(Objects.nonNull(totalQuery.getToDate()),
                            TaRecordsStatus::getArrivalDate, totalQuery.getToDate())
                    .select(TaRecordsStatus::getId, TaRecordsStatus::getStatus));

            Map<Integer, Long> status2Total = recordsStatusList.stream()
                    .collect(Collectors.groupingBy(TaRecordsStatus::getStatus, Collectors.counting()));

            return TaRecordsStatusTotalDTO.builder()
                    .matchedTotal(status2Total.getOrDefault(TaRecordsStatusEnum.MATCHED.getCode(), BaseConstant.LONG_ZERO))
                    .missingTaTotal(status2Total.getOrDefault(TaRecordsStatusEnum.MISS_TA.getCode(), BaseConstant.LONG_ZERO))
                    .missingPmsTotal(status2Total.getOrDefault(TaRecordsStatusEnum.MISS_PMS.getCode(), BaseConstant.LONG_ZERO))
                    .priceVarianceTotal(status2Total.getOrDefault(TaRecordsStatusEnum.PRICE_VARIANCE.getCode(), BaseConstant.LONG_ZERO))
                    .ilwdpTotal(status2Total.getOrDefault(TaRecordsStatusEnum.ILWDP.getCode(), BaseConstant.LONG_ZERO))
                    .tbaTotal(status2Total.getOrDefault(TaRecordsStatusEnum.TBA.getCode(), BaseConstant.LONG_ZERO))
                    .ignoreTotal(status2Total.getOrDefault(TaRecordsStatusEnum.IGNORE.getCode(), BaseConstant.LONG_ZERO))
                    .notInPmsTotal(status2Total.getOrDefault(TaRecordsStatusEnum.NOT_IN_PMS.getCode(), BaseConstant.LONG_ZERO))
                    .build();
        }

        /**
         * 校验 Records Status 数据是否存在
         *
         * @param id 数据 id
         * @return TaRecordsStatus
         * @author zc
         * @date 2023/7/11
         */
        @Override
        public TaRecordsStatus checkExist(long id) {
            TaRecordsStatus taRecordsStatus = getById(id);
            if (Objects.isNull(taRecordsStatus)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The taRecordsStatus is not found; id: " + id);
            }
            return taRecordsStatus;
        }

        /**
         * 校验 Records Status 数据归属
         *
         * @param propertyId      资产 id
         * @param taRecordsStatus Records Status 数据
         * @author zc
         * @date 2023/7/11
         */
        @Override
        public void checkBelong(int propertyId, TaRecordsStatus taRecordsStatus) {
            if (!taRecordsStatus.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The taRecordsStatus does not belong to this property; propertyId:{0}; taRecordsStatusId:{1}",
                                propertyId, taRecordsStatus.getId()));
            }
        }

        /**
         * 获取 Records Status 数据详情
         *
         * @param tenantId        租户 id
         * @param taRecordsStatus Records Status 数据
         * @return TaRecordsStatusDetailDTO
         * @author zc
         * @date 2023/7/11
         */
        @Override
        public TaRecordsStatusDetailDTO getDetail(int tenantId, TaRecordsStatus taRecordsStatus) {
            // 获取 Credit Card 数据
            List<TaCreditCardDetailDTO> creditCardDetailList = Lists.newArrayList();
            if (StringUtils.isNotBlank(taRecordsStatus.getOIds())) {
                List<Long> creditCardIds = Stream.of(StringUtils.split(taRecordsStatus.getOIds(), ","))
                        .map(Long::parseLong).toList();
                List<TaCreditCard> taCreditCardList = taCreditCardService.listByIds(creditCardIds);
                creditCardDetailList.addAll(beanMapper.mapList(taCreditCardList, TaCreditCardDetailDTO.class, (item, itemDTO) -> {
                    itemDTO.setLastFourDigits(item.getCardNo());
                    itemDTO.setRoomRate(item.getRoomRevenue());
                }));
            }

            // 获取 Portal 数据
            TaPortalDetailDTO portalDetail = null;
            if (taRecordsStatus.getPId() > BaseConstant.LONG_ZERO) {
                TaPortal taPortal = taPortalService.getById(taRecordsStatus.getPId());
                TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                        taPortal.getDetails(), TaPortalJsonDTO.class);
                portalDetail = beanMapper.map(taPortal, TaPortalDetailDTO.class, item -> {
                    item.setAirline(taPortalJsonDTO.getAirline());
                    item.setDepartureDate(taPortalJsonDTO.getDepartDate());
                    item.setRoomRate(taPortal.getRate());
                    item.setLastFourDigits(taPortal.getCardNo());
                });
            }

            // 获取操作人名称
            String operatorName = (taRecordsStatus.getOperatorId() == BaseConstant.ZERO) ?
                    null : userInternalService.getOne(tenantId, taRecordsStatus.getOperatorId()).getName();

            return TaRecordsStatusDetailDTO.builder()
                    .creditCardList(creditCardDetailList)
                    .portal(portalDetail)
                    .status(taRecordsStatus.getStatus())
                    .updateTime(taRecordsStatus.getUpdateTime())
                    .confirmation(taRecordsStatus.getConfirmation())
                    .airline(taRecordsStatus.getAirline())
                    .canUndo(StringUtils.isNotEmpty(taRecordsStatus.getStatusDetail()))
                    .operatorName(operatorName)
                    .build();
        }

        /**
         * 更新数据状态
         *
         * @param originRecordsStatus 原数据
         * @param updateStatusDTO     更新内容
         * @param userId              用户 id
         * @author zc
         * @date 2023/7/11
         */
        @Override
        public void updateStatus(TaRecordsStatus originRecordsStatus, TaUpdateStatusDTO updateStatusDTO, int userId) {
            updateById(TaRecordsStatus.builder()
                    .id(originRecordsStatus.getId())
                    .status(updateStatusDTO.getStatus())
                    .operatorId(userId)
                    .canChoose(Boolean.FALSE)
                    .statusDetail(JsonUtils.convertObjToString(objectMapper,
                            TaStatusDetailJsonDTO.builder()
                                    .oldStatus(originRecordsStatus.getStatus())
                                    .build()))
                    .updateTime(DateTimeUtils.nowTimestamp()).build());
        }

        /**
         * 更新数据状态 for TBA
         *
         * @param originRecordsStatus 原数据
         * @param updateStatusDTO     更新内容
         * @param userId              用户 id
         * @author zc
         * @date 2023/7/11
         */
        @Override
        public void updateStatusTba(TaRecordsStatus originRecordsStatus, TaUpdateStatusDTO updateStatusDTO, int userId) {
            updateById(TaRecordsStatus.builder()
                    .id(originRecordsStatus.getId())
                    .status(updateStatusDTO.getStatus())
                    .airline(updateStatusDTO.getAirline())
                    .operatorId(userId)
                    .canChoose(Boolean.FALSE)
                    .statusDetail(JsonUtils.convertObjToString(objectMapper,
                            TaStatusDetailJsonDTO.builder()
                                    .oldStatus(originRecordsStatus.getStatus())
                                    .build()))
                    .updateTime(DateTimeUtils.nowTimestamp()).build());
        }

        /**
         * 更新数据状态 missPms to Matched
         *
         * @param originRecordsStatus 原数据
         * @param updateStatusDTO     更新内容
         * @param userId              用户 id
         * @author zc
         * @date 2023/7/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void updateStatusMissPmsToMatched(TaRecordsStatus originRecordsStatus, TaUpdateStatusDTO updateStatusDTO, int userId) {
            TaRecordsStatus taRecordsStatus = TaRecordsStatus.builder()
                    .id(originRecordsStatus.getId())
                    .status(updateStatusDTO.getStatus())
                    .operatorId(userId)
                    .statusDetail(JsonUtils.convertObjToString(objectMapper,
                            TaStatusDetailJsonDTO.builder()
                                    .oldStatus(originRecordsStatus.getStatus())
                                    .build()))
                    .updateTime(DateTimeUtils.nowTimestamp()).build();

            String creditCardIdsStr = updateStatusDTO.getCreditCardIds();
            if (StringUtils.isBlank(creditCardIdsStr)) {
                // 如果 creditCardIds 为空，则表示用户是输入 confirmation 来 match 的
                taRecordsStatus.setConfirmation(updateStatusDTO.getConfirmation());
            } else {
                // 如果 creditCardIds 不为空，则表示用户是选择 Credit Card 来 match 的
                taRecordsStatus.setOIds(creditCardIdsStr);

                List<String> creditCardIds = Stream.of(StringUtils.split(creditCardIdsStr, ","))
                        .toList();
                // 将传入的 creditCardIds 在 TaRecordsStatus 中对应的记录删除
                remove(new QueryWrapper<TaRecordsStatus>().lambda()
                        .in(TaRecordsStatus::getOIds, creditCardIds));

                String roomsNo = taCreditCardService.listByIds(creditCardIds).stream().map(TaCreditCard::getRoomNo)
                        .filter(StringUtils::isNotBlank).distinct().collect(Collectors.joining(","));
                taRecordsStatus.setRoomsNo(roomsNo);
            }
            updateById(taRecordsStatus);
        }

        /**
         * 更新数据状态 priceVariance to Matched
         *
         * @param originRecordsStatus 原数据
         * @param updateStatusDTO     更新内容
         * @param userId              用户 id
         * @author zc
         * @date 2023/7/11
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void updateStatusPriceVarianceToMatched(TaRecordsStatus originRecordsStatus, TaUpdateStatusDTO updateStatusDTO, int userId) {
            TaRecordsStatus taRecordsStatus = TaRecordsStatus.builder()
                    .id(originRecordsStatus.getId())
                    .status(updateStatusDTO.getStatus())
                    .operatorId(userId)
                    .updateTime(DateTimeUtils.nowTimestamp()).build();

            List<TaRoomRateDTO> rates = updateStatusDTO.getRates();
            // 原数据状态为 priceVariance 且 rates 不为空，则表示：修改房价 match
            if (CollectionUtils.isNotEmpty(rates)) {
                // 查询 PMS Credit Card 房价信息，并记录变更之前的房价信息
                List<Long> creditCardIds = Stream.of(StringUtils.split(originRecordsStatus.getOIds(), ","))
                        .map(Long::parseLong).toList();
                List<TaRoomRateDTO> taRoomRateList = taCreditCardService.listByIds(creditCardIds).stream().map(item ->
                                TaRoomRateDTO.builder()
                                        .id(item.getId())
                                        .rate(item.getRoomRevenue()).build())
                        .toList();

                // 记录变更之前的房价信息
                taRecordsStatus.setStatusDetail(JsonUtils.convertObjToString(objectMapper,
                        TaStatusDetailJsonForPriceVarianceDTO.builder()
                                .oldStatus(originRecordsStatus.getStatus())
                                .rates(taRoomRateList).build()));

                // 修改 PMS Credit Card 房价
                List<TaCreditCard> updateTaCreditCardList = rates.stream().map(item ->
                                TaCreditCard.builder()
                                        .id(item.getId())
                                        .roomRevenue(item.getRate()).build())
                        .toList();
                taCreditCardService.updateBatchById(updateTaCreditCardList);
            } else {
                taRecordsStatus.setStatusDetail(JsonUtils.convertObjToString(objectMapper,
                        TaStatusDetailJsonDTO.builder()
                                .oldStatus(originRecordsStatus.getStatus()).build()));
            }

            updateById(taRecordsStatus);
        }

        /**
         * 回滚数据状态
         *
         * @param taRecordsStatus 原数据
         * @author zc
         * @date 2023/7/12
         */
        @Override
        public void undo(TaRecordsStatus taRecordsStatus) {
            TaStatusDetailJsonDTO statusDetail = JsonUtils.convertStringToObject(objectMapper,
                    taRecordsStatus.getStatusDetail(), TaStatusDetailJsonDTO.class);

            updateById(TaRecordsStatus.builder()
                    .id(taRecordsStatus.getId())
                    .statusDetail(StringUtils.EMPTY)
                    .operatorId(BaseConstant.ZERO)
                    .updateTime(DateTimeUtils.nowTimestamp())
                    .status(statusDetail.getOldStatus())
                    .canChoose(Boolean.TRUE)
                    .build());
        }

        /**
         * 回滚数据状态 for TBA
         *
         * @param taRecordsStatus 原数据
         * @author zc
         * @date 2023/7/12
         */
        @Override
        public void undoTba(TaRecordsStatus taRecordsStatus) {
            TaStatusDetailJsonDTO statusDetail = JsonUtils.convertStringToObject(objectMapper,
                    taRecordsStatus.getStatusDetail(), TaStatusDetailJsonDTO.class);

            updateById(TaRecordsStatus.builder()
                    .id(taRecordsStatus.getId())
                    .statusDetail(StringUtils.EMPTY)
                    .airline(StringUtils.EMPTY)
                    .operatorId(BaseConstant.ZERO)
                    .updateTime(DateTimeUtils.nowTimestamp())
                    .status(statusDetail.getOldStatus())
                    .canChoose(Boolean.TRUE)
                    .build());
        }

        /**
         * 回滚数据状态 for Not in PMS
         *
         * @param taRecordsStatus 原数据
         * @author zc
         * @date 2023/7/12
         */
        @Override
        public void undoNotInPms(TaRecordsStatus taRecordsStatus) {
            TaStatusDetailJsonDTO statusDetail = JsonUtils.convertStringToObject(objectMapper,
                    taRecordsStatus.getStatusDetail(), TaStatusDetailJsonDTO.class);

            updateById(TaRecordsStatus.builder()
                    .id(taRecordsStatus.getId())
                    .statusDetail(StringUtils.EMPTY)
                    .operatorId(BaseConstant.ZERO)
                    .updateTime(DateTimeUtils.nowTimestamp())
                    .status(statusDetail.getOldStatus())
                    .build());
        }

        /**
         * 回滚数据状态 for Matched
         *
         * @param taRecordsStatus 原数据
         * @author zc
         * @date 2023/7/12
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void undoMatched(TaRecordsStatus taRecordsStatus) {
            TaStatusDetailJsonForPriceVarianceDTO statusDetail = JsonUtils.convertStringToObject(objectMapper,
                    taRecordsStatus.getStatusDetail(), TaStatusDetailJsonForPriceVarianceDTO.class);

            TaRecordsStatus recordsStatus = TaRecordsStatus.builder()
                    .id(taRecordsStatus.getId())
                    .statusDetail(StringUtils.EMPTY)
                    .operatorId(BaseConstant.ZERO)
                    .updateTime(DateTimeUtils.nowTimestamp())
                    .status(statusDetail.getOldStatus())
                    .build();

            if (statusDetail.getOldStatus().equals(TaRecordsStatusEnum.PRICE_VARIANCE.getCode())) {
                // 检查是否修改过房价，如果修改过房价，则回滚房价信息
                if (CollectionUtils.isNotEmpty(statusDetail.getRates())) {
                    taCreditCardService.updateBatchById(beanMapper.mapList(statusDetail.getRates(), TaCreditCard.class,
                            (itemDTO, item) -> item.setRoomRevenue(itemDTO.getRate())));
                }
            } else {
                /*
                 * 判断用户之前是选择 PMS Credit Card 还是输入 confirmation 来操作 match ：
                 *     1、如果是输入 confirmation 操作 match，则清空 confirmation 信息
                 *     2、如果是选择 PMS Credit Card 操作 match，则清空当前 records 中的 Credit Card id、房间号，并生成指定 Credit Card 相关的 miss ta records 数据
                 */
                if (StringUtils.isBlank(taRecordsStatus.getOIds())) {
                    recordsStatus.setConfirmation(StringUtils.EMPTY);
                } else {
                    // 清空当前 records 中的 Credit Card id、房间号
                    recordsStatus.setOIds(StringUtils.EMPTY);
                    recordsStatus.setRoomsNo(StringUtils.EMPTY);

                    // 生成指定 Credit Card 相关的 miss ta records 数据
                    List<Long> creditCardIds = Stream.of(StringUtils.split(taRecordsStatus.getOIds(), ","))
                            .map(Long::parseLong).toList();
                    List<TaRecordsStatus> recordsStatusList = taCreditCardService.listByIds(creditCardIds).stream()
                            .map(item -> TaRecordsStatus.builder()
                                    .propertyId(item.getPropertyId())
                                    .pId(BaseConstant.LONG_ZERO)
                                    .oIds(item.getId().toString())
                                    .status(TaRecordsStatusEnum.MISS_TA.getCode())
                                    .statusDetail(StringUtils.EMPTY)
                                    .category(BaseConstant.ZERO)
                                    .createId(BaseConstant.ZERO)
                                    .operatorId(BaseConstant.ZERO)
                                    .updateTime(DateTimeUtils.nowTimestamp())
                                    .arrivalDate(item.getArrivalDate())
                                    .name(item.getName())
                                    .roomsNo(item.getRoomNo())
                                    .airline(StringUtils.EMPTY)
                                    .type(TaRecordsStatus.PLATFORM_TYPE_TA)
                                    .canChoose(Boolean.TRUE)
                                    .cardNo(item.getCardNo())
                                    .build()).toList();
                    saveBatch(recordsStatusList);
                }
            }
            updateById(recordsStatus);
        }

        /**
         * 导出比对数据
         *
         * @param propertyDTO 资产
         * @param exportQuery 查询条件
         * @return Context
         * @author zc
         * @date 2023/7/13
         */
        @Override
        public HashMap<String, Object> exportComparativeData(PropertyDTO propertyDTO, TaRecordsStatusExportQuery exportQuery) {
            List<TaRecordsStatus> taRecordsStatusList = listForExport(propertyDTO.getId(), exportQuery);

            // 获取 Credit Card 数据
            Map<Long, TaCreditCard> id2CreditCard = getCreditCardByRecords(taRecordsStatusList).stream()
                    .collect(Collectors.toMap(TaCreditCard::getId, Function.identity()));

            // 获取 Portal 数据
            Map<Long, TaPortal> portalId2Portal = getPortalByRecords(taRecordsStatusList).stream()
                    .collect(Collectors.toMap(TaPortal::getId, Function.identity()));

            // 获取国际化配置
            String longDateFormat = propertyDTO.getInternationalization().getDateTime().getLongDate();
            String timezone = propertyDTO.getTimezone();
            Locale locale = LocaleContextHolder.getLocale();

            InternationalizationNumberDTO numberDTO = propertyDTO.getInternationalization().getNumber();
            String currency = numberDTO.getCurrency();
            String calculationRule = numberDTO.getCalculationRule();

            List<TaExportComparativeDataVO> comparativeDataList = Lists.newArrayList();
            taRecordsStatusList.forEach(recordsStatus -> {
                TaExportComparativeDataVO comparativeDataVO = new TaExportComparativeDataVO();
                comparativeDataVO.setConfirmation(recordsStatus.getConfirmation());

                TaPortal taPortal = portalId2Portal.get(recordsStatus.getPId());
                TaExportPortalDataVO portalData = new TaExportPortalDataVO();
                if (Objects.nonNull(taPortal)) {
                    portalData.setName(taPortal.getName());
                    portalData.setRooms(InternationalizationUtils.formatViewValue(taPortal.getRooms(),
                            0, calculationRule, StringUtils.EMPTY));
                    portalData.setLastFourDigits(taPortal.getCardNo());
                    portalData.setRoomRate(InternationalizationUtils.formatAmountViewValue(taPortal.getRate(),
                            calculationRule, currency));
                    portalData.setTotalCharged(InternationalizationUtils.formatAmountViewValue(taPortal.getTotalCharged(),
                            calculationRule, currency));
                    portalData.setNights(InternationalizationUtils.formatViewValue(taPortal.getNights(),
                            0, calculationRule, StringUtils.EMPTY));
                    portalData.setArrivalDate(InternationalizationUtils.formatDateTime(taPortal.getArrivalDate(),
                            longDateFormat, StringUtils.EMPTY, timezone, locale));

                    TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                            taPortal.getDetails(), TaPortalJsonDTO.class);
                    if (Objects.nonNull(taPortalJsonDTO)) {
                        portalData.setDepartureDate(InternationalizationUtils.formatDateTime(taPortalJsonDTO.getDepartDate(),
                                longDateFormat, StringUtils.EMPTY, timezone, locale));
                        portalData.setAirline(taPortalJsonDTO.getAirline());
                        portalData.setVoucher(taPortalJsonDTO.getVoucher());
                    }
                } else {
                    portalData.setLastFourDigits(StringUtils.EMPTY);
                    portalData.setVoucher(i18nMessageUtils.message(TaRecordsStatusEnum.getI18nCode(recordsStatus.getStatus()),
                            locale));
                }

                List<TaExportCreditCardDataVO> creditCardDataList = Lists.newArrayList();
                List<Long> creditCardIds = Stream.of(StringUtils.split(recordsStatus.getOIds(), ","))
                        .map(Long::parseLong).toList();
                if (CollectionUtils.isEmpty(creditCardIds)) {
                    creditCardDataList.add(new TaExportCreditCardDataVO());
                } else {
                    creditCardIds.forEach(creditCardId -> {
                                TaCreditCard taCreditCard = id2CreditCard.get(creditCardId);
                                if (Objects.nonNull(taCreditCard)) {
                                    creditCardDataList.add(TaExportCreditCardDataVO.builder()
                                            .roomNo(taCreditCard.getRoomNo())
                                            .arrivalDate(taCreditCard.getRoomNo())
                                            .name(taCreditCard.getName())
                                            .lastFourDigits(taCreditCard.getCardNo())
                                            .roomRate(InternationalizationUtils.formatAmountViewValue(taCreditCard.getRoomRevenue(),
                                                    calculationRule, currency))
                                            .arrivalDate(InternationalizationUtils.formatDateTime(taCreditCard.getArrivalDate(),
                                                    longDateFormat, StringUtils.EMPTY, timezone, locale))
                                            .departureDate(InternationalizationUtils.formatDateTime(taCreditCard.getDepartureDate(),
                                                    longDateFormat, StringUtils.EMPTY, timezone, locale))
                                            .build());
                                }
                            }
                    );
                }

                comparativeDataVO.setCreditCardList(creditCardDataList);
                comparativeDataVO.setPortal(portalData);
                comparativeDataList.add(comparativeDataVO);
            });

            HashMap<String, Object> data = new HashMap<>();
            data.put("comparativeData", comparativeDataList);
            return data;
        }

        /**
         * 导出 Credit Card 数据
         *
         * @param propertyDTO 资产
         * @param exportQuery 查询条件
         * @return Context
         * @author zc
         * @date 2023/7/14
         */
        @Override
        public HashMap<String, Object> exportCreditCardData(PropertyDTO propertyDTO, TaRecordsStatusExportQuery exportQuery) {
            List<TaRecordsStatus> taRecordsStatusList = listForExport(propertyDTO.getId(), exportQuery);

            // 获取国际化配置
            String longDateFormat = propertyDTO.getInternationalization().getDateTime().getLongDate();
            String timezone = propertyDTO.getTimezone();
            Locale locale = LocaleContextHolder.getLocale();

            InternationalizationNumberDTO numberDTO = propertyDTO.getInternationalization().getNumber();
            String currency = numberDTO.getCurrency();
            String calculationRule = numberDTO.getCalculationRule();

            // 获取 Credit Card 数据
            List<TaExportCreditCardDataVO> creditCardDataList = getCreditCardByRecords(taRecordsStatusList).stream()
                    .map(taCreditCard -> TaExportCreditCardDataVO.builder()
                            .roomNo(taCreditCard.getRoomNo())
                            .arrivalDate(taCreditCard.getRoomNo())
                            .name(taCreditCard.getName())
                            .lastFourDigits(taCreditCard.getCardNo())
                            .roomRate(InternationalizationUtils.formatAmountViewValue(taCreditCard.getRoomRevenue(),
                                    calculationRule, currency))
                            .arrivalDate(InternationalizationUtils.formatDateTime(taCreditCard.getArrivalDate(),
                                    longDateFormat, StringUtils.EMPTY, timezone, locale))
                            .departureDate(InternationalizationUtils.formatDateTime(taCreditCard.getDepartureDate(),
                                    longDateFormat, StringUtils.EMPTY, timezone, locale)).build()
                    ).toList();

            HashMap<String, Object> data = new HashMap<>();
            data.put("creditCardData", creditCardDataList);
            return data;
        }

        /**
         * 导出 Portal 数据
         *
         * @param propertyDTO 资产
         * @param exportQuery 查询条件
         * @return Context
         * @author zc
         * @date 2023/7/14
         */
        @Override
        public HashMap<String, Object> exportPortalData(PropertyDTO propertyDTO, TaRecordsStatusExportQuery exportQuery) {
            List<TaRecordsStatus> taRecordsStatusList = listForExport(propertyDTO.getId(), exportQuery);

            // 获取国际化配置
            String longDateFormat = propertyDTO.getInternationalization().getDateTime().getLongDate();
            String timezone = propertyDTO.getTimezone();
            Locale locale = LocaleContextHolder.getLocale();

            InternationalizationNumberDTO numberDTO = propertyDTO.getInternationalization().getNumber();
            String currency = numberDTO.getCurrency();
            String calculationRule = numberDTO.getCalculationRule();

            // 获取 Portal 数据
            List<TaExportPortalDataVO> portalDataList = getPortalByRecords(taRecordsStatusList).stream()
                    .map(taPortal -> {
                        TaExportPortalDataVO portalData = new TaExportPortalDataVO();
                        portalData.setName(taPortal.getName());
                        portalData.setRooms(InternationalizationUtils.formatViewValue(taPortal.getRooms(),
                                0, calculationRule, StringUtils.EMPTY));
                        portalData.setLastFourDigits(taPortal.getCardNo());
                        portalData.setRoomRate(InternationalizationUtils.formatAmountViewValue(taPortal.getRate(),
                                calculationRule, currency));
                        portalData.setTotalCharged(InternationalizationUtils.formatAmountViewValue(taPortal.getTotalCharged(),
                                calculationRule, currency));
                        portalData.setNights(InternationalizationUtils.formatViewValue(taPortal.getNights(),
                                0, calculationRule, StringUtils.EMPTY));
                        portalData.setArrivalDate(InternationalizationUtils.formatDateTime(taPortal.getArrivalDate(),
                                longDateFormat, StringUtils.EMPTY, timezone, locale));

                        TaPortalJsonDTO taPortalJsonDTO = JsonUtils.convertStringToObject(objectMapper,
                                taPortal.getDetails(), TaPortalJsonDTO.class);
                        if (Objects.nonNull(taPortalJsonDTO)) {
                            portalData.setDepartureDate(InternationalizationUtils.formatDateTime(taPortalJsonDTO.getDepartDate(),
                                    longDateFormat, StringUtils.EMPTY, timezone, locale));
                            portalData.setAirline(taPortalJsonDTO.getAirline());
                            portalData.setVoucher(taPortalJsonDTO.getVoucher());
                        }
                        return portalData;
                    }).toList();

            HashMap<String, Object> data = new HashMap<>();
            data.put("portalData", portalDataList);
            return data;
        }

        /**
         * 获取 TaRecordsStatus 数据集合
         *
         * @param propertyId  资产 id
         * @param exportQuery 查询条件
         * @return TaRecordsStatus 集合
         * @author zc
         * @date 2023/7/13
         */
        private List<TaRecordsStatus> listForExport(int propertyId, TaRecordsStatusExportQuery exportQuery) {
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .between(TaRecordsStatus::getArrivalDate, exportQuery.getFromDate(), exportQuery.getToDate())
                    .in(TaRecordsStatus::getStatus, exportQuery.getStatus())
                    .like(StringUtils.isNotBlank(exportQuery.getAirline()),
                            TaRecordsStatus::getAirline, exportQuery.getAirline())
                    .orderByDesc(TaRecordsStatus::getId));
        }

        /**
         * 获取 TaRecordsStatus 关联的 TaCreditCard 数据集合
         *
         * @param taRecordsStatusList TaRecordsStatus
         * @return TaCreditCard 集合
         * @author zc
         * @date 2023/7/13
         */
        @Override
        public List<TaCreditCard> getCreditCardByRecords(List<TaRecordsStatus> taRecordsStatusList) {
            List<Long> creditCardIds = taRecordsStatusList.stream().filter(item -> StringUtils.isNotBlank(item.getOIds()))
                    .flatMap(item -> Stream.of(StringUtils.split(item.getOIds(), ",")).map(Long::parseLong))
                    .toList();
            List<TaCreditCard> taCreditCardList = Lists.newArrayList();
            if (CollectionUtils.isNotEmpty(creditCardIds)) {
                taCreditCardList = taCreditCardService.listByIds(creditCardIds);
            }
            return taCreditCardList;
        }

        /**
         * 获取 TaRecordsStatus 关联的 Portal 数据集合
         *
         * @param taRecordsStatusList TaRecordsStatus
         * @return TaPortal 集合
         * @author zc
         * @date 2023/7/13
         */
        @Override
        public List<TaPortal> getPortalByRecords(List<TaRecordsStatus> taRecordsStatusList) {
            List<Long> portalIds = taRecordsStatusList.stream().map(TaRecordsStatus::getPId)
                    .filter(item -> item > 0L).toList();
            List<TaPortal> taPortals = Lists.newArrayList();
            if (CollectionUtils.isNotEmpty(portalIds)) {
                taPortals = taPortalService.listByIds(portalIds);
            }
            return taPortals;
        }

        /**
         * 获取 Credit Card 列表
         *
         * @param propertyId            资产 id
         * @param taCreditCardListQuery 查询条件
         * @return TaCreditCardListDTO 集合
         * @author zc
         * @date 2023/7/15
         */
        @Override
        public ListPagedDTO<TaCreditCardListDTO> listCreditCard(int propertyId, TaCreditCardListQuery taCreditCardListQuery) {
            LambdaQueryWrapper<TaCreditCard> queryWrapper = new QueryWrapper<TaCreditCard>().lambda()
                    .eq(TaCreditCard::getPropertyId, propertyId)
                    .ge(Objects.nonNull(taCreditCardListQuery.getFromDate()),
                            TaCreditCard::getArrivalDate, taCreditCardListQuery.getFromDate())
                    .le(Objects.nonNull(taCreditCardListQuery.getToDate()),
                            TaCreditCard::getArrivalDate, taCreditCardListQuery.getToDate())
                    .like(StringUtils.isNotBlank(taCreditCardListQuery.getName()),
                            TaCreditCard::getName, taCreditCardListQuery.getName())
                    .like(StringUtils.isNotBlank(taCreditCardListQuery.getLastFourDigits()),
                            TaCreditCard::getCardNo, taCreditCardListQuery.getLastFourDigits())
                    .orderByDesc(TaCreditCard::getCanChoose);

            Page<TaCreditCard> page = taCreditCardService.page(new Page<>(taCreditCardListQuery.getPage(),
                    taCreditCardListQuery.getSize()), queryWrapper);

            List<TaCreditCard> taCreditCardList = page.getRecords();
            if (CollectionUtils.isEmpty(taCreditCardList)) {
                return new ListPagedDTO<>();
            }

            List<TaCreditCardListDTO> resultList = beanMapper.mapList(taCreditCardList,
                    TaCreditCardListDTO.class, (item, dto) -> {
                        dto.setRoomRate(item.getRoomRevenue());
                        dto.setLastFourDigits(item.getCardNo());
                    });
            return new ListPagedDTO<>(page.getTotal(), resultList);
        }

        /**
         * 获取关联的 Credit Card 数据
         *
         * @param taRecordsStatusList TaRecordsStatus
         * @author zc
         * @date 2023/7/24
         */
        @Override
        public void relateWithCreditCards(List<TaRecordsStatus> taRecordsStatusList) {
            List<TaCreditCard> taCreditCardList = getCreditCardByRecords(taRecordsStatusList);

            taRecordsStatusList.forEach(item -> {
                List<Long> oIds = Stream.of(StringUtils.split(item.getOIds(), ","))
                        .map(Long::parseLong).toList();
                item.setCreditCards(taCreditCardList.stream().filter(creditCard ->
                        oIds.contains(creditCard.getId())).toList());
            });
        }

        /**
         * 根据 Credit Card ids 获取比对数据
         *
         * @param creditCardIds Credit Card ids
         * @param status        状态集合
         * @return TaRecordsStatus 集合
         * @author zc
         * @date 2023/11/18
         */
        @Override
        public List<TaRecordsStatus> listByCreditCardIds(List<Long> creditCardIds, List<Integer> status) {
            if (CollectionUtils.isEmpty(creditCardIds)) {
                return Collections.emptyList();
            }
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .in(CollectionUtils.isNotEmpty(status), TaRecordsStatus::getStatus, status)
                    .apply(String.format("CONCAT( ',', REPLACE ( REPLACE ( REPLACE ( o_ids, '[', '' ), ']', '' ), ' ', '' ), ',' ) REGEXP ',(%s),'",
                            StringUtils.join(creditCardIds, "|"))));
        }

        /**
         * 根据 Portal ids 获取比对数据
         *
         * @param portalIds Portal ids
         * @param status    状态集合
         * @return TaRecordsStatus 集合
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public List<TaRecordsStatus> listByPortalIds(List<Long> portalIds, List<Integer> status) {
            if (CollectionUtils.isEmpty(portalIds)) {
                return Collections.emptyList();
            }
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .in(TaRecordsStatus::getPId, portalIds));
        }

        /**
         * 获取指定日期范围的比对记录数据
         *
         * @param propertyId       资产 id
         * @param startArrivalDate 入住开始时间
         * @param endArrivalDate   入住结束时间
         * @return TaRecordsStatus 集合
         * @author zc
         * @date 2023/11/19
         */
        @Override
        public List<TaRecordsStatus> listByArrivalDateRange(int propertyId, long startArrivalDate, long endArrivalDate) {
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .between(TaRecordsStatus::getArrivalDate, startArrivalDate, endArrivalDate));
        }

        /**
         * 获取各状态数据数量
         *
         * @param propertyId     资产 id
         * @param exceptionTypes 异常类型
         * @return 各状态数据数量
         * @author zc
         * @date 2024/11/1
         */
        @Override
        public Map<Integer, Long> getTotalByStatus(int propertyId, List<Integer> exceptionTypes) {
            if (CollectionUtils.isEmpty(exceptionTypes)) {
                return Collections.emptyMap();
            }
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .in(TaRecordsStatus::getStatus, exceptionTypes)
                    .select(TaRecordsStatus::getStatus, TaRecordsStatus::getId)).stream()
                    .collect(Collectors.groupingBy(TaRecordsStatus::getStatus, Collectors.counting()));
        }

        /**
         * 根据状态获取最新10条数据
         *
         * @param propertyId 资产 id
         * @param status     状态
         * @param limit      查询数量
         * @return TaRecordsStatus 集合
         * @author zc
         * @date 2024/11/1
         */
        @Override
        public List<TaRecordsStatus> listLimitByStatus(int propertyId, int status, int limit) {
            return list(new QueryWrapper<TaRecordsStatus>().lambda()
                    .eq(TaRecordsStatus::getPropertyId, propertyId)
                    .eq(TaRecordsStatus::getStatus, status)
                    .orderByDesc(TaRecordsStatus::getUpdateTime)
                    .last("LIMIT " + limit));
        }
    }