# HFHistoryDataListenerFileParseByOperaImpl.java

    package cloud.wconnect.rateshop.service.revenue.historyandforecast.history;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.rateshop.pojo.bo.revenue.HistoryAndForecastBO;
    import cloud.wconnect.rateshop.service.revenue.IRevenueImportService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.google.common.collect.Lists;
    import lombok.RequiredArgsConstructor;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.Node;
    import org.dom4j.io.SAXReader;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.io.InputStream;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Collections;
    import java.util.List;

    /**
     * @author yyt
     * 2026/3/3
     */
    @RequiredArgsConstructor
    @Service
    @DS("rate_shop")
    public class HFHistoryDataListenerFileParseByOperaImpl implements IHFHistoryDataListenerFileParse {

        private final IRevenueImportService revenueImportService;

        private static final String SOURCE = SystemPlatformEnum.OPERA.getDesc();

        /**
         * 判断支持的平台
         *
         * @param source 平台
         * @return boolean
         * @author yyt
         * @date 2026/3/3
         */
        @Override
        public boolean supports(String source) {
            return SOURCE.equals(source);
        }

        /**
         * 解析 History and Forecast 报告数据
         *
         * @param propertyDTO 资产
         * @param in          文件流
         * @param startDate   开始日期
         * @param endDate     结束日期
         * @return java.util.List<cloud.wconnect.rateshop.pojo.bo.revenue.HistoryAndForecastBO>
         * @author yyt
         * @date 2026/3/3
         */
        @Override
        public List<HistoryAndForecastBO> parseReport(PropertyDTO propertyDTO, InputStream in, Long startDate, Long endDate) throws Exception {
            // 解析文件数据
            Document document = new SAXReader().read(in);
            List<Node> nodes = document.selectNodes("//G_CONSIDERED_DATE");
            if (CollectionUtils.isEmpty(nodes)) {
                return Collections.emptyList();
            }

            // 最多 104 天数据
            List<HistoryAndForecastBO> historyAndForecastBOS = Lists.newArrayListWithExpectedSize(31);
            ZoneId zoneId = ZoneId.of(propertyDTO.getTimezone());
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_MM_DD_YY_STRIKE);
            DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            int dataPrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());

            for (Node node : nodes) {
                Element dataElement = (Element) node;
                String theDateStr = dataElement.element("CHAR_CONSIDERED_DATE").getTextTrim();
                if (!theDateStr.isEmpty()) {
                    ZonedDateTime targetDateZoneDateTime = LocalDate.parse(StringUtils.substringBefore(theDateStr, " "),
                            dateTimeFormatter).atStartOfDay(zoneId);
                    long targetDate = targetDateZoneDateTime.toEpochSecond();
                    if (targetDate >= startDate && targetDate <= endDate) {
                        HistoryAndForecastBO historyAndForecastBO = new HistoryAndForecastBO();
                        historyAndForecastBO.setTargetDate(targetDateZoneDateTime.format(dateTimeFormatter2));

                        String occPercentStr = dataElement.element("CF_OCCUPANCY").getTextTrim();
                        if (StringUtils.isNotEmpty(occPercentStr)) {
                            historyAndForecastBO.setOccPercent(new BigDecimal(occPercentStr).setScale(dataPrecision, roundingMode));
                        }

                        String transientRoomsStr = dataElement.element("IND_DEDUCT_ROOMS").getTextTrim();
                        if (StringUtils.isNotEmpty(transientRoomsStr)) {
                            historyAndForecastBO.setTransientRoomNum(Integer.valueOf(transientRoomsStr));
                        }

                        String groupRoomsStr = dataElement.element("GRP_DEDUCT_ROOMS").getTextTrim();
                        if (StringUtils.isNotEmpty(groupRoomsStr)) {
                            historyAndForecastBO.setGroupRoomNum(Integer.valueOf(groupRoomsStr));
                        }

                        String adrStr = dataElement.element("CF_AVERAGE_ROOM_RATE").getTextTrim();
                        if (StringUtils.isNotEmpty(adrStr)) {
                            historyAndForecastBO.setAdr(new BigDecimal(adrStr).setScale(dataPrecision, roundingMode));
                        }

                        historyAndForecastBOS.add(historyAndForecastBO);

                        // 提前结束循环
                        if (targetDate == endDate) {
                            break;
                        }
                    }
                }
            }
            return historyAndForecastBOS;
        }

        /**
         * 保存报告数据
         *
         * @param propertyDTO 资产
         * @param boList      报告数据
         * @param operatorId  操作人
         * @author yyt
         * @date 2026/3/3
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveReportData(PropertyDTO propertyDTO, List<HistoryAndForecastBO> boList, int operatorId) {
            revenueImportService.saveHistoryAndForecastData(propertyDTO, boList, operatorId);
        }

    }