# GmwrSelloutEfficiencyServiceImpl.java

    package cloud.wconnect.workperformance.service.gmwr.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.RoomsConfigDTO;
    import cloud.wconnect.commonlib.service.RoomsConfigInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.workperformance.cons.gmwr.GmwrCons;
    import cloud.wconnect.workperformance.cons.gmwr.selloutefficiency.GmwrSelloutEfficiencyCons;
    import cloud.wconnect.workperformance.cons.gmwr.selloutefficiency.GmwrSelloutEfficiencyEnum;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrDailySummaryOcc;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrSelloutEfficiency;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrSelloutEfficiencyNote;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrWeeks;
    import cloud.wconnect.workperformance.mapper.gmwr.GmwrSelloutEfficiencyMapper;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.note.GmwrNoteDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.GmwrOccSelloutConfigDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.GmwrSelloutEfficiencyDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.GmwrSelloutEfficiencyPackageDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.GmwrSelloutEfficiencyValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.basic.GmwrSelloutEfficiencyBasicDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.basic.GmwrSelloutEfficiencyBasicValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.internal.OooRoomCountInternalDTO;
    import cloud.wconnect.workperformance.service.OooRoomInternalService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrDailySummaryOccService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrOccSelloutConfigService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrSelloutEfficiencyNoteService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrSelloutEfficiencyService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    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.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.time.LocalDate;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Set;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * GM Weekly Report Sellout Efficiency 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-10-12
     */
    @Service
    @DS("work_performance")
    public class GmwrSelloutEfficiencyServiceImpl extends ServiceImpl<GmwrSelloutEfficiencyMapper, GmwrSelloutEfficiency> implements IGmwrSelloutEfficiencyService {
        @Resource
        private RoomsConfigInternalService roomsConfigInternalService;
        @Resource
        private OooRoomInternalService oooRoomInternalService;
        @Resource
        private IGmwrOccSelloutConfigService occSelloutConfigService;
        @Resource
        private IGmwrDailySummaryOccService dailySummaryOccService;
        @Resource
        private IGmwrSelloutEfficiencyNoteService selloutEfficiencyNoteService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private ObjectMapper objectMapper;
        @Resource
        private BeanMapper beanMapper;

        /**
         * 生成 Sellout Efficiency 数据
         *
         * @param propertyDTO 资产
         * @param gmwrWeeks   周报数据
         * @author zc
         * @date 2024/10/20
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void generate(PropertyDTO propertyDTO, GmwrWeeks gmwrWeeks) {
            // 获取房间数配置
            RoomsConfigDTO roomsConfigDTO = roomsConfigInternalService.getRoomsConfig(propertyDTO.getId());
            Integer totalRooms = Objects.isNull(roomsConfigDTO) ? null : roomsConfigDTO.getRooms();

            // 由于生成的是前一周的数据，因此需要获取前一周的开始和结束日期
            LocalDate startLocalDate = DateTimeUtils.strToLocalDate(gmwrWeeks.getWeek().toString(),
                    BaseConstant.DATE_FORMAT_YYYYMMDD).minusDays(7);
            long startDate = Long.parseLong(DateTimeUtils.localDateToStr(startLocalDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
            LocalDate endLocalDate = startLocalDate.plusDays(6);
            long endDate = Long.parseLong(DateTimeUtils.localDateToStr(endLocalDate, BaseConstant.DATE_FORMAT_YYYYMMDD));

            // 获取资产配置的 OCC 时刻
            GmwrOccSelloutConfigDTO configDTO = occSelloutConfigService.getByPropertyId(propertyDTO.getId());
            // 获取 Daily Summary OCC
            List<Integer> hours = List.of(configDTO.getOccHour(), GmwrSelloutEfficiencyCons.FINAL_OCC_HOUR);
            List<GmwrDailySummaryOcc> dailySummaryOccList = dailySummaryOccService.list(propertyDTO.getId(), startDate, endDate, hours);

            // 获取指定日期未完成的 OOO 房间数
            List<OooRoomCountInternalDTO> oooRoomDTOList = oooRoomInternalService.listCount(propertyDTO.getId(), startDate, endDate);

            // 生成 Basic DTO 数据
            List<GmwrSelloutEfficiencyBasicDTO> basicDTOList = generateBasicDTOList(propertyDTO, configDTO, totalRooms,
                    startLocalDate, endLocalDate, dailySummaryOccList, oooRoomDTOList);

            GmwrSelloutEfficiency selloutEfficiency = new GmwrSelloutEfficiency();
            selloutEfficiency.setPropertyId(propertyDTO.getId());
            selloutEfficiency.setReportId(gmwrWeeks.getId());
            selloutEfficiency.setTotalRooms(totalRooms);
            selloutEfficiency.setDetail(JsonUtils.convertObjListToString(objectMapper, basicDTOList));
            selloutEfficiency.setConfigDetail(JsonUtils.convertObjToString(objectMapper, configDTO));

            baseMapper.insertOrUpdateByUnique(selloutEfficiency);
        }

        /**
         * 生成 Basic DTO 数据
         *
         * @param propertyDTO         资产
         * @param configDTO           OCC 配置
         * @param totalRooms          总房间数量
         * @param startLocalDate      开始日期
         * @param endLocalDate        结束日期
         * @param dailySummaryOccList Daily Summary OCC 数据
         * @param oooRoomDTOList      OOO Room Count 数据
         * @return GmwrSelloutEfficiencyBasicDTO 集合
         * @author zc
         * @date 2024/10/21
         */
        private List<GmwrSelloutEfficiencyBasicDTO> generateBasicDTOList(PropertyDTO propertyDTO, GmwrOccSelloutConfigDTO configDTO,
                                                                         Integer totalRooms, LocalDate startLocalDate, LocalDate endLocalDate,
                                                                         List<GmwrDailySummaryOcc> dailySummaryOccList,
                                                                         List<OooRoomCountInternalDTO> oooRoomDTOList) {
            Map<Long, Map<Integer, GmwrDailySummaryOcc>> hour2OccByDate = dailySummaryOccList.stream()
                    .collect(Collectors.groupingBy(GmwrDailySummaryOcc::getDate,
                            Collectors.toMap(GmwrDailySummaryOcc::getHour, Function.identity())));

            Map<Long, OooRoomCountInternalDTO> date2OooCountDTO = oooRoomDTOList.stream()
                    .collect(Collectors.toMap(OooRoomCountInternalDTO::getDate, Function.identity()));

            List<GmwrSelloutEfficiencyBasicValueDTO> occList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> oooRoomsList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> roomsSoldList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> roomsUnsoldList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> roomTotalList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> occFinalList = Lists.newArrayList();
            List<GmwrSelloutEfficiencyBasicValueDTO> selloutEfficiencyList = Lists.newArrayList();
            for (LocalDate indexDate = startLocalDate; !indexDate.isAfter(endLocalDate); indexDate = indexDate.plusDays(1)) {
                long date = Long.parseLong(DateTimeUtils.localDateToStr(indexDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
                Map<Integer, GmwrDailySummaryOcc> hour2Occ = hour2OccByDate.getOrDefault(date, Maps.newHashMap());
                GmwrDailySummaryOcc finalOcc = hour2Occ.getOrDefault(GmwrSelloutEfficiencyCons.FINAL_OCC_HOUR, new GmwrDailySummaryOcc());
                GmwrDailySummaryOcc occ = hour2Occ.getOrDefault(configDTO.getOccHour(), new GmwrDailySummaryOcc());
                occList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(occ.getOccPercent())
                        .build());

                OooRoomCountInternalDTO oooRoomCount = date2OooCountDTO.getOrDefault(date, new OooRoomCountInternalDTO());
                Integer oooRoomsCount = oooRoomCount.getOooRoomsCount();
                oooRoomsList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(Objects.isNull(oooRoomsCount) ? null : new BigDecimal(oooRoomsCount.toString()))
                        .build());

                Integer roomsSold = finalOcc.getRoomsSold();
                roomsSoldList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(Objects.isNull(roomsSold) ? null : new BigDecimal(roomsSold.toString()))
                        .build());

                Integer roomsUnSold = (Objects.isNull(totalRooms) || Objects.isNull(oooRoomsCount) || Objects.isNull(roomsSold)) ?
                        null : totalRooms - oooRoomsCount - roomsSold;
                roomsUnsoldList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(Objects.isNull(roomsUnSold) ? null : new BigDecimal(roomsUnSold.toString()))
                        .build());

                roomTotalList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(Objects.isNull(totalRooms) ? null : new BigDecimal(totalRooms.toString()))
                        .build());

                occFinalList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(finalOcc.getOccPercent())
                        .build());

                selloutEfficiencyList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                        .date(date)
                        .value(getSelloutEfficiencyValue(finalOcc.getOccPercent()))
                        .build());
            }
            return Lists.newArrayList(new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.OCC.getCategory(), occList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.OOO_ROOMS.getCategory(), oooRoomsList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.ROOM_SOLD.getCategory(), roomsSoldList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.ROOM_UNSOLD.getCategory(), roomsUnsoldList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.ROOM_TOTAL.getCategory(), roomTotalList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.OCC_FINAL.getCategory(), occFinalList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.SELLOUT_EFFICIENCY.getCategory(), selloutEfficiencyList),
                    new GmwrSelloutEfficiencyBasicDTO(GmwrSelloutEfficiencyEnum.SELLOUT_EFFICIENCY_WEEKLY.getCategory(),
                            List.of(new GmwrSelloutEfficiencyBasicValueDTO(null, getSelloutEfficiencyWeekly(propertyDTO,
                                    configDTO, occList, occFinalList)))));
        }

        /**
         * 获取 Sellout Efficiency 数值
         *
         * @param occFinal Occ Final
         * @return BigDecimal
         * @author zc
         * @date 2025/6/6
         */
        private BigDecimal getSelloutEfficiencyValue(BigDecimal occFinal) {
            if (Objects.isNull(occFinal)) {
                return null;
            }
            // 若 Occ Final < 100%，则 Sellout Efficiency = 0%，否则 Sellout Efficiency = 100%
            if (occFinal.compareTo(GmwrSelloutEfficiencyCons.PERFECT_SELL) < 0) {
                return BigDecimal.ZERO;
            }
            return GmwrSelloutEfficiencyCons.PERFECT_SELL;
        }

        /**
         * 生成空集合
         *
         * @param startLocalDate 开始日期
         * @param endLocalDate   结束日期
         * @return GmwrSelloutEfficiencyBasicDTO 集合
         * @author zc
         * @date 2025/3/3
         */
        private List<GmwrSelloutEfficiencyBasicDTO> generateEmptyBasicDTOList(LocalDate startLocalDate, LocalDate endLocalDate) {
            List<GmwrSelloutEfficiencyBasicDTO> basicDTOList = Lists.newArrayList();
            Lists.newArrayList(GmwrSelloutEfficiencyEnum.OCC.getCategory(), GmwrSelloutEfficiencyEnum.OOO_ROOMS.getCategory(),
                    GmwrSelloutEfficiencyEnum.ROOM_SOLD.getCategory(), GmwrSelloutEfficiencyEnum.ROOM_UNSOLD.getCategory(),
                    GmwrSelloutEfficiencyEnum.ROOM_TOTAL.getCategory(), GmwrSelloutEfficiencyEnum.OCC_FINAL.getCategory(),
                    GmwrSelloutEfficiencyEnum.SELLOUT_EFFICIENCY.getCategory()).forEach(category -> {
                List<GmwrSelloutEfficiencyBasicValueDTO> basicValueDTOList = Lists.newArrayList();
                for (LocalDate indexDate = startLocalDate; !indexDate.isAfter(endLocalDate); indexDate = indexDate.plusDays(1)) {
                    long date = Long.parseLong(DateTimeUtils.localDateToStr(indexDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
                    basicValueDTOList.add(GmwrSelloutEfficiencyBasicValueDTO.builder()
                            .date(date).build());
                }
                basicDTOList.add(GmwrSelloutEfficiencyBasicDTO.builder()
                        .category(category).list(basicValueDTOList)
                        .build());
            });
            basicDTOList.add(GmwrSelloutEfficiencyBasicDTO.builder()
                    .category(GmwrSelloutEfficiencyEnum.SELLOUT_EFFICIENCY_WEEKLY.getCategory())
                    .list(Lists.newArrayList(new GmwrSelloutEfficiencyBasicValueDTO()))
                    .build());
            return basicDTOList;
        }

        /**
         * 获取 Sellout Efficiency
         *
         * @param propertyDTO  资产
         * @param configDTO    配置规则
         * @param occList      OCC
         * @param occFinalList OCC Final
         * @return Sellout Efficiency
         * @author zc
         * @date 2025/6/13
         */
        private BigDecimal getSelloutEfficiencyWeekly(PropertyDTO propertyDTO, GmwrOccSelloutConfigDTO configDTO,
                                                      List<GmwrSelloutEfficiencyBasicValueDTO> occList,
                                                      List<GmwrSelloutEfficiencyBasicValueDTO> occFinalList) {
            if (CollectionUtils.isEmpty(occList) || CollectionUtils.isEmpty(occFinalList)) {
                return null;
            }

            Map<Long, GmwrSelloutEfficiencyBasicValueDTO> date2ValueDTO = occList.stream()
                    .collect(Collectors.toMap(GmwrSelloutEfficiencyBasicValueDTO::getDate, Function.identity()));

            /*
             * Perfect Sell = OCC Final >= 100%; Technical sellout = OCC 7PM >= 95%;
             * If reached perfect sell however OCC 7PM < 95%, still consider a Technical sellout;
             * 0 night / 2 night = 0%
             */
            long perfectSell = 0;
            long technicalSellout = 0;
            for (GmwrSelloutEfficiencyBasicValueDTO item : occFinalList) {
                BigDecimal occFinalValue = item.getValue();
                // 若 OCC Final >= 100%，则 perfectSell++, technicalSellout++
                if (Objects.nonNull(occFinalValue) && occFinalValue.compareTo(GmwrSelloutEfficiencyCons.PERFECT_SELL) >= 0) {
                    perfectSell++;
                    technicalSellout++;
                    continue;
                }
                BigDecimal occValue = date2ValueDTO.getOrDefault(item.getDate(), new GmwrSelloutEfficiencyBasicValueDTO()).getValue();
                if (Objects.nonNull(occValue) && occValue.compareTo(configDTO.getOccThreshold()) >= 0) {
                    technicalSellout++;
                }
            }

            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());
            return (technicalSellout == 0) ? null : new BigDecimal(perfectSell).multiply(new BigDecimal(100))
                    .divide(new BigDecimal(technicalSellout), BaseConstant.DATA_PRECISION, roundingMode);
        }

        /**
         * 获取 OOO Rooms
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param gmwrWeeks  周报
         * @return GmwrSelloutEfficiencyPackageDTO
         * @author zc
         * @date 2024/10/21
         */
        @Override
        public GmwrSelloutEfficiencyPackageDTO getDetail(int tenantId, int propertyId, GmwrWeeks gmwrWeeks) {
            List<GmwrSelloutEfficiencyBasicDTO> basicDTOList;
            GmwrSelloutEfficiency selloutEfficiency = getByReportId(gmwrWeeks.getId());

            GmwrOccSelloutConfigDTO currentConfig;
            if (Objects.isNull(selloutEfficiency)) {
                // 由于获取的是前一周的数据，因此需要获取前一周的开始和结束日期
                LocalDate startLocalDate = DateTimeUtils.strToLocalDate(gmwrWeeks.getWeek().toString(),
                        BaseConstant.DATE_FORMAT_YYYYMMDD).minusDays(7);
                LocalDate endLocalDate = startLocalDate.plusDays(6);
                basicDTOList = generateEmptyBasicDTOList(startLocalDate, endLocalDate);

                currentConfig = occSelloutConfigService.getByPropertyId(propertyId);
            } else {
                basicDTOList = JsonUtils.convertStringToList(objectMapper, selloutEfficiency.getDetail(), GmwrSelloutEfficiencyBasicDTO.class);

                currentConfig = JsonUtils.convertStringToObject(objectMapper, selloutEfficiency.getConfigDetail(), GmwrOccSelloutConfigDTO.class);
            }

            // 获取备注数据
            List<GmwrSelloutEfficiencyNote> noteList = selloutEfficiencyNoteService.listByReportId(gmwrWeeks.getId());
            Map<Integer, GmwrSelloutEfficiencyNote> category2Note = noteList.stream()
                    .collect(Collectors.toMap(GmwrSelloutEfficiencyNote::getCategory, Function.identity()));
            Set<Integer> userIds = noteList.stream().map(GmwrSelloutEfficiencyNote::getUpdatedBy).collect(Collectors.toSet());
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId, userIds);

            List<GmwrSelloutEfficiencyDTO> selloutEfficiencyDTOList = beanMapper.mapList(basicDTOList,
                    GmwrSelloutEfficiencyDTO.class, (basicDTO, dto) -> {
                        dto.setCategoryName(GmwrSelloutEfficiencyEnum.getCategoryName(basicDTO.getCategory()));
                        GmwrSelloutEfficiencyNote note = category2Note.get(dto.getCategory());
                        if (Objects.nonNull(note)) {
                            GmwrNoteDTO noteDTO = beanMapper.map(note, GmwrNoteDTO.class, noteDto ->
                                    noteDto.setUpdatedName(userId2Name.get(noteDto.getUpdatedBy())));
                            dto.setNote(noteDTO);
                        }
                    });

            // 组装 Sellout Efficiency 比较结果
            packageJudgment(currentConfig.getOccThreshold(), selloutEfficiencyDTOList);

            return new GmwrSelloutEfficiencyPackageDTO(selloutEfficiencyDTOList, currentConfig);
        }

        /**
         * 根据周报 id 获取 oooRooms
         *
         * @param reportId 周报 id
         * @return GmwrOooRooms
         * @author zc
         * @date 2024/11/6
         */
        @Override
        public GmwrSelloutEfficiency getByReportId(int reportId) {
            return getOne(new LambdaQueryWrapper<GmwrSelloutEfficiency>()
                    .eq(GmwrSelloutEfficiency::getReportId, reportId));
        }

        /**
         * 组装 Sellout Efficiency 比较结果
         *
         * @param occThreshold             OCC 阈值
         * @param selloutEfficiencyDTOList DTO数据集合
         * @author zc
         * @date 2024/10/21
         */
        private void packageJudgment(BigDecimal occThreshold, List<GmwrSelloutEfficiencyDTO> selloutEfficiencyDTOList) {
            Map<Integer, GmwrSelloutEfficiencyDTO> category2DTO = selloutEfficiencyDTOList.stream()
                    .collect(Collectors.toMap(GmwrSelloutEfficiencyDTO::getCategory, Function.identity()));

            Map<Long, GmwrSelloutEfficiencyValueDTO> date2OccFinal = category2DTO.getOrDefault(GmwrSelloutEfficiencyEnum.OCC_FINAL.getCategory(),
                            new GmwrSelloutEfficiencyDTO()).getList().stream()
                    .collect(Collectors.toMap(GmwrSelloutEfficiencyValueDTO::getDate, Function.identity()));
            Map<Long, GmwrSelloutEfficiencyValueDTO> date2OccPm = category2DTO.getOrDefault(GmwrSelloutEfficiencyEnum.OCC.getCategory(),
                            new GmwrSelloutEfficiencyDTO()).getList().stream()
                    .collect(Collectors.toMap(GmwrSelloutEfficiencyValueDTO::getDate, Function.identity()));

            List<GmwrSelloutEfficiencyValueDTO> selloutEfficiencyList = category2DTO.getOrDefault(GmwrSelloutEfficiencyEnum.SELLOUT_EFFICIENCY.getCategory(),
                    new GmwrSelloutEfficiencyDTO()).getList();

            for (GmwrSelloutEfficiencyValueDTO selloutEfficiencyValueDTO : selloutEfficiencyList) {
                BigDecimal occFinal = date2OccFinal.getOrDefault(selloutEfficiencyValueDTO.getDate(), new GmwrSelloutEfficiencyValueDTO()).getValue();
                BigDecimal occPm = date2OccPm.getOrDefault(selloutEfficiencyValueDTO.getDate(), new GmwrSelloutEfficiencyValueDTO()).getValue();
                // 设置比较结果
                selloutEfficiencyValueDTO.setJudgment(getJudgment(occPm, occFinal, occThreshold));
            }
        }

        /**
         * 获取比较结果
         *
         * @param occPm          OCC PM
         * @param occFinal       OCC Final
         * @param occPmThreshold OCC PM Threshold
         * @return java.lang.String
         * @author zc
         * @date 2025/1/17
         */
        private String getJudgment(BigDecimal occPm, BigDecimal occFinal, BigDecimal occPmThreshold) {
            /*
             * 1. 如果 OCC Final = 100%，则 Sellout Efficiency = 100%，显示为绿色；
             * 2. 如果 OCC PM < 阈值，并且 OCC Final < 100%，则 Sellout Efficiency = 0%，显示为红色；
             * 3. 如果 OCC PM ≥ 阈值，并且 OCC Final ≥ OCC PM，则 Sellout Efficiency = 0%，显示为黄色；
             * 4. 如果 OCC PM ≥ 阈值，并且 OCC Final < OCC PM，则 Sellout Efficiency = 0%，显示为红色。
             */
            if (occFinal == null) {
                return null;
            }
            // 如果 OCC Final = 100%，则 Sellout Efficiency = 100%，显示为绿色 Good
            if (occFinal.compareTo(GmwrSelloutEfficiencyCons.PERFECT_SELL) >= 0) {
                return GmwrCons.JUDGMENT_GOOD;
            }
            if (occPm == null) {
                return null;
            }
            // 如果 OCC PM < 阈值，则 Sellout Efficiency = 0%，显示为红色 Bad
            if (occPm.compareTo(occPmThreshold) < 0) {
                return GmwrCons.JUDGMENT_BAD;
            }
            // OCC Final ≥ OCC PM，则 Sellout Efficiency = 0%，显示为黄色 Cure
            if (occFinal.compareTo(occPm) >= 0) {
                return GmwrCons.JUDGMENT_CURE;
            }
            // OCC Final < OCC PM，则 Sellout Efficiency = 0%，显示为红色 Bad
            return GmwrCons.JUDGMENT_BAD;
        }
    }