# DbDbPmsHfForecastServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.workperformance.cons.database.DbCons;
    import cloud.wconnect.workperformance.entity.database.DbPmsHfForecast;
    import cloud.wconnect.workperformance.mapper.database.DbPmsHfForecastMapper;
    import cloud.wconnect.workperformance.pojo.bo.database.historyforecast.DbPmsHfBO;
    import cloud.wconnect.workperformance.pojo.dto.database.historyforecast.DbPmsHfForecastDTO;
    import cloud.wconnect.workperformance.pojo.vo.database.historyforecast.DbPmsHfVO;
    import cloud.wconnect.workperformance.service.database.IDbPmsHfForecastService;
    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 jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Service;

    import java.time.LocalDate;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Objects;

    /**
     * <p>
     * Database History and Forecast - 365 Forecast 服务实现类
     * </p>
     *
     * @author zc
     * @since 2026-01-04
     */
    @Service
    @DS("database")
    public class DbDbPmsHfForecastServiceImpl extends ServiceImpl<DbPmsHfForecastMapper, DbPmsHfForecast> implements IDbPmsHfForecastService {

        @Resource
        private BeanMapper beanMapper;
        @Resource
        private ObjectMapper objectMapper;

        /**
         * 处理报告数据
         *
         * @param propertyDTO  资产
         * @param boList       报告数据
         * @param businessDate 营业日期(yyyyMMdd)
         * @param operatorId   操作人 id
         * @author zc
         * @date 2026/1/5
         */
        @Override
        public void handle(PropertyDTO propertyDTO, List<DbPmsHfBO> boList, long businessDate, int operatorId) {
            if (CollectionUtils.isEmpty(boList)) {
                return;
            }

            List<DbPmsHfBO> forecastBOList;

            LocalDate businessLocalDate = DateTimeUtils.strToLocalDate(String.valueOf(businessDate), BaseConstant.DATE_FORMAT_YYYYMMDD);
            long maxEndData = Long.parseLong(DateTimeUtils.localDateToStr(businessLocalDate.plusDays(365), BaseConstant.DATE_FORMAT_YYYYMMDD));

            String operaDataType = boList.get(0).getOperaDataType();
            // operaDataType 不等于空时，说明数据是从 Opera xml 文件中解析的
            if (StringUtils.isNotBlank(operaDataType)) {
                forecastBOList = boList.stream().filter(bo -> {
                    LocalDate localDate = DateTimeUtils.strToLocalDate(bo.getDStr(), BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                    long date = Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
                    return DbCons.HF_REPORT_DATA_FORECAST.equals(bo.getOperaDataType())
                            && (date >= businessDate) && (date <= maxEndData);
                }).toList();
            } else {
                forecastBOList = boList.stream().filter(bo -> {
                    LocalDate localDate = DateTimeUtils.strToLocalDate(bo.getDStr(), BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                    long date = Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
                    return (date >= businessDate) && (date <= maxEndData);
                }).toList();
            }

            if (CollectionUtils.isEmpty(forecastBOList)) {
                return;
            }

            // 转换为 DTO
            List<DbPmsHfForecastDTO> forecastDTOList = beanMapper.mapList(forecastBOList, DbPmsHfForecastDTO.class, (bo, dto) -> {
                LocalDate localDate = DateTimeUtils.strToLocalDate(bo.getDStr(), BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                long date = Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
                dto.setD(date);
            });

            long nowTimestamp = DateTimeUtils.nowTimestamp();
            int propertyId = propertyDTO.getId();

            DbPmsHfForecast hfForecast = getByDate(propertyId, businessDate);
            if (Objects.nonNull(hfForecast)) {
                // 从 DB 数据中过滤出报告中不存在的数据，然后和报告中的数据合并为新的集合，最后更新到 DB 中
                List<Long> dateListByReport = forecastDTOList.stream().map(DbPmsHfForecastDTO::getD).toList();
                List<DbPmsHfForecastDTO> filterOriginDTOList = JsonUtils.convertStringToList(objectMapper,
                                hfForecast.getData(), DbPmsHfForecastDTO.class).stream()
                        .filter(item -> !dateListByReport.contains(item.getD())).toList();
                forecastDTOList.addAll(filterOriginDTOList);
            } else {
                hfForecast = new DbPmsHfForecast();
                hfForecast.setPropertyId(propertyId);
                hfForecast.setDate(businessDate);
            }
            hfForecast.setData(JsonUtils.convertObjListToString(objectMapper, forecastDTOList));
            hfForecast.setUpdateBy(operatorId);
            hfForecast.setUpdateTime(nowTimestamp);

            saveOrUpdate(hfForecast);
        }

        /**
         * 获取指定日期的数据
         *
         * @param propertyId   资产 id
         * @param businessDate 营业日期(yyyyMMdd)
         * @return PmsHfForecast
         * @author zc
         * @date 2026/1/5
         */
        private DbPmsHfForecast getByDate(int propertyId, long businessDate) {
            return getOne(new LambdaQueryWrapper<DbPmsHfForecast>()
                    .eq(DbPmsHfForecast::getDate, businessDate)
                    .eq(DbPmsHfForecast::getPropertyId, propertyId));
        }

        /**
         * 获取指定 Data as of 日期的 History and forecast 数据详情
         *
         * @param propertyId 资产 id
         * @param date       Data as of 日期(yyyyMMdd)
         * @return DbPmsHfVO 集合
         * @author zc
         * @date 2026/1/12
         */
        @Override
        public List<DbPmsHfVO> detail(int propertyId, long date) {
            DbPmsHfForecast hfForecast = getByDate(propertyId, date);
            if (Objects.isNull(hfForecast)) {
                return Collections.emptyList();
            }
            List<DbPmsHfForecastDTO> dtoList = JsonUtils.convertStringToList(objectMapper,
                    hfForecast.getData(), DbPmsHfForecastDTO.class);
            List<DbPmsHfVO> voList = beanMapper.mapList(dtoList, DbPmsHfVO.class,
                    (dto, vo) -> vo.setDate(dto.getD()));
            voList.sort(Comparator.comparing(DbPmsHfVO::getDate));
            return voList;
        }
    }