# HKEfficiencyReportController.java

    package cloud.wconnect.workperformance.controller.hkefficiency;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.*;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.exception.InternalServerErrorException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.group.Addition;
    import cloud.wconnect.commonlib.pojo.dto.group.Edition;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.workperformance.cons.hkefficiency.HkEfficiencyI18nConstant;
    import cloud.wconnect.workperformance.entity.hkefficiency.HkEfficiencyImportLog;
    import cloud.wconnect.workperformance.entity.hkefficiency.HkEfficiencyReportFilterConfig;
    import cloud.wconnect.workperformance.pojo.dto.hkefficiency.HkEfficiencyImportLogDTO;
    import cloud.wconnect.workperformance.pojo.dto.hkefficiency.HkEfficiencyImportReportDTO;
    import cloud.wconnect.workperformance.pojo.query.hkefficiency.HkEfficiencyImportLogQuery;
    import cloud.wconnect.workperformance.pojo.query.hkefficiency.HkEfficiencyListQuery;
    import cloud.wconnect.workperformance.service.hkefficiency.IHkEfficiencyImportLogService;
    import cloud.wconnect.workperformance.service.hkefficiency.IHkEfficiencyReportFilterConfigService;
    import cloud.wconnect.workperformance.service.hkefficiency.IHkEfficiencyService;
    import cloud.wconnect.workperformance.service.hkefficiency.hkefficiency.IHkEfficiencyParseService;
    import cloud.wconnect.workperformance.service.hkefficiency.operatingstatistics.IHkOperatingStatisticsParseService;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.HttpServletResponse;
    import jakarta.validation.Valid;
    import org.jxls.transform.poi.JxlsPoiTemplateFillerBuilder;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;

    import java.io.IOException;
    import java.io.InputStream;
    import java.text.MessageFormat;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 控制器类：Housekeeping Efficiency report
     * </p>
     *
     * @author zc
     * @date 2024/02/19
     */
    @RestController
    @RequestMapping("/housekeeping-efficiency/report")
    public class HKEfficiencyReportController {
        @Resource
        private IHkEfficiencyService housekeepingEfficiencyService;
        @Resource
        private IHkEfficiencyImportLogService importLogService;
        @Resource
        private IHkEfficiencyReportFilterConfigService hkReportFilterConfigService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private ApplicationContext applicationContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 导出报告
         *
         * @param property 资产标识
         * @param query    查询条件
         * @author zc
         * @date 2023/3/29
         */
        @ApiId(210)
        @GetMapping
        public void exportFile(@RequestParam String property, @Valid HkEfficiencyListQuery query, HttpServletResponse response) {
            try (InputStream is = new ClassPathResource("tmpl-xls/housekeeping_tmpl_" + LocaleContextHolder.getLocale() + ".xlsx").getInputStream()) {
                int propertyId = sessionContext.getPropertyId(property);
                PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
                HashMap<String, Object> data = housekeepingEfficiencyService.exportFile(propertyDTO, query);

                String fileName = "HkEfficiency.xlsx";
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
                response.setHeader("Content-Disposition", String.join("", "attachment; filename=", fileName));

                JxlsPoiTemplateFillerBuilder.newInstance()
                        .withTemplate(is)
                        .buildAndFill(data, response::getOutputStream);
            } catch (IOException exp) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "export Housekeeping Efficiency failed;exp:" + exp);
            }
        }

        /**
         * 导入报告
         *
         * @param importReportDTO 导入报告 DTO
         * @return empty map
         * @author zc
         * @date 2024/2/19
         */
        @ApiId(211)
        @PostMapping
        public Map<Object, Object> importFile(@Validated(Addition.class) HkEfficiencyImportReportDTO importReportDTO) {
            // 校验日期范围
            dateParamCheck(importReportDTO.getStartDate(), importReportDTO.getEndDate());
            // 校验是否有权限处理资产数据
            List<Integer> propertyIds = importReportDTO.getPropertyIds();
            if (!Sets.newHashSet(sessionContext.getPropertyIds()).containsAll(propertyIds)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "Cannot be operated others property data; propertyIds: " + propertyIds);
            }
            List<PropertyDTO> propertyDTOList = propertyI18nRedisService.list(propertyIds);

            if (HkEfficiencyImportReportDTO.HK_REPORT == importReportDTO.getReportType()) {
                // 获取文件过滤配置数据，没有则不能导入报告
                List<HkEfficiencyReportFilterConfig> configs = hkReportFilterConfigService.list(propertyIds);
                if (configs.isEmpty()) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(HkEfficiencyI18nConstant.REPORT_PARSE_CONFIG),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The Housekeeping Efficiency report filter config is empty.");
                }
                importHkReport(propertyDTOList, importReportDTO);
            } else {
                importOperatingReport(propertyDTOList, importReportDTO);
            }
            return Collections.emptyMap();
        }

        /**
         * 导入 Operating 报告
         *
         * @param propertyDTOList 资产集合
         * @param importReportDTO 导入报告 DTO
         * @author zc
         * @date 2024/8/20
         */
        private void importOperatingReport(List<PropertyDTO> propertyDTOList, HkEfficiencyImportReportDTO importReportDTO) {
            // 转换来源标识
            String source = HkEfficiencyImportReportDTO.TYPE_M3 == importReportDTO.getType() ?
                    SystemPlatformEnum.M3.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IHkOperatingStatisticsParseService parser = getHKOperatingStatisticsParser(source);

            // 校验文件是否满足需求
            parser.checkFile(propertyDTOList, importReportDTO.getFile());

            // 组装资产数据范围
            Map<Integer, List<Long>> propertyId2DateRange = propertyDTOList.stream()
                    .collect(Collectors.toMap(PropertyDTO::getId, propertyDTO ->
                            Lists.newArrayList(importReportDTO.getStartDate(), importReportDTO.getEndDate())));

            // 插入进行中导入记录
            List<Long> importLogIds = importLogService.addBatch(propertyDTOList, propertyId2DateRange,
                    HkEfficiencyImportReportDTO.OPERATING_REPORT, ReportImportResultCons.IMPORT_IN_PROGRESS,
                    sessionContext.getLoginUserId(), null, new HashMap<>());

            // 处理数据
            parser.handleData(propertyDTOList, importReportDTO.getFile(), propertyId2DateRange,
                    importLogIds, sessionContext.getLoginUserId());
        }

        /**
         * 导入 HK 报告
         *
         * @param propertyDTOList 资产集合
         * @param importReportDTO 导入报告 DTO
         * @author zc
         * @date 2024/8/20
         */
        private void importHkReport(List<PropertyDTO> propertyDTOList, HkEfficiencyImportReportDTO importReportDTO) {
            // 转换来源标识
            String source = HkEfficiencyImportReportDTO.TYPE_ADP == importReportDTO.getType() ?
                    SystemPlatformEnum.ADP.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IHkEfficiencyParseService parser = getHKEfficiencyParser(source);

            // 校验文件是否满足需求
            parser.checkFile(propertyDTOList, importReportDTO.getFile());

            // 组装资产数据范围
            Map<Integer, List<Long>> propertyId2DateRange = propertyDTOList.stream()
                    .collect(Collectors.toMap(PropertyDTO::getId, propertyDTO ->
                            Lists.newArrayList(importReportDTO.getStartDate(), importReportDTO.getEndDate())));

            // 插入进行中导入记录
            List<Long> importLogIds = importLogService.addBatch(propertyDTOList, propertyId2DateRange,
                    HkEfficiencyImportReportDTO.HK_REPORT, ReportImportResultCons.IMPORT_IN_PROGRESS,
                    sessionContext.getLoginUserId(), null, new HashMap<>());

            // 处理数据
            parser.handleData(propertyDTOList, importReportDTO.getFile(), propertyId2DateRange,
                    importLogIds, sessionContext.getLoginUserId());
        }

        /**
         * 忽略指定的报告异常记录
         *
         * @param property 资产标识
         * @param id       id
         * @return empty map
         * @author zc
         * @date 2024/2/19
         */
        @ApiId(898)
        @PutMapping("/exception/{id:\\d+}/ignore")
        public Map<Object, Object> ignore(@RequestParam String property, @PathVariable long id) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验是否存在
            HkEfficiencyImportLog importLog = importLogService.checkExist(id);
            // 校验归属
            importLogService.checkBelong(propertyId, importLog);
            // 校验是否是失败、缺失记录
            checkImportResult(importLog.getImportResult());
            // 忽略失败记录
            importLogService.ignore(id, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 解决导入异常
         *
         * @param property        资产标识
         * @param id              异常 id
         * @param importReportDTO 导入报告 DTO
         * @return Empty Map
         * @author zc
         * @date 2024/8/20
         */
        @ApiId(899)
        @PostMapping("/exception/{id:\\d+}/resolve")
        public Map<Object, Object> resolve(@RequestParam String property, @PathVariable long id,
                                           @Validated(Edition.class) HkEfficiencyImportReportDTO importReportDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            // 校验日志是否存在
            HkEfficiencyImportLog importLog = importLogService.checkExist(id);
            // 校验日志归属
            importLogService.checkBelong(propertyDTO.getId(), importLog);
            // 校验是否是失败、缺失记录
            checkImportResult(importLog.getImportResult());

            if (HkEfficiencyImportReportDTO.HK_REPORT == importLog.getReportType()) {
                // 获取文件过滤配置数据，没有则不能导入报告
                List<HkEfficiencyReportFilterConfig> configs = hkReportFilterConfigService.list(Lists.newArrayList(propertyId));
                if (configs.isEmpty()) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(HkEfficiencyI18nConstant.REPORT_PARSE_CONFIG),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The Housekeeping Efficiency report filter config is empty.");
                }
                resolveHKEfficiencyReport(propertyDTO, importLog, importReportDTO);
            } else {
                resolveHKOperatingStatisticsReport(propertyDTO, importLog, importReportDTO);
            }
            return Collections.emptyMap();
        }

        /**
         * 解决导入异常 for HK Operating Statistics
         *
         * @param propertyDTO     资产
         * @param importLog       异常log
         * @param importReportDTO 导入报告 DTO
         * @author zc
         * @date 2024/8/20
         */
        private void resolveHKOperatingStatisticsReport(PropertyDTO propertyDTO, HkEfficiencyImportLog importLog,
                                                        HkEfficiencyImportReportDTO importReportDTO) {
            // 转换来源标识
            String source = HkEfficiencyImportReportDTO.TYPE_M3 == importReportDTO.getType() ?
                    SystemPlatformEnum.M3.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IHkOperatingStatisticsParseService parser = getHKOperatingStatisticsParser(source);

            List<PropertyDTO> propertyDTOList = Lists.newArrayList(propertyDTO);
            // 校验文件是否满足需求
            parser.checkFile(propertyDTOList, importReportDTO.getFile());

            // 更新日志为修复中
            importLogService.updateById(HkEfficiencyImportLog.builder()
                    .id(importLog.getId()).importResult(ReportImportResultCons.IMPORT_RESULT_REPAIRING).build());

            long startDate = Long.parseLong(DateTimeUtils.localDateToStr(DateTimeUtils.longToLocalDate(importLog.getStartDate(),
                    propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD));
            long endDate = Long.parseLong(DateTimeUtils.localDateToStr(DateTimeUtils.longToLocalDate(importLog.getEndDate(),
                    propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD));
            // 组装资产数据范围
            Map<Integer, List<Long>> propertyId2DateRange = propertyDTOList.stream()
                    .collect(Collectors.toMap(PropertyDTO::getId, item -> Lists.newArrayList(startDate, endDate)));

            // 处理数据
            parser.handleData(propertyDTOList, importReportDTO.getFile(), propertyId2DateRange,
                    Lists.newArrayList(importLog.getId()), sessionContext.getLoginUserId());
        }

        /**
         * 解决导入异常 for HK Efficiency
         *
         * @param propertyDTO     资产
         * @param importLog       异常log
         * @param importReportDTO 导入报告 DTO
         * @author zc
         * @date 2024/8/20
         */
        private void resolveHKEfficiencyReport(PropertyDTO propertyDTO, HkEfficiencyImportLog importLog,
                                               HkEfficiencyImportReportDTO importReportDTO) {
            // 转换来源标识
            String source = HkEfficiencyImportReportDTO.TYPE_ADP == importReportDTO.getType() ?
                    SystemPlatformEnum.ADP.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IHkEfficiencyParseService parser = getHKEfficiencyParser(source);

            List<PropertyDTO> propertyDTOList = Lists.newArrayList(propertyDTO);
            // 校验文件后缀、内容是否满足需求
            parser.checkFile(propertyDTOList, importReportDTO.getFile());

            // 更新日志为修复中
            importLogService.updateById(HkEfficiencyImportLog.builder()
                    .id(importLog.getId()).importResult(ReportImportResultCons.IMPORT_RESULT_REPAIRING).build());

            long startDate = Long.parseLong(DateTimeUtils.localDateToStr(DateTimeUtils.longToLocalDate(importLog.getStartDate(),
                    propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD));
            long endDate = Long.parseLong(DateTimeUtils.localDateToStr(DateTimeUtils.longToLocalDate(importLog.getEndDate(),
                    propertyDTO.getTimezone()), BaseConstant.DATE_FORMAT_YYYYMMDD));
            // 组装资产数据范围
            Map<Integer, List<Long>> propertyId2DateRange = propertyDTOList.stream()
                    .collect(Collectors.toMap(PropertyDTO::getId, item -> Lists.newArrayList(startDate, endDate)));

            // 处理数据
            parser.handleData(propertyDTOList, importReportDTO.getFile(), propertyId2DateRange,
                    Lists.newArrayList(importLog.getId()), sessionContext.getLoginUserId());
        }

        /**
         * 查询报告导入记录
         *
         * @param property       资产标识
         * @param importLogQuery 查询条件
         * @return HousekeepingEfficiencyImportLogDTO 集合
         * @author zc
         * @date 2023/3/31
         */
        @ApiId(235)
        @GetMapping("/import-log")
        public ListPagedDTO<HkEfficiencyImportLogDTO> listImportLog(@RequestParam String property,
                                                                    @Valid HkEfficiencyImportLogQuery importLogQuery) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            return importLogService.list(propertyDTO, importLogQuery);
        }

        /**
         * 获取导入报告文件的过滤配置信息
         *
         * @param property 资产标识
         * @return 配置信息
         * @author zc
         * @date 2023/3/31
         */
        @ApiId(236)
        @GetMapping("/filter-config")
        public List<String> getReportFilterConfig(@RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);
            return hkReportFilterConfigService.list(Lists.newArrayList(propertyId)).stream()
                    .map(HkEfficiencyReportFilterConfig::getDepartmentCode).collect(Collectors.toList());
        }

        /**
         * 保存导入报告文件的过滤配置信息
         *
         * @param property           资产标识
         * @param departmentCodeList 部门 code
         * @return empty map
         * @author zc
         * @date 2025/5/16
         */
        @ApiId(237)
        @PostMapping("/filter-config")
        public Map<Object, Object> saveReportFilterConfig(@RequestParam String property, @RequestBody List<String> departmentCodeList) {
            int propertyId = sessionContext.getPropertyId(property);
            hkReportFilterConfigService.save(propertyId, departmentCodeList, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 校验是否是失败、缺失记录
         *
         * @param importResult 导入结果
         * @author zc
         * @date 2024/6/13
         */
        private void checkImportResult(int importResult) {
            if (importResult != ReportImportResultCons.IMPORT_RESULT_FAIL
                    && importResult != ReportImportResultCons.IMPORT_RESULT_MISSED) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_ONLY_HANDLE_EXCEPTION,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Cannot operate status is not importResultFail or importResultMissed log");
            }
        }

        /**
         * 校验日期查询条件
         *
         * @param startDate 开始时间
         * @param endDate   结束时间
         * @author zc
         * @date 2024/2/29
         */
        private void dateParamCheck(long startDate, long endDate) {
            if (startDate > endDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The startDate cannot be greater than endDate; startDate:{0}, endDate:{1}",
                                startDate, endDate));
            }
        }

        /**
         * 获取报告解析服务 for HK operating statistics
         *
         * @param source 来源标识
         * @return 报告解析服务
         * @author zc
         * @date 2024/8/20
         */
        private IHkOperatingStatisticsParseService getHKOperatingStatisticsParser(String source) {
            Collection<IHkOperatingStatisticsParseService> parserList =
                    applicationContext.getBeansOfType(IHkOperatingStatisticsParseService.class).values();
            IHkOperatingStatisticsParseService parser = null;
            for (IHkOperatingStatisticsParseService parseService : parserList) {
                if (parseService.supports(source)) {
                    parser = parseService;
                    break;
                }
            }
            if (Objects.isNull(parser)) {
                throw new BadRequestException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Can not find HK Operating Statistics report parser");
            }
            return parser;
        }

        /**
         * 获取报告解析服务 for HK efficiency
         *
         * @param source 来源标识
         * @return 报告解析服务
         * @author zc
         * @date 2024/8/20
         */
        private IHkEfficiencyParseService getHKEfficiencyParser(String source) {
            Collection<IHkEfficiencyParseService> parserList =
                    applicationContext.getBeansOfType(IHkEfficiencyParseService.class).values();
            IHkEfficiencyParseService parser = null;
            for (IHkEfficiencyParseService parseService : parserList) {
                if (parseService.supports(source)) {
                    parser = parseService;
                    break;
                }
            }
            if (Objects.isNull(parser)) {
                throw new BadRequestException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Can not find HK Efficiency report parser");
            }
            return parser;
        }
    }