# RevenueImportController.java

    package cloud.wconnect.rateshop.controller.revenue;

    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.ParseReportException;
    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.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.commonlib.util.UploadUtils;
    import cloud.wconnect.rateshop.cons.revenue.ReveneuErrorCons;
    import cloud.wconnect.rateshop.cons.revenue.RevenueImportCons;
    import cloud.wconnect.rateshop.entity.revenue.RevenueImportLog;
    import cloud.wconnect.rateshop.pojo.dto.revenue.ImportLogDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.ResolveExceptionDTO;
    import cloud.wconnect.rateshop.pojo.query.revenue.ImportLogQuery;
    import cloud.wconnect.rateshop.service.revenue.IRevenueBudgetOtbService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueImportLogService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueImportService;
    import cloud.wconnect.rateshop.service.revenue.report.IImportRevenueReport;
    import com.google.common.collect.Lists;
    import jakarta.validation.Valid;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.ss.usermodel.WorkbookFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;

    import java.io.InputStream;
    import java.util.*;

    /**
     * 控制层：Revenue Import
     *
     * @author yyt
     * 2023/10/30
     */
    @RestController
    @RequestMapping("/revenue")
    public class RevenueImportController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IRevenueBudgetOtbService revenueBudgetOtbService;
        @Autowired
        private IRevenueImportLogService revenueImportLogService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private IRevenueImportService revenueImportService;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 导入 budget
         *
         * @param year     选择年
         * @param property 资产标识
         * @param file     文件
         * @return {}
         * @author yyt
         * @date 2023/10/28
         */
        @ApiId(871)
        @PostMapping("/{year:\\d+}/budget")
        public Map importBudget(@PathVariable int year, String property, MultipartFile file) {
            // 校验上传文件不能为空
            if (file.isEmpty()) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.FILE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The report file can not be empty");
            }

            // 校验文件格式
            UploadUtils.checkFileFormat(file, Lists.newArrayList("xls", "xlsx"));

            try (InputStream in = file.getInputStream();
                 Workbook workbook = WorkbookFactory.create(in)) {
                revenueBudgetOtbService.importBudgetFile(propertyI18nRedisService.get(sessionContext.getPropertyId(property)), year, workbook,
                        sessionContext.getLoginUserId());
            } catch (Exception e) {
                throw new ParseReportException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Exception occured when importing budget file", e);
            }

            return Collections.emptyMap();
        }

        /**
         * 导入记录
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return 导入记录
         * @author yyt
         * @date 2023/10/17
         */
        @ApiId(872)
        @GetMapping("/import-log")
        public ListPagedDTO<ImportLogDTO> listImportLog(String property, @Valid ImportLogQuery query) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            checkFileType(propertyDTO.getPmsType(), query.getReportTypes());
            return revenueImportLogService.listImportLog(propertyDTO, query);
        }

        /**
         * 检查报告类型
         *
         * @param pmsType     平台类型
         * @param reportTypes 报告类型
         * @author yyt
         * @date 2025/8/2
         **/
        private void checkFileType(String pmsType, List<Integer> reportTypes) {
            if (pmsType.equals(SystemPlatformEnum.OPERA.getDesc())) {
                // 检查选择报告属于当前平台下支持的报告类型
                if (!new HashSet<>(RevenueImportCons.OPERA_NOTIFY_TYPES).containsAll(reportTypes)) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the fileType is not support");
                }
            } else {
                // 检查选择报告属于当前平台下支持的报告类型
                if (!new HashSet<>(RevenueImportCons.SHI_JI_NOTIFY_TYPES).containsAll(reportTypes)) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the fileType is not support");
                }
            }
        }

        /**
         * 解决异常
         *
         * @param id                  异常 id
         * @param property            资产标识
         * @param file                报告文件（选择 opera 时，xml格式；选择 other 时，xls、xlsx 格式）（忽略时不传）
         * @param resolveExceptionDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/10/17
         */
        @ApiId(873)
        @PutMapping("/{id:\\d+}/resolve")
        public Map resolveException(@PathVariable long id, String property, MultipartFile file,
                                    ResolveExceptionDTO resolveExceptionDTO) {
            RevenueImportLog importLog = revenueImportLogService.checkExist(id);
            if (!Lists.newArrayList(ReportImportResultCons.IMPORT_RESULT_FAIL, ReportImportResultCons.IMPORT_RESULT_MISSED)
                    .contains(importLog.getStatus())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_ONLY_HANDLE_EXCEPTION,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the importLog status is error");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            checkBelong(importLog, propertyDTO.getId());

            int userId = sessionContext.getLoginUserId();
            if (Objects.isNull(file)) {
                revenueImportLogService.updateLogStatus(id, ReportImportResultCons.IMPORT_RESULT_IGNORE, userId);
            } else {
                if (Objects.isNull(resolveExceptionDTO.getType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the type must not be null");
                }

                // 系统自动导入
                if (importLog.getCreatedBy().equals(BaseConstant.ZERO)) {
                    if (!revenueImportService.checkHandleDate(propertyDTO, importLog.getReportType(), importLog.getTheDate())) {
                        throw new BadRequestException(ReveneuErrorCons.SELECT_DATE_NOT_SUPPORT_IMPORT_REPORT,
                                BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Date selection is not supported for import");
                    }
                } else {
                    // 手动导入
                    if (!revenueImportService.checkHandleDateForImport(propertyDTO, importLog.getReportType(), importLog.getTheDate())) {
                        throw new BadRequestException(ReveneuErrorCons.SELECT_DATE_NOT_SUPPORT_IMPORT_REPORT,
                                BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Date selection is not supported for import");
                    }
                }

                resolveExceptionDTO.setFileType(importLog.getReportType());
                checkSystemPlatformAndFileType(propertyDTO, resolveExceptionDTO);

                Map<String, IImportRevenueReport> beans = applicationContext.getBeansOfType(IImportRevenueReport.class);
                for (IImportRevenueReport bean : beans.values()) {
                    if (bean.supports(resolveExceptionDTO.getType(), importLog.getReportType())) {
                        bean.checkFile(propertyDTO, file);

                        importLog = bean.handleImportLog(propertyDTO, importLog, resolveExceptionDTO, file, userId);

                        bean.handleFile(propertyDTO, file, importLog, userId);
                        return Collections.emptyMap();
                    }
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 手动导入报告
         *
         * @param property            资产标识
         * @param file                报告
         * @param resolveExceptionDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/10/31
         */
        @ApiId(874)
        @PostMapping("/import")
        public Map importFileByManual(String property, MultipartFile file, @Validated(Addition.class) ResolveExceptionDTO resolveExceptionDTO) {
            if (Objects.isNull(file)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.FILE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the file must not be null");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            int userId = sessionContext.getLoginUserId();

            if (!revenueImportService.checkHandleDateForImport(propertyDTO, resolveExceptionDTO.getFileType(), resolveExceptionDTO.getDate())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INVALID,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.DATE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Date selection is not supported for import");
            }

            checkSystemPlatformAndFileType(propertyDTO, resolveExceptionDTO);

            Map<String, IImportRevenueReport> beans = applicationContext.getBeansOfType(IImportRevenueReport.class);
            for (IImportRevenueReport bean : beans.values()) {
                if (bean.supports(resolveExceptionDTO.getType(), resolveExceptionDTO.getFileType())) {
                    bean.checkFile(propertyDTO, file);

                    //校验日期
                    bean.checkDate(propertyDTO, resolveExceptionDTO);

                    RevenueImportLog importLog = bean.handleImportLog(propertyDTO, null, resolveExceptionDTO, file, userId);

                    bean.handleFile(propertyDTO, file, importLog, userId);
                    return Collections.emptyMap();
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 检查系统平台与文件类型
         *
         * @param propertyDTO         资产
         * @param resolveExceptionDTO 选择条件
         * @author yyt
         * @date 2025/7/4
         **/
        private void checkSystemPlatformAndFileType(PropertyDTO propertyDTO, ResolveExceptionDTO resolveExceptionDTO) {
            String pmsType = propertyDTO.getPmsType();
            if (!Lists.newArrayList(pmsType, SystemPlatformEnum.OTHER.getDesc()).contains(resolveExceptionDTO.getType())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_SOURCE_SYSTEM_NOT_SUPPORTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the pmsType is not support");
            }

            if (resolveExceptionDTO.getType().equals(SystemPlatformEnum.OPERA.getDesc())) {
                // 检查选择报告属于当前平台下支持的报告类型
                if (!RevenueImportCons.OPERA_NOTIFY_TYPES.contains(resolveExceptionDTO.getFileType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the fileType is not support");
                }
            } else if (resolveExceptionDTO.getType().equals(SystemPlatformEnum.SHI_JI.getDesc())) {
                // 检查选择报告属于当前平台下支持的报告类型
                if (!RevenueImportCons.SHI_JI_NOTIFY_TYPES.contains(resolveExceptionDTO.getFileType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the fileType is not support");
                }
            }
        }

        /**
         * 校验数据归属
         *
         * @param importLog  import Log
         * @param propertyId 资产 id
         * @author yyt
         * @date 2023/10/30
         */
        private void checkBelong(RevenueImportLog importLog, Integer propertyId) {
            if (!importLog.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the revenueImportLog;propertyId:" + propertyId + ",revenueImportLogPropertyId:" +
                        importLog.getPropertyId() + ",revenueImportLogId:" + importLog.getId());
            }
        }

    }