# HotelKpiM3FbDailyFlashReportParseService.java

    package cloud.wconnect.workperformance.service.hotelkpi.m3;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.exception.InternalServerErrorException;
    import cloud.wconnect.commonlib.exception.ParseReportException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.workperformance.pojo.bo.hotelkpi.m3.HotelKpiM3FbDailyFlashBO;
    import cloud.wconnect.workperformance.pojo.bo.hotelkpi.m3.HotelKpiM3FbDailyFlashValueBO;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;

    import java.io.InputStream;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    /**
     * 解析报告：M3 F&B Daily Flash Report
     *
     * @author zc
     * @date 2025/08/27
     */
    @Slf4j
    @Component
    @DS("work_performance")
    public class HotelKpiM3FbDailyFlashReportParseService {

        private static final DailyFlashReportFullParser dailyFlashReportFullParser = new DailyFlashReportFullParser();

        /**
         * 解析报告：M3 Monthly Report Totals
         *
         * @param propertyDTOList 资产列表
         * @param in              文件
         * @return 解析结果
         * @throws ParseReportException 解析异常
         * @author zc
         * @date 2025/8/27
         */
        public List<HotelKpiM3FbDailyFlashBO> parse(List<PropertyDTO> propertyDTOList, InputStream in) throws ParseReportException {
            try {
                String flashResult = dailyFlashReportFullParser.parse(in);

                // 解析JSON并排序
                ObjectMapper mapper = new ObjectMapper();
                mapper.enable(SerializationFeature.INDENT_OUTPUT);

                List<DailyFlashReportFullParser.PropertyGroup> propertyGroups =
                        mapper.readValue(flashResult, new TypeReference<>() {
                        });

                List<HotelKpiM3FbDailyFlashBO> result = Lists.newArrayList();

                // 将 propertyGroups 按 property 分组
                Map<String, List<DailyFlashReportFullParser.PropertyGroup>> propertyGroupsByProperty =
                        propertyGroups.stream().collect(Collectors.groupingBy(pg -> pg.property));

                // 遍历每个资产，查找匹配的 propertyGroup
                for (PropertyDTO propertyDTO : propertyDTOList) {
                    RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                            .getInternationalization().getNumber().getCalculationRule());

                    HotelKpiM3FbDailyFlashBO bo = new HotelKpiM3FbDailyFlashBO();
                    bo.setPropertyId(propertyDTO.getId());
                    List<DailyFlashReportFullParser.PropertyGroup> matchedGroups =
                            M3ReportCommonUtils.getIgnoreCase(propertyGroupsByProperty, propertyDTO.getName());
                    if (matchedGroups == null || matchedGroups.isEmpty()) {
                        continue;
                    }
                    matchedGroups.forEach(pg -> setPropertyGroupToBO(pg, bo, roundingMode));
                    result.add(bo);
                }

                return result;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Handle parse Hotel KPI M3 Daily Flash Report error.", e);
            }
        }

        /**
         * 将 PropertyGroup 数值设置到 HotelKpiM3FbDailyFlashBO
         *
         * @param propertyGroup PropertyGroup 对象
         * @param bo            对象
         * @param roundingMode  舍入模式
         * @author zc
         * @date 2025/8/27
         */
        private void setPropertyGroupToBO(DailyFlashReportFullParser.PropertyGroup propertyGroup,
                                          HotelKpiM3FbDailyFlashBO bo, RoundingMode roundingMode) {
            // 计算该 PropertyGroup 下所有 items 的总和
            HotelKpiM3FbDailyFlashValueBO valueBO = calculateCategoryTotal(propertyGroup.items, roundingMode);

            String category = propertyGroup.category.toLowerCase();
            if (category.contains("food") && category.contains("beverage")) {
                // F&B Revenue 对应 Food & Beverage Revenue 分类
                bo.setFbRevenue(valueBO);
            } else if (category.contains("banquet")) {
                // Banquet Revenue 对应 Banquet Revenue 分类
                bo.setBanquetRevenue(valueBO);
            } else if (category.contains("other")) {
                // Other Revenue 对应 Other Revenue 分类
                bo.setOtherRevenue(valueBO);
            }
        }

        /**
         * 计算分类总和
         *
         * @param items        分类下的所有项目
         * @param roundingMode 舍入模式
         * @return HotelKpiM3FbDailyFlashValueBO 对象
         * @author zc
         * @date 2025/8/27
         */
        private HotelKpiM3FbDailyFlashValueBO calculateCategoryTotal(List<DailyFlashReportFullParser.ReportItem> items,
                                                                     RoundingMode roundingMode) {
            BigDecimal currentTotal = BigDecimal.ZERO;
            BigDecimal budgetTotal = BigDecimal.ZERO;
            BigDecimal previousTotal = BigDecimal.ZERO;

            for (DailyFlashReportFullParser.ReportItem item : items) {
                if (item == null) {
                    continue;
                }
                // 跳过 "Total"
                if (item.item_name.equalsIgnoreCase("total")) {
                    continue;
                }
                currentTotal = currentTotal.add(M3ReportCommonUtils.parseCurrencyValue(item.actual_mtd));
                budgetTotal = budgetTotal.add(M3ReportCommonUtils.parseCurrencyValue(item.budget_mtd));
                previousTotal = previousTotal.add(M3ReportCommonUtils.parseCurrencyValue(item.ly_actual_mtd));
            }

            HotelKpiM3FbDailyFlashValueBO valueBO = new HotelKpiM3FbDailyFlashValueBO();
            valueBO.setMtd(currentTotal.setScale(BaseConstant.DATA_PRECISION, roundingMode));
            valueBO.setMtdBudget(budgetTotal.setScale(BaseConstant.DATA_PRECISION, roundingMode));
            valueBO.setMtdLastYear(previousTotal.setScale(BaseConstant.DATA_PRECISION, roundingMode));
            return valueBO;
        }

    }