# AcApiController.java

    package cloud.wconnect.finance.controller.api;

    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.jsonview.Views;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.finance.cons.api.ApiErrorI18nConstant;
    import cloud.wconnect.finance.cons.api.ApiI18nConstant;
    import cloud.wconnect.finance.cons.api.ApiStatusEnum;
    import cloud.wconnect.finance.entity.api.AcApi;
    import cloud.wconnect.finance.entity.api.AcApiComment;
    import cloud.wconnect.finance.pojo.dto.api.*;
    import cloud.wconnect.finance.pojo.query.api.AcOtaQuery;
    import cloud.wconnect.finance.pojo.query.api.ApiBatchNoQuery;
    import cloud.wconnect.finance.pojo.query.api.ApiCommissionQuery;
    import cloud.wconnect.finance.pojo.vo.api.AcExportVO;
    import cloud.wconnect.finance.service.api.IAcApiCommentService;
    import cloud.wconnect.finance.service.api.IAcApiService;
    import cloud.wconnect.finance.service.api.IAcOtaService;
    import cloud.wconnect.finance.service.api.acota.IApiOtaParseService;
    import cloud.wconnect.finance.service.api.api.IApiParseService;
    import com.fasterxml.jackson.annotation.JsonView;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.HttpServletResponse;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    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.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;

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

    /**
     * <p>
     * api 报告数据 前端控制器
     * </p>
     *
     * @author zc
     * @since 2023-08-04
     */
    @RestController
    @RequestMapping("/api-commission")
    public class AcApiController {
        @Resource
        private IAcApiService acApiService;
        @Resource
        private IAcOtaService acOtaService;
        @Resource
        private IAcApiCommentService acApiCommentService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private ApplicationContext applicationContext;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 Batch no
         *
         * @param property        资产标识
         * @param apiBatchNoQuery 查询条件
         * @return Batch no 集合
         * @author zc
         * @date 2023/8/4
         */
        @ApiId(513)
        @GetMapping("/batch-no")
        public ListPagedDTO<String> listBatchNo(@RequestParam String property, ApiBatchNoQuery apiBatchNoQuery) {
            if (Objects.isNull(apiBatchNoQuery.getPage()) && Objects.isNull(apiBatchNoQuery.getSize())) {
                if (StringUtils.isBlank(apiBatchNoQuery.getBatchNo())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(ApiI18nConstant.BATCH_NO),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The batchNo cannot be blank");
                }
            }

            int propertyId = sessionContext.getPropertyId(property);

            return acApiService.listBatchNo(propertyId, apiBatchNoQuery);
        }

        /**
         * 获取 Api commission 列表数据
         *
         * @param property           资产标识
         * @param apiCommissionQuery 查询条件
         * @return AcApiDTO 集合
         * @author zc
         * @date 2023/8/4
         */
        @ApiId(514)
        @GetMapping
        public ListPagedDTO<AcApiDTO> list(@RequestParam String property, @Valid ApiCommissionQuery apiCommissionQuery) {
            if (Objects.nonNull(apiCommissionQuery.getFromDate())
                    && Objects.nonNull(apiCommissionQuery.getToDate())) {
                // 校验起止时间参数
                checkDateParam(apiCommissionQuery.getFromDate(), apiCommissionQuery.getToDate());
            }
            if (CollectionUtils.isNotEmpty(apiCommissionQuery.getStatus())) {
                // 校验 status 参数
                checkStatusParam(apiCommissionQuery.getStatus());
            }
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            return acApiService.list(tenantId, propertyId, apiCommissionQuery);
        }

        /**
         * 获取 Api 数据各状态统计
         *
         * @param property           资产标识
         * @param apiCommissionQuery 查询条件
         * @return AcApiStatusTotalDTO
         * @author zc
         * @date 2023/8/4
         */
        @ApiId(515)
        @GetMapping("/status-total")
        public AcApiStatusTotalDTO getApiStatusTotal(@RequestParam String property, ApiCommissionQuery apiCommissionQuery) {
            if (Objects.nonNull(apiCommissionQuery.getFromDate())
                    && Objects.nonNull(apiCommissionQuery.getToDate())) {
                // 校验起止时间参数
                checkDateParam(apiCommissionQuery.getFromDate(), apiCommissionQuery.getToDate());
            }
            int propertyId = sessionContext.getPropertyId(property);

            return acApiService.getApiStatusTotal(propertyId, apiCommissionQuery);
        }

        /**
         * 获取数据详情
         *
         * @param id       api id
         * @param property 资产标识
         * @return ApiDataDTO
         * @author zc
         * @date 2023/8/4
         */
        @ApiId(516)
        @GetMapping("/{id:\\d+}")
        @JsonView(Views.DetailsView.class)
        public ApiDataDTO getDetail(@PathVariable long id, @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            return acApiService.getDetail(propertyId, acApi);
        }

        /**
         * 获取未匹配 Api 的 OTA 数据
         *
         * @param property   资产标识
         * @param acOtaQuery 查询条件
         * @return AcOtaDTO 集合
         * @author zc
         * @date 2023/8/5
         */
        @ApiId(517)
        @GetMapping("/ota")
        public ListPagedDTO<AcOtaDTO> listOta(@RequestParam String property, @Valid AcOtaQuery acOtaQuery) {
            if (Objects.nonNull(acOtaQuery.getFromDate())
                    && Objects.nonNull(acOtaQuery.getToDate())) {
                // 校验起止时间参数
                checkDateParam(acOtaQuery.getFromDate(), acOtaQuery.getToDate());
            }
            int propertyId = sessionContext.getPropertyId(property);

            return acOtaService.listUnbind(propertyId, acOtaQuery);
        }

        /**
         * 更新 API 数据状态
         *
         * @param id              api id
         * @param property        资产标识
         * @param updateStatusDTO 更新内容
         * @return empty map
         * @author zc
         * @date 2023/8/5
         */
        @ApiId(518)
        @PutMapping("/{id:\\d+}/status")
        public Map<Object, Object> updateStatus(@PathVariable long id, @RequestParam String property,
                                                @RequestBody @Valid ApiUpdateStatusDTO updateStatusDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            if (updateStatusDTO.getStatus().equals(ApiStatusEnum.STATUS_NO_RECORD_IN_PMS.getCode())) {
                updateStatusNoRecordInPms(acApi, updateStatusDTO);
            } else if (updateStatusDTO.getStatus().equals(ApiStatusEnum.STATUS_MATCHED.getCode())) {
                updateStatusMatched(acApi, updateStatusDTO);
            } else {
                updateStatusPayClose(acApi, updateStatusDTO);
            }

            return Collections.emptyMap();
        }

        /**
         * 更新状态为：2.No record in PMS
         *
         * @param originApi       原 api 数据
         * @param updateStatusDTO 更新内容
         * @author zc
         * @date 2023/8/5
         */
        private void updateStatusNoRecordInPms(AcApi originApi, ApiUpdateStatusDTO updateStatusDTO) {
            if (!originApi.getStatus().equals(ApiStatusEnum.STATUS_MISSING_IN_PMS.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(ApiStatusEnum.STATUS_MISSING_IN_PMS.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [1:Missing in PMS]; originStatus: " + originApi.getStatus());
            }
            acApiService.updateStatus(updateStatusDTO.getStatus(), sessionContext.getLoginUserId(), originApi);
        }

        /**
         * 更新状态为：3.Matched
         *
         * @param originApi       原 api 数据
         * @param updateStatusDTO 更新内容
         * @author zc
         * @date 2023/8/5
         */
        private void updateStatusMatched(AcApi originApi, ApiUpdateStatusDTO updateStatusDTO) {
            if (!originApi.getStatus().equals(ApiStatusEnum.STATUS_MISSING_IN_PMS.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(ApiStatusEnum.STATUS_MISSING_IN_PMS.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [1:Missing in PMS]; originStatus: " + originApi.getStatus());
            }
            if (Objects.isNull(updateStatusDTO.getOtaId())) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("otaId"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The otaId cannot be null");
            }
            acApiService.updateStatusMatched(updateStatusDTO, sessionContext.getLoginUserId(), originApi);
        }

        /**
         * 更新状态为：4.Pay & Close
         *
         * @param originApi       原 api 数据
         * @param updateStatusDTO 更新内容
         * @author zc
         * @date 2023/8/5
         */
        private void updateStatusPayClose(AcApi originApi, ApiUpdateStatusDTO updateStatusDTO) {
            if (!originApi.getStatus().equals(ApiStatusEnum.STATUS_MATCHED.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(ApiStatusEnum.STATUS_MATCHED.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [3:Matched]; originStatus: " + originApi.getStatus());
            }
            acApiService.updateStatus(updateStatusDTO.getStatus(), sessionContext.getLoginUserId(), originApi);
        }

        /**
         * 批量更新数据状态为 Pay & Close
         *
         * @param property             资产标识
         * @param updateStatusBatchDTO 更新内容
         * @return empty map
         * @author zc
         * @date 2023/8/5
         */
        @ApiId(519)
        @PutMapping("/status/matched-to-closed")
        public Map<Object, Object> updateStatusPayCloseBatch(@RequestParam String property,
                                                             @RequestBody @Valid ApiUpdateStatusBatchDTO updateStatusBatchDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            if (CollectionUtils.isNotEmpty(updateStatusBatchDTO.getIds())) {
                acApiService.updateStatusPayCloseBatchByIds(propertyId, sessionContext.getLoginUserId(), updateStatusBatchDTO.getIds());
            } else if (Objects.nonNull(updateStatusBatchDTO.getParam())) {
                // 校验 Batch no 是否存在
                acApiService.checkBatchNoExist(propertyId, updateStatusBatchDTO.getParam().getBatchNo());
                // 校验 Batch no 是否有效
                boolean isEffective = acApiService.checkInUseForBatchNo(propertyId, updateStatusBatchDTO.getParam().getBatchNo());
                if (!isEffective) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INVALID,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(ApiI18nConstant.BATCH_NO),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The batch no is invalid");
                }
                acApiService.updateStatusPayCloseBatchByParam(propertyId, sessionContext.getLoginUserId(), updateStatusBatchDTO.getParam());
            }
            return Collections.emptyMap();
        }

        /**
         * 回滚数据状态
         *
         * @param id       API id
         * @param property 资产标识
         * @return empty map
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(520)
        @PutMapping("/{id:\\d+}/undo")
        public Map<Object, Object> undo(@PathVariable long id, @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            if (Lists.newArrayList(ApiStatusEnum.STATUS_NO_RECORD_IN_PMS.getCode(),
                    ApiStatusEnum.STATUS_PAY_CLOSE.getCode()).contains(acApi.getStatus())) {
                acApiService.undo(acApi);
            } else if (acApi.getStatus().equals(ApiStatusEnum.STATUS_MATCHED.getCode())) {
                if (acApi.getStatusUpdatedBy().equals(BaseConstant.ZERO)) {
                    throw new BadRequestException(ApiErrorI18nConstant.CANNOT_UNDO_SYSTEM_MATCHED_DATA,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "Cannot undo system automatically matched data.");
                }
                acApiService.undoMatched(acApi);
            } else {
                throw new BadRequestException(ApiErrorI18nConstant.CANNOT_UNDO_MISSING_IN_PMS_DATA,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Cannot undo missing in PMS status data.");
            }

            return Collections.emptyMap();
        }

        /**
         * 获取备注集合
         *
         * @param id       API id
         * @param property 资产标识
         * @return AcApiCommentDTO 集合
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(521)
        @GetMapping("/{id:\\d+}/note")
        public List<AcApiCommentDTO> listNotes(@PathVariable long id, @RequestParam String property) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            return acApiCommentService.list(tenantId, propertyId, id);
        }

        /**
         * 新增备注
         *
         * @param id              API id
         * @param property        资产标识
         * @param acApiCommentDTO 评论内容
         * @return id
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(522)
        @PostMapping("/{id:\\d+}/note")
        public Map<String, Long> addNote(@PathVariable long id, @RequestParam String property,
                                         @RequestBody @Valid AcApiCommentDTO acApiCommentDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            AcApiComment acApiComment = new AcApiComment()
                    .setApiId(id)
                    .setPropertyId(propertyId)
                    .setNote(acApiCommentDTO.getNote())
                    .setOperatorId(sessionContext.getLoginUserId())
                    .setCreateTime(DateTimeUtils.nowTimestamp());
            acApiCommentService.save(acApiComment);

            return ImmutableMap.of("id", acApiComment.getId());
        }

        /**
         * 删除备注
         *
         * @param id       API id
         * @param noteId   备注 id
         * @param property 资产标识
         * @return id
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(523)
        @DeleteMapping("/{id:\\d+}/note/{noteId:\\d+}")
        public Map<String, Long> deleteNote(@PathVariable long id, @PathVariable long noteId, @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            AcApi acApi = acApiService.checkExist(id);
            // 校验数据归属
            acApiService.checkBelong(propertyId, acApi);

            // 校验备注是否存在
            AcApiComment acApiComment = acApiCommentService.checkExist(noteId);
            // 校验备注数据归属
            acApiCommentService.checkBelong(propertyId, id, acApiComment);
            // 校验当前用户是否是创建人
            if (!acApiComment.getOperatorId().equals(sessionContext.getLoginUserId())) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "The login user is not creator; loginUserId:" + sessionContext.getLoginUserId()
                                + ", operatorId:" + acApiComment.getOperatorId());
            }

            acApiCommentService.removeById(noteId);

            return Collections.emptyMap();
        }

        /**
         * 根据 Batch # 手动导入 API 报告
         *
         * @param property        资产标识
         * @param importReportDTO 导入报告DTO
         * @return ConflictApiDTO
         * @author zc
         * @date 2024/8/23
         */
        @ApiId(524)
        @PostMapping("/report/api")
        @JsonView(Views.DetailsView.class)
        public ConflictApiDTO importApiReport(@RequestParam String property, @Valid ApiImportReportDTO importReportDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            // 转换来源标识
            String source = ApiImportReportDTO.TYPE_API == importReportDTO.getType() ?
                    SystemPlatformEnum.API.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IApiParseService parser = getApiParser(source);

            // 校验文件内容是否合法
            parser.checkFile(propertyDTO, importReportDTO.getFile());

            // 处理 API 报告数据
            return parser.handleData(propertyDTO, importReportDTO.getFile(), importReportDTO.getBatchNo());
        }

        /**
         * 根据 Batch # 手动导入 OTA 报告
         *
         * @param property        资产标识
         * @param importReportDTO 导入报告DTO
         * @author zc
         * @date 2024/8/23
         */
        @ApiId(542)
        @PostMapping("/report/ota")
        public Map<Object, Object> importOtaReport(@RequestParam String property, @Valid ApiOtaImportReportDTO importReportDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            // 转换来源标识
            String source = ApiOtaImportReportDTO.TYPE_OPERA == importReportDTO.getType() ?
                    SystemPlatformEnum.OPERA.getDesc() : SystemPlatformEnum.OTHER.getDesc();

            IApiOtaParseService parser = getOtaParser(source);

            // 循环校验文件内容的有效性
            for (MultipartFile file : importReportDTO.getFiles()) {
                parser.checkFile(propertyDTO, file);
            }

            // 处理文件数据
            parser.handleData(propertyDTO, importReportDTO.getFiles(), importReportDTO.getBatchNo());

            return Collections.emptyMap();
        }

        /**
         * 根据 Batch # 导出数据为 Excel
         *
         * @param status   状态（1.Missing in PMS 2.No Record in PMS 3.Matched 4.Pay & Close）
         * @param batchNo  Batch no
         * @param property 资产标识
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(525)
        @GetMapping("/report")
        public void exportFile(@RequestParam List<Integer> status, @RequestParam String batchNo,
                               @RequestParam String property, HttpServletResponse response) {
            if (CollectionUtils.isEmpty(status)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The status cannot be empty");
            }
            // 校验 status 参数
            checkStatusParam(status);

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

            // 校验 Batch no 是否存在
            acApiService.checkBatchNoExist(propertyId, batchNo);
            // 校验 Batch no 是否有效
            boolean isEffective = acApiService.checkInUseForBatchNo(propertyId, batchNo);
            if (!isEffective) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INVALID,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(ApiI18nConstant.BATCH_NO),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The batch no is invalid");
            }

            List<AcExportVO> acExportVOList = acApiService.getApiExportList(propertyDTO, batchNo, status);

            String tmplPath = "tmpl-xls/ac_api_tmpl_" + LocaleContextHolder.getLocale() + ".xlsx";
            try (InputStream inputStream = new ClassPathResource(tmplPath).getInputStream()) {
                HashMap<String, Object> data = new HashMap<>();
                data.put("propertyName", propertyDTO.getName());
                data.put("apiSheetList", acExportVOList);

                String fileName = "API Commission.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(inputStream)
                        .buildAndFill(data, response::getOutputStream);
            } catch (IOException e) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Export file failed; exception:" + e);
            }

        }

        /**
         * 根据 Batch # 清除未比对过的 Api 数据
         *
         * @param batchNo  Batch no
         * @param property 资产标识
         * @return empty map
         * @author zc
         * @date 2023/8/7
         */
        @ApiId(526)
        @DeleteMapping("/invalid")
        public Map<Object, Object> deleteInvalidApiByBatchNo(@RequestParam String batchNo, @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验 Batch no 是否存在
            acApiService.checkBatchNoExist(propertyId, batchNo);
            // 校验 Batch no 是否有效，有效说明已比对过数据，不能删除；无效则可以删除
            boolean isEffective = acApiService.checkInUseForBatchNo(propertyId, batchNo);
            if (isEffective) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_IS_IN_USE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(ApiI18nConstant.BATCH_NO),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The batch no is effective, cannot be delete");
            }

            // 清除未比对过的 Api 数据
            acApiService.deleteByBatchNo(propertyId, batchNo);

            return Collections.emptyMap();
        }

        /**
         * 校验起止时间参数
         *
         * @param fromDate 开始时间
         * @param toDate   结束时间
         * @author zc
         * @date 2023/8/5
         */
        private void checkDateParam(long fromDate, long toDate) {
            // 开始时间不能大于结束时间
            if (fromDate > toDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The fromDate cannot be greater than the toDate");
            }
        }

        /**
         * 校验状态参数
         *
         * @param status 状态集合
         * @author zc
         * @date 2023/8/7
         */
        private void checkStatusParam(List<Integer> status) {
            List<Integer> apiStatusList = Lists.newArrayList(ApiStatusEnum.STATUS_MISSING_IN_PMS.getCode(),
                    ApiStatusEnum.STATUS_NO_RECORD_IN_PMS.getCode(),
                    ApiStatusEnum.STATUS_MATCHED.getCode(),
                    ApiStatusEnum.STATUS_PAY_CLOSE.getCode());
            if (status.stream().noneMatch(apiStatusList::contains)) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(ApiStatusEnum.STATUS_MISSING_IN_PMS.getI18nCode(),
                                        ApiStatusEnum.STATUS_NO_RECORD_IN_PMS.getI18nCode(),
                                        ApiStatusEnum.STATUS_MATCHED.getI18nCode(),
                                        ApiStatusEnum.STATUS_PAY_CLOSE.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The status must be in [1.Missing in PMS 2.No Record in PMS 3.Matched 4.Pay & Close]");
            }
        }

        /**
         * 获取报告解析服务 for API
         *
         * @param source 来源标识
         * @return 报告解析服务
         * @author zc
         * @date 2024/8/22
         */
        private IApiParseService getApiParser(String source) {
            Collection<IApiParseService> parserList = applicationContext.getBeansOfType(IApiParseService.class).values();
            IApiParseService parser = null;
            for (IApiParseService 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 API Commission API report parser");
            }
            return parser;
        }

        /**
         * 获取报告解析服务 for OTA
         *
         * @param source 来源标识
         * @return 报告解析服务
         * @author zc
         * @date 2024/8/22
         */
        private IApiOtaParseService getOtaParser(String source) {
            Collection<IApiOtaParseService> parserList = applicationContext.getBeansOfType(IApiOtaParseService.class).values();
            IApiOtaParseService parser = null;
            for (IApiOtaParseService 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 API Commission OTA report parser");
            }
            return parser;
        }
    }