# GmwrInternalServiceImpl.java

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

    import cloud.wconnect.workperformance.cons.gmwr.selloutefficiency.GmwrSelloutEfficiencyCons;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrDailySummaryOcc;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrStrData;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrWeeks;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.internal.SelloutEfficiencyInternalDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.internal.SelloutEfficiencyOccInternalDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.selloutefficiency.GmwrOccSelloutConfigDTO;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrDailySummaryOccService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrOccSelloutConfigService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrStrDataService;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrWeeksService;
    import cloud.wconnect.workperformance.service.gmwr.internal.IGmwrInternalService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import jakarta.annotation.Resource;
    import org.springframework.stereotype.Service;

    import java.math.BigDecimal;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * Internal Service
     *
     * @author zc
     * @date 2025/06/11
     */
    @Service
    @DS("work_performance")
    public class GmwrInternalServiceImpl implements IGmwrInternalService {

        @Resource
        private IGmwrDailySummaryOccService dailySummaryOccService;
        @Resource
        private IGmwrOccSelloutConfigService occSelloutConfigService;
        @Resource
        private IGmwrStrDataService strDataService;
        @Resource
        private IGmwrWeeksService weeksService;

        /**
         * 根据日期返回获取 Sellout Efficiency OCC 数据
         *
         * @param propertyId 资产 id
         * @param startDate  开始日期(yyyyMMdd)
         * @param endDate    结束日期(yyyyMMdd)
         * @return SelloutEfficiencyOccInternalDTO 集合
         * @author zc
         * @date 2025/6/13
         */
        @Override
        public List<SelloutEfficiencyOccInternalDTO> listSelloutEfficiencyOcc(int propertyId, long startDate, long endDate) {
            GmwrOccSelloutConfigDTO configDTO = occSelloutConfigService.getByPropertyId(propertyId);
            int occHour = configDTO.getOccHour();
            int occFinalHour = GmwrSelloutEfficiencyCons.FINAL_OCC_HOUR;
            // 获取 Daily Summary OCC
            Map<Long, List<GmwrDailySummaryOcc>> date2OccList = dailySummaryOccService.list(propertyId, startDate,
                    endDate, List.of(occHour, occFinalHour)).stream().collect(Collectors.groupingBy(GmwrDailySummaryOcc::getDate));
            return date2OccList.entrySet().stream().map(entry -> {
                Map<Integer, GmwrDailySummaryOcc> hour2OccEntity = entry.getValue().stream()
                        .collect(Collectors.toMap(GmwrDailySummaryOcc::getHour, Function.identity()));
                BigDecimal occ = hour2OccEntity.getOrDefault(occHour, new GmwrDailySummaryOcc()).getOccPercent();
                BigDecimal occFinal = hour2OccEntity.getOrDefault(occFinalHour, new GmwrDailySummaryOcc()).getOccPercent();
                return new SelloutEfficiencyOccInternalDTO(entry.getKey(), occ, occFinal);
            }).toList();
        }

        /**
         * 获取 OCC 配置
         *
         * @param propertyId 资产 id
         * @return GmwrOccSelloutConfigDTO
         * @author zc
         * @date 2025/9/13
         */
        @Override
        public GmwrOccSelloutConfigDTO getOccSelloutConfig(int propertyId) {
            return occSelloutConfigService.getByPropertyId(propertyId);
        }

        /**
         * 根据 OCC 数据获取 Sellout Efficiency
         *
         * @param configDTO OCC 配置
         * @param occList   occ 数据
         * @return SelloutEfficiencyInternalDTO
         * @author zc
         * @date 2025/6/13
         */
        @Override
        public SelloutEfficiencyInternalDTO getSelloutEfficiency(GmwrOccSelloutConfigDTO configDTO, List<SelloutEfficiencyOccInternalDTO> occList) {
            BigDecimal occThreshold = configDTO.getOccThreshold();

            /*
             * Perfect Sell = OCC Final >= 100%; Technical sellout = OCC >= 95%;
             * If reached perfect sell however OCC < 95%, still consider a Technical sellout;
             * Weekly Sellout Efficiency % = Perfect Sell / Technical sellout; 0 night / 2 night = 0%
             */
            long perfectSell = 0;
            long technicalSellout = 0;

            for (SelloutEfficiencyOccInternalDTO selloutEfficiencyOcc : occList) {
                BigDecimal occFinal = selloutEfficiencyOcc.getOccFinal();
                BigDecimal occ = selloutEfficiencyOcc.getOcc();
                if (Objects.isNull(occFinal)) {
                    continue;
                }
                // 若 OCC Final >= 100%，则 perfectSell++, technicalSellout++
                if (occFinal.compareTo(GmwrSelloutEfficiencyCons.PERFECT_SELL) >= 0) {
                    perfectSell++;
                    technicalSellout++;
                } else if (Objects.nonNull(occ) && occ.compareTo(occThreshold) >= 0) {
                    technicalSellout++;
                }
            }
            return new SelloutEfficiencyInternalDTO(perfectSell, technicalSellout);
        }

        /**
         * 获取指定周期日期的 Str Data 数据
         *
         * @param propertyId 资产 id
         * @param date       周期日期(yyyyMMdd)
         * @return GmwrStrData
         * @author zc
         * @date 2025/8/23
         */
        @Override
        public GmwrStrData getStrData(int propertyId, long date) {
            GmwrWeeks gmwrWeeks = weeksService.getByDate(propertyId, date);
            return strDataService.getOne(new LambdaQueryWrapper<GmwrStrData>()
                    .eq(GmwrStrData::getPropertyId, propertyId)
                    .eq(GmwrStrData::getReportId, gmwrWeeks.getId()));
        }
    }