# DbReportServiceImpl.java

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

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.EventBodyParamEnum;
    import cloud.wconnect.commonlib.exception.InternalServerErrorException;
    import cloud.wconnect.commonlib.exception.ListenerException;
    import cloud.wconnect.commonlib.pojo.dto.EventBodyDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.UploadUtils;
    import cloud.wconnect.sns.http.processor.Sender;
    import cloud.wconnect.workperformance.cons.BusinessEnum;
    import cloud.wconnect.workperformance.cons.database.DbPmsReportDataRangeEnum;
    import cloud.wconnect.workperformance.entity.database.DbReportImportLog;
    import cloud.wconnect.workperformance.pojo.bo.database.historyforecast.DbPmsHfBO;
    import cloud.wconnect.workperformance.pojo.bo.database.managerflash.DbPmsManagerFlashBO;
    import cloud.wconnect.workperformance.pojo.bo.database.marketcode.DbPmsMarketCodeStatisticsBO;
    import cloud.wconnect.workperformance.service.AmazonS3Service;
    import cloud.wconnect.workperformance.service.database.IDbReportService;
    import cloud.wconnect.workperformance.service.database.historyforecast.IDbPmsHfParseService;
    import cloud.wconnect.workperformance.service.database.managerflash.IDbPmsManagerFlashParseService;
    import cloud.wconnect.workperformance.service.database.marketcode.IDbPmsMarketCodeStatisticsParseService;
    import cloud.wconnect.workperformance.service.database.marketsegsummary.IDbPmsMssParseService;
    import cloud.wconnect.workperformance.service.database.occsummary.IDbPmsOsParseService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Maps;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;

    import java.io.InputStream;
    import java.text.MessageFormat;
    import java.time.LocalDate;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.List;

    /**
     * <p>
     * Database Report 服务类
     * </p>
     *
     * @author zc
     * @date 2025/12/02
     */
    @Slf4j
    @Service
    @DS("database")
    public class DbReportServiceImpl implements IDbReportService {
        private final ApplicationContext applicationContext;
        private final AmazonS3Service amazonS3Service;
        private final ObjectMapper objectMapper;
        private final Sender snsSender;

        public DbReportServiceImpl(ApplicationContext applicationContext, AmazonS3Service amazonS3Service,
                                   ObjectMapper objectMapper, @Qualifier("3pSender") Sender snsSender) {
            this.applicationContext = applicationContext;
            this.amazonS3Service = amazonS3Service;
            this.objectMapper = objectMapper;
            this.snsSender = snsSender;
        }

        /**
         * 解析报告 Manager Flash
         *
         * @param propertyDTO 资产
         * @param in          报告文件流
         * @param source      报告来源
         * @param dateRange   数据日期范围（yyyyMMdd）
         * @return DbPmsManagerFlashBO
         * @author zc
         * @date 2025/12/02
         */
        @Override
        public DbPmsManagerFlashBO parseManagerFlashReport(PropertyDTO propertyDTO, InputStream in, String source, List<Long> dateRange) {
            Collection<IDbPmsManagerFlashParseService> parserList = applicationContext.getBeansOfType(IDbPmsManagerFlashParseService.class).values();
            for (IDbPmsManagerFlashParseService parser : parserList) {
                if (parser.supports(source)) {
                    return parser.parse(propertyDTO, in, dateRange);
                }
            }
            throw new ListenerException(MessageFormat.format("Not found report parser, Report Type: Manager Flash, " +
                    "Source: {0}", source));
        }

        /**
         * 解析报告 Market Code Statistics
         *
         * @param propertyDTO 资产
         * @param in          报告文件流
         * @param source      报告来源
         * @param dateRange   数据日期范围（yyyyMMdd）
         * @return DbPmsMarketCodeStatisticsBO
         * @author zc
         * @date 2025/12/12
         */
        @Override
        public DbPmsMarketCodeStatisticsBO parseMarketCodeStatisticsReport(PropertyDTO propertyDTO, InputStream in, String source, List<Long> dateRange) {
            Collection<IDbPmsMarketCodeStatisticsParseService> parserList = applicationContext.getBeansOfType(IDbPmsMarketCodeStatisticsParseService.class).values();
            for (IDbPmsMarketCodeStatisticsParseService parser : parserList) {
                if (parser.supports(source)) {
                    return parser.parse(propertyDTO, in, dateRange);
                }
            }
            throw new ListenerException(MessageFormat.format("Not found report parser, Report Type: Market Code Statistics, " +
                    "Source: {0}", source));
        }

        /**
         * 解析报告 History and Forecast
         *
         * @param propertyDTO 资产
         * @param in          报告文件流
         * @param source      报告来源
         * @param dateRange   数据日期范围（yyyyMMdd）
         * @return List<DbPmsHfBO>
         * @author zc
         * @date 2026/1/5
         */
        @Override
        public List<DbPmsHfBO> parseHistoryAndForecastReport(PropertyDTO propertyDTO, InputStream in, String source, List<Long> dateRange) {
            Collection<IDbPmsHfParseService> parserList = applicationContext.getBeansOfType(IDbPmsHfParseService.class).values();
            for (IDbPmsHfParseService parser : parserList) {
                if (parser.supports(source)) {
                    return parser.parse(propertyDTO, in, dateRange);
                }
            }
            throw new ListenerException(MessageFormat.format("Not found report parser, Report Type: History and Forecast, " +
                    "Source: {0}", source));
        }

        /**
         * 解析报告 Occupancy Summary
         *
         * @param propertyDTO 资产
         * @param in          报告文件流
         * @param source      报告来源
         * @param dateRange   数据日期范围（yyyyMMdd）
         * @return List<DbPmsHfBO>
         * @author zc
         * @date 2026/3/5
         */
        @Override
        public List<DbPmsHfBO> parseOccupancySummaryReport(PropertyDTO propertyDTO, InputStream in, String source, List<Long> dateRange) {
            Collection<IDbPmsOsParseService> parserList = applicationContext.getBeansOfType(IDbPmsOsParseService.class).values();
            for (IDbPmsOsParseService parser : parserList) {
                if (parser.supports(source)) {
                    return parser.parse(propertyDTO, in, dateRange);
                }
            }
            throw new ListenerException(MessageFormat.format("Not found report parser, Report Type: Occupancy Summary, " +
                    "Source: {0}", source));
        }

        /**
         * 解析报告 Market Segment Summary
         *
         * @param propertyDTO 资产
         * @param in          报告文件流
         * @param source      报告来源
         * @param dateRange   数据日期范围（yyyyMMdd）
         * @return List<DbPmsHfBO>
         * @author zc
         * @date 2026/3/6
         */
        @Override
        public List<DbPmsHfBO> parseMarketSegmentSummaryReport(PropertyDTO propertyDTO, InputStream in, String source, List<Long> dateRange) {
            Collection<IDbPmsMssParseService> parserList = applicationContext.getBeansOfType(IDbPmsMssParseService.class).values();
            for (IDbPmsMssParseService parser : parserList) {
                if (parser.supports(source)) {
                    return parser.parse(propertyDTO, in, dateRange);
                }
            }
            throw new ListenerException(MessageFormat.format("Not found report parser, Report Type: Market Segment Summary, " +
                    "Source: {0}", source));
        }

        /**
         * 获取数据的时间范围
         *
         * @param businessDate 营业日期（yyyyMMdd）
         * @param type         类型
         * @return 时间范围
         * @author zc
         * @date 2025/12/02
         */
        @Override
        public List<Long> getDateRange(long businessDate, int type) {
            LocalDate localDate = DateTimeUtils.strToLocalDate(String.valueOf(businessDate),
                    BaseConstant.DATE_FORMAT_YYYYMMDD);
            List<Long> dateRange = Arrays.stream(DbPmsReportDataRangeEnum.getDateRange(type)
                    .split(",")).map(Long::parseLong).toList();

            return List.of(Long.parseLong(DateTimeUtils.localDateToStr(localDate.plusDays(dateRange.get(0)),
                            BaseConstant.DATE_FORMAT_YYYYMMDD)),
                    Long.parseLong(DateTimeUtils.localDateToStr(localDate.plusDays(dateRange.get(1)),
                            BaseConstant.DATE_FORMAT_YYYYMMDD)));
        }

        /**
         * 发送报告事件
         *
         * @param propertyDTO 资产
         * @param file        文件
         * @param importLog   导入日志
         * @param operatorId  操作人ID
         * @param s3FilePath  S3文件路径
         * @param subject     主题
         * @author zc
         * @date 2026/3/12
         */
        @Override
        public void sendReportEvent(PropertyDTO propertyDTO, MultipartFile file, DbReportImportLog importLog, int operatorId,
                                    String s3FilePath, String subject) {
            // 组装 S3 文件路径
            String key = propertyDTO.getTenantSymbol() + "/" + propertyDTO.getAbbr() + "/" + s3FilePath
                    + UploadUtils.getRandomFileName(file.getOriginalFilename());

            // 封装事件体
            HashMap<String, Object> parameter = Maps.newHashMap();
            parameter.put(EventBodyParamEnum.TENANT_SYMBOL.getDesc(), propertyDTO.getTenantSymbol());
            parameter.put(EventBodyParamEnum.PROPERTY_ABBR.getDesc(), List.of(propertyDTO.getAbbr()));
            parameter.put(EventBodyParamEnum.ORIGINATE_FROM.getDesc(), BusinessEnum.DATABASE.getBusiness());
            parameter.put(EventBodyParamEnum.SOURCE.getDesc(), propertyDTO.getPmsType());
            parameter.put(EventBodyParamEnum.UID.getDesc(), operatorId);
            parameter.put(EventBodyParamEnum.ID.getDesc(), importLog.getId());
            parameter.put(EventBodyParamEnum.BUSINESS_DATE.getDesc(), importLog.getBusinessDate());
            parameter.put(EventBodyParamEnum.START_DATE.getDesc(), importLog.getStartDate());
            parameter.put(EventBodyParamEnum.END_DATE.getDesc(), importLog.getEndDate());

            try {
                // 上传报告到 S3 服务器，用于事件监听解析报告
                amazonS3Service.uploadFile(file, key);

                // 发送报告事件
                snsSender.publish(subject, JsonUtils.convertObjToString(objectMapper, new EventBodyDTO(key, parameter)));
            } catch (Exception e) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        MessageFormat.format("Send report event exception:parameter={0}, exception={1}",
                                parameter, e));
            }
        }

    }