# ScheduleController.java

    package cloud.wconnect.workperformance.controller.schedule;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.ReportImportResultCons;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.bo.SymbolBO;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.group.Edition;
    import cloud.wconnect.commonlib.pojo.dto.jsonview.Views;
    import cloud.wconnect.commonlib.pojo.dto.result.ResultDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.workperformance.cons.schedule.ScheduleErrorFiledCons;
    import cloud.wconnect.workperformance.cons.schedule.ScheduleLimitDayCons;
    import cloud.wconnect.workperformance.entity.schedule.*;
    import cloud.wconnect.workperformance.pojo.dto.schedule.*;
    import cloud.wconnect.workperformance.pojo.group.Addition;
    import cloud.wconnect.workperformance.pojo.query.schedule.ImportLogQuery;
    import cloud.wconnect.workperformance.pojo.query.schedule.ImportScheduleLogQuery;
    import cloud.wconnect.workperformance.pojo.query.schedule.ScheduleQuery;
    import cloud.wconnect.workperformance.service.schedule.*;
    import cloud.wconnect.workperformance.service.schedule.employee.IEmployeeFileParse;
    import cloud.wconnect.workperformance.service.schedule.historyandforecast.IHistoryAndForecastFileParse;
    import cloud.wconnect.workperformance.service.schedule.scheduleinfo.IScheduleFileParse;
    import com.fasterxml.jackson.annotation.JsonView;
    import com.google.common.collect.Lists;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    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.util.*;

    /**
     * <p>
     * 控制层：Schedule
     * </p>
     *
     * @author yyt
     * @since 2023-06-28
     */
    @Validated
    @RestController
    @RequestMapping("/schedule")
    public class ScheduleController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IScheduleHistoryAndForecastService scheduleHistoryAndForecastService;
        @Autowired
        private IScheduleInfoService scheduleInfoService;
        @Autowired
        private IScheduleBeoService scheduleBeoService;
        @Autowired
        private IScheduleWorkDayService scheduleWorkDayService;
        @Autowired
        private IScheduleDepartmentService scheduleDepartmentService;
        @Autowired
        private IScheduleInfoImportLogService scheduleInfoImportLogService;
        @Autowired
        private IScheduleHistoryAndForecastImportLogService scheduleHistoryAndForecastImportLogService;
        @Autowired
        private IScheduleDepartmentEmployeeImportLogService scheduleDepartmentEmployeeImportLogService;
        @Autowired
        private IScheduleDepartmentPermissionConfigService scheduleDepartmentPermissionConfigService;
        @Autowired
        private IScheduleDepartmentEmployeeService scheduleDepartmentEmployeeService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 检查日期
         *
         * @param timezone  时区
         * @param startDate 开始日期
         * @param endDate   结束日期
         * @param limitDays 限制天数
         * @author yyt
         * @date 2023/6/28
         */
        private void checkDate(String timezone, Long startDate, Long endDate, int limitDays) {
            // 开始日期必须小于结束日期
            if (startDate > endDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The end date must be greater than the start date");
            }

            // 限制日期选择天数
            if (DateTimeUtils.dateDiffOfDays(startDate, endDate, timezone) > limitDays - 1) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(limitDays),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Support selection for up to " + limitDays + " days");
            }
        }

        /**
         * 获取 history and forecast 数据
         *
         * @param property      资产标识
         * @param scheduleQuery 查询条件
         * @return history and forecast 数据
         * @author yyt
         * @date 2023/6/28
         */
        @ApiId(386)
        @GetMapping("/history-and-forecast")
        public List<ScheduleHistoryAndForecastDTO> listHistoryAndForecast(String property, ScheduleQuery scheduleQuery) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            checkDate(propertyDTO.getTimezone(), scheduleQuery.getStartDate(), scheduleQuery.getEndDate(), ScheduleLimitDayCons.PAGE_SEARCH);
            return scheduleHistoryAndForecastService.listDTO(propertyId, scheduleQuery);
        }

        /**
         * 获取排班信息
         *
         * @param property      资产标识
         * @param scheduleQuery 查询条件
         * @return 排班信息
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(387)
        @GetMapping
        @JsonView(Views.ListView.class)
        public List<ScheduleDepartmentDTO> listSchedule(String property, ScheduleQuery scheduleQuery) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            checkDate(propertyDTO.getTimezone(), scheduleQuery.getStartDate(), scheduleQuery.getEndDate(), ScheduleLimitDayCons.PAGE_SEARCH);

            return scheduleInfoService.listSchedule(propertyId, scheduleQuery, sessionContext.getLoginUserId());
        }

        /**
         * 获取 beo 信息
         *
         * @param property      资产标识
         * @param scheduleQuery 查询条件
         * @return beo 信息
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(388)
        @GetMapping("/beo")
        public List<ScheduleBeoDTO> listBeo(String property, ScheduleQuery scheduleQuery) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            checkDate(propertyDTO.getTimezone(), scheduleQuery.getStartDate(), scheduleQuery.getEndDate(), ScheduleLimitDayCons.PAGE_SEARCH);

            return scheduleBeoService.listDTO(propertyId, scheduleQuery);
        }

        /**
         * 编辑 history and forecast 数据
         *
         * @param property                  资产标识
         * @param historyAndForecastDTOList history and forecast 数据
         * @return {}
         * @author yyt
         * @date 2023/7/8
         */
        @ApiId(455)
        @PutMapping("/history-and-forecast")
        public Map<String, Object> updateHistoryAndForecast(String property, @RequestBody List<@Valid ScheduleHistoryAndForecastDTO> historyAndForecastDTOList) {
            if (CollectionUtils.isEmpty(historyAndForecastDTOList)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("historyAndForecastDTOList"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the historyAndForecastDTOList must not be empty");
            }

            scheduleHistoryAndForecastService.updateHistoryAndForecast(sessionContext.getPropertyId(property), historyAndForecastDTOList);

            return Collections.emptyMap();
        }

        /**
         * 编辑部门排班信息
         *
         * @param departmentId       部门
         * @param property           资产标识
         * @param scheduleUpdateDtos 排班
         * @return {}
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(389)
        @PostMapping("/{departmentId:\\d+}")
        public Map<String, Object> updateSchedule(@PathVariable("departmentId") int departmentId, String property,
                                  @RequestBody List<@Valid ScheduleUpdateDTO> scheduleUpdateDtos) {

            scheduleInfoService.updateScheduleInfo(sessionContext.getPropertyId(property), departmentId, scheduleUpdateDtos, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 新增、编辑 beo
         *
         * @param id             beo id
         * @param property       资产标识
         * @param scheduleBeoDto beo 信息
         * @return {}
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(390)
        @PostMapping("/beo/{id:\\d+}")
        public Map<String, Object> saveBeo(@PathVariable("id") int id, String property, @Valid @RequestBody ScheduleBeoDTO scheduleBeoDto) {
            Integer propertyId = sessionContext.getPropertyId(property);
            int userId = sessionContext.getLoginUserId();
            if (id == 0) {
                scheduleBeoService.save(propertyId, scheduleBeoDto, userId);
            } else {
                ScheduleBeo scheduleBeo = scheduleBeoService.getBeoById(id);
                checkPropertySame(scheduleBeo, propertyId);
                checkIsCreatorForBeo(scheduleBeo, userId);

                scheduleBeoService.updateBeo(id, scheduleBeoDto, userId);
            }
            return Collections.emptyMap();
        }

        /**
         * 删除 beo
         *
         * @param id beo id
         * @return {}
         * @author yyt
         * 2023/6/29
         */
        @ApiId(391)
        @DeleteMapping("/beo/{id:\\d+}")
        public Map<String, Object> deleteBeo(@PathVariable("id") int id, String property) {
            Integer propertyId = sessionContext.getPropertyId(property);

            ScheduleBeo scheduleBeo = scheduleBeoService.getBeoById(id);
            checkPropertySame(scheduleBeo, propertyId);
            checkIsCreatorForBeo(scheduleBeo, sessionContext.getLoginUserId());

            scheduleBeoService.delete(id);

            return Collections.emptyMap();
        }

        /**
         * 获取工作开始日
         *
         * @param property 资产标识
         * @return 工作开始日
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(395)
        @GetMapping("/work-day")
        public ScheduleWorkDayDTO getWorkDay(String property) {
            return scheduleWorkDayService.getWorkDayDTO(sessionContext.getPropertyId(property));
        }

        /**
         * 修改工作开始日
         *
         * @param property           资产标识
         * @param scheduleWorkDayDTO workDay
         * @return {}
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(396)
        @PutMapping("/work-day")
        public Map<String, Object> updateWorkDay(String property, @Valid @RequestBody ScheduleWorkDayDTO scheduleWorkDayDTO) {
            Integer propertyId = sessionContext.getPropertyId(property);
            scheduleWorkDayService.save(propertyId, scheduleWorkDayDTO, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 获取用户拥有权限的部门集合
         * <p>用于前端渲染分类下拉框中的数据，区分出来当前用户拥有的、未拥有的权限分类数据，用于【申请数据权限】的操作</p>
         *
         * @param property 资产标识
         * @return 部门权限集合
         * @author yyt
         * @date 2023/6/29
         */
        @ApiId(392)
        @GetMapping("/login-user/department/permission-config")
        public List<UserDepartmentPermissionDTO> listUserDepartmentPermission(String property) {
            Integer propertyId = sessionContext.getPropertyId(property);
            return scheduleDepartmentPermissionConfigService.listUserDepartmentPermission(propertyId, sessionContext.getLoginUserId());
        }

        /**
         * 用户申请部门权限
         *
         * @param id                      部门 id
         * @param property                资产标识
         * @param departmentPermissionDTO 申请权限信息
         * @return cloud.wconnect.commonlib.pojo.dto.result.ResultDTO<java.lang.Void>
         * @author lichen
         * @date 2025/11/13
         */
        @ApiId(393)
        @PostMapping("/department/{id:\\d+}/permission-config/apply")
        public ResultDTO<Void> apply(@PathVariable int id, String property,
                                     @Valid @RequestBody UserDepartmentPermissionDTO departmentPermissionDTO) {
            if (Objects.isNull(departmentPermissionDTO.getRead()) && Objects.isNull(departmentPermissionDTO.getWrite())) {
                throw new BadRequestException(CommonErrorI18nConstant.INPUT_AT_LEAST_ONE,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(Lists.newArrayList("read", "write")),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "parameter missing");
            }

            //资产DTO
            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));

            ScheduleDepartment scheduleDepartment = scheduleDepartmentService.checkExist(id);
            checkPropertySame(scheduleDepartment, propertyDTO.getId());

            return scheduleDepartmentPermissionConfigService.apply(scheduleDepartment, propertyDTO, sessionContext.getLoginUserId(),
                    departmentPermissionDTO);
        }

        /**
         * 员工分配部门
         *
         * @param employeeId 员工
         * @param property   资产标识
         * @param params     部门
         * @return {}
         * @author yyt
         * @date 2023/6/30
         */
        @ApiId(394)
        @PutMapping("/employee/{employeeId:\\d+}/department")
        public Map<String, Object> employeeChangeDepartment(@PathVariable int employeeId, String property, @RequestBody Map<String, Integer> params) {
            Integer departmentId = params.get("departmentId");
            if (departmentId == null) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("departmentId"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the departmentId cannot be null");
            }

            ScheduleDepartment scheduleDepartment = scheduleDepartmentService.checkExist(departmentId);
            Integer propertyId = sessionContext.getPropertyId(property);
            checkPropertySame(scheduleDepartment, propertyId);

            ScheduleDepartmentEmployee employee = scheduleDepartmentEmployeeService.checkExist(employeeId);
            checkPropertySame(employee, propertyId);
            // 只有杂项部门下的员工支持调整部门
            if (employee.getDepartmentId() != ScheduleDepartment.MISCELLANEOUS_ID) {
                throw new BadRequestException(ScheduleErrorFiledCons.SCHEDULE_CHANGE_DEPARTMENT,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only employee of the miscellaneous department support the change department;employeeId:" + employeeId);
            }

            // 校验是否拥有此部门权限
            List<ScheduleDepartmentPermissionConfig> hasAccessPermissionConfigs =
                    scheduleDepartmentPermissionConfigService.getHasAccessPermissionConfigs(propertyI18nRedisService.get(propertyId).getTenantId(),
                            propertyId, sessionContext.getLoginUserId(), departmentId);
            if (CollectionUtils.isEmpty(hasAccessPermissionConfigs)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "You have no permission to operate this department");
            }

            scheduleDepartmentEmployeeService.changeEmployeeDepartment(employee.getId(), departmentId);

            return Collections.emptyMap();
        }

        /**
         * 获取员工导入记录
         *
         * @param property       资产标识
         * @param importLogQuery 搜索条件
         * @return 员工导入记录
         * @author yyt
         * @date 2023/7/3
         */
        @ApiId(397)
        @GetMapping("/employee/import-log")
        public ListPagedDTO<EmployeeImportLogDTO> listEmployeeImportLog(String property, @Valid ImportLogQuery importLogQuery) {
            Integer propertyId = sessionContext.getPropertyId(property);
            return scheduleDepartmentEmployeeImportLogService.listEmployeeImportLog(propertyId, importLogQuery);
        }

        /**
         * 获取排班导入记录
         *
         * @param property 资产标识
         * @param query    搜索条件
         * @return 排班导入记录
         * @author yyt
         * @date 2023/7/3
         */
        @ApiId(398)
        @GetMapping("/import-log")
        public ListPagedDTO<ScheduleImportLogDTO> listScheduleImportLog(String property, @Valid ImportScheduleLogQuery query) {
            Integer propertyId = sessionContext.getPropertyId(property);
            return scheduleInfoImportLogService.listScheduleImportLog(propertyId, query);
        }

        /**
         * 获取 history and forecast 导入记录
         *
         * @param property       资产标识
         * @param importLogQuery 搜索条件
         * @return 排班导入记录
         * @author yyt
         * @date 2023/7/3
         */
        @ApiId(399)
        @GetMapping("/history-and-forecast/import-log")
        public ListPagedDTO<HistoryAndForecastImportLogDTO> listHistoryAndForecastImportLog(String property, @Valid ImportLogQuery importLogQuery) {
            Integer propertyId = sessionContext.getPropertyId(property);
            return scheduleHistoryAndForecastImportLogService.listHistoryAndForecastImportLog(propertyId, importLogQuery);
        }

        /**
         * 解决 history and forecast 异常
         *
         * @param id        异常记录 id
         * @param property  资产标识
         * @param importDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/7/4
         */
        @ApiId(400)
        @PutMapping("/history-and-forecast/exception/{id:\\d+}")
        public Map<String, Object> resolveHistoryAndForecastException(@PathVariable int id, String property,
                                                      @Validated(Edition.class) ImportHistoryAndForecastDTO importDTO) {
            ScheduleHistoryAndForecastImportLog importLog = scheduleHistoryAndForecastImportLogService.getById(id);
            if (Objects.isNull(importLog)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND, BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "the scheduleHistoryAndForecastImportLog is not found;id:" + id);
            }
            if (!Lists.newArrayList(ReportImportResultCons.IMPORT_RESULT_FAIL, ReportImportResultCons.IMPORT_RESULT_MISSED)
                    .contains(importLog.getImportResult())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_ONLY_HANDLE_EXCEPTION,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the scheduleHistoryAndForecastImportLog status is " +
                        "error;id:" + id);
            }

            int userId = sessionContext.getLoginUserId();

            MultipartFile file = importDTO.getFile();
            if (Objects.isNull(file)) {
                // 忽略该异常
                scheduleHistoryAndForecastImportLogService.update(id, ScheduleHistoryAndForecastImportLog.IMPORT_RESULT_IGNORE, userId);
            } else {
                PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));

                if (StringUtils.isBlank(importDTO.getSource())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("source"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The source cannot be blank");
                }
                //校验source是否符合预期
                checkSource(propertyDTO, importDTO.getSource());

                Map<String, IHistoryAndForecastFileParse> beans = applicationContext.getBeansOfType(IHistoryAndForecastFileParse.class);
                for (IHistoryAndForecastFileParse bean : beans.values()) {
                    if (bean.supports(importDTO.getSource())) {
                        bean.checkFile(propertyDTO, file);

                        bean.handleImportLog(propertyDTO, importLog, importLog.getTheDate(), importLog.getFromDate(),
                                importLog.getToDate(), userId);

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

            return Collections.emptyMap();
        }

        /**
         * 手动导入 history and forecast 报告
         *
         * @param property                    资产标识
         * @param importHistoryAndForecastDTO 报告导入DTO
         * @return {}
         * @author yyt
         * @date 2023/7/4
         */
        @ApiId(401)
        @PostMapping("/history-and-forecast/file")
        public Map<String, Object> importHistoryAndForecastReport(String property, @Validated(Addition.class) ImportHistoryAndForecastDTO importHistoryAndForecastDTO) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));

            //校验source是否符合预期
            checkSource(propertyDTO, importHistoryAndForecastDTO.getSource());

            String timezone = propertyDTO.getTimezone();
            checkDate(timezone, importHistoryAndForecastDTO.getStartDate(), importHistoryAndForecastDTO.getToDate(), ScheduleLimitDayCons.IMPORT_DATA);

            int userId = sessionContext.getLoginUserId();

            MultipartFile file = importHistoryAndForecastDTO.getFile();
            Map<String, IHistoryAndForecastFileParse> beans = applicationContext.getBeansOfType(IHistoryAndForecastFileParse.class);
            for (IHistoryAndForecastFileParse bean : beans.values()) {
                if (bean.supports(importHistoryAndForecastDTO.getSource())) {
                    bean.checkFile(propertyDTO, file);

                    ScheduleHistoryAndForecastImportLog importLog = bean.handleImportLog(propertyDTO, null,
                            DateTimeUtils.dateNowTimestamp(timezone), importHistoryAndForecastDTO.getStartDate(),
                            importHistoryAndForecastDTO.getToDate(), userId);

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

            return Collections.emptyMap();
        }

        /**
         * 解决异常
         *
         * @param property          资产标识
         * @param id                异常 id
         * @param file              报告
         * @param importEmployeeDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/11/20
         */
        @ApiId(900)
        @PutMapping("/employee/exception/{id:\\d+}")
        public Map<Object, Object> resolve(String property, @PathVariable int id, MultipartFile file, ImportEmployeeDTO importEmployeeDTO) {
            ScheduleDepartmentEmployeeImportLog importLog = scheduleDepartmentEmployeeImportLogService.checkExist(id);

            int propertyId = sessionContext.getPropertyId(property);
            checkPropertySame(importLog, propertyId);

            if (!Lists.newArrayList(ReportImportResultCons.IMPORT_RESULT_FAIL, ReportImportResultCons.IMPORT_RESULT_MISSED)
                    .contains(importLog.getImportResult())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_ONLY_HANDLE_EXCEPTION,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the scheduleDepartmentEmployeeImportLog status is " +
                        "error;id:" + id);
            }

            int userId = sessionContext.getLoginUserId();

            if (Objects.isNull(file)) {
                // 忽略该异常
                scheduleDepartmentEmployeeImportLogService.update(id, ReportImportResultCons.IMPORT_RESULT_IGNORE, userId);
            } else {
                PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
                SymbolBO tenantSymbolBO = sessionContext.getTenantSymbolBO();

                Map<String, IEmployeeFileParse> beans = applicationContext.getBeansOfType(IEmployeeFileParse.class);
                for (IEmployeeFileParse bean : beans.values()) {
                    if (bean.supports(SystemPlatformEnum.getSourceForADP(importEmployeeDTO.getType()))) {
                        bean.checkFile(propertyDTO, file);

                        List<ScheduleDepartmentEmployeeImportLog> importLogs = bean.handleImportLog(tenantSymbolBO, Lists.newArrayList(propertyDTO), importLog, file, userId);

                        bean.handleFile(tenantSymbolBO, Lists.newArrayList(propertyDTO), file, importLogs, userId);
                        return Collections.emptyMap();
                    }
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 手动导入员工
         *
         * @param property 资产标识
         * @param file     员工报告
         * @return {}
         * @author yyt
         * @date 2023/7/6
         */
        @ApiId(402)
        @PostMapping("/employee/file")
        public Map<String, Object> importEmployeeReport(String property, MultipartFile file, @Valid ImportEmployeeDTO importEmployeeDTO) {
            if (Objects.isNull(file)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("file"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the file must not be null");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            List<PropertyDTO> propertyDTOS = Lists.newArrayList(propertyDTO);
            if (importEmployeeDTO.getType() == ImportEmployeeDTO.TYPE_ADP) {
                List<Integer> propertyIds = importEmployeeDTO.getPropertyIds();
                if (CollectionUtils.isEmpty(propertyIds)) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("propertyIds"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the propertyIds must not be empty");
                }
                if (!new HashSet<>(sessionContext.getPropertyIds()).containsAll(propertyIds)) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the user has no permission to access the property");
                }
                propertyDTOS = propertyI18nRedisService.list(propertyIds);
            }

            SymbolBO tenant = sessionContext.getTenantSymbolBO();
            int userId = sessionContext.getLoginUserId();

            Map<String, IEmployeeFileParse> beans = applicationContext.getBeansOfType(IEmployeeFileParse.class);
            for (IEmployeeFileParse bean : beans.values()) {
                if (bean.supports(SystemPlatformEnum.getSourceForADP(importEmployeeDTO.getType()))) {
                    bean.checkFile(propertyDTO, file);

                    List<ScheduleDepartmentEmployeeImportLog> importLogs = bean.handleImportLog(tenant, propertyDTOS, null, file, userId);

                    bean.handleFile(tenant, propertyDTOS, file, importLogs, userId);
                    return Collections.emptyMap();
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 手动导入排班
         *
         * @param property          资产标识
         * @param file              报告文件
         * @param importScheduleDTO 选择日期
         * @return {}
         * @author yyt
         * @date 2023/7/6
         */
        @ApiId(403)
        @PostMapping("/file")
        public Map<String, Object> importScheduleReport(String property, MultipartFile file, @Valid ImportScheduleDTO importScheduleDTO) {
            if (Objects.isNull(file)) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("file"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the file must not be null");
            }

            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            checkDate(propertyDTO.getTimezone(), importScheduleDTO.getStartDate(), importScheduleDTO.getToDate(), ScheduleLimitDayCons.IMPORT_DATA);

            int userId = sessionContext.getLoginUserId();

            Map<String, IScheduleFileParse> beans = applicationContext.getBeansOfType(IScheduleFileParse.class);
            for (IScheduleFileParse bean : beans.values()) {
                bean.checkFile(propertyDTO, file);

                ScheduleInfoImportLog importLog = bean.handleImportLog(propertyDTO, importScheduleDTO.getStartDate(),
                        importScheduleDTO.getToDate(), file, userId);

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

            return Collections.emptyMap();
        }

        /**
         * 检查指定用户是创建人
         *
         * @param scheduleBeo beo
         * @param userId      指定用户
         * @author yyt
         * 2023/6/29
         */
        public void checkIsCreatorForBeo(ScheduleBeo scheduleBeo, int userId) {
            if (scheduleBeo.getUpdatedBy() != userId) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "Modify without permission;scheduleBeoId:"
                        + scheduleBeo.getId() + ",userId:" + userId);
            }
        }

        /**
         * 校验 scheduleBeo property 与 参数 property 一致
         *
         * @param scheduleBeo     scheduleBeo
         * @param paramPropertyId 参数 property
         * @author yyt
         * @date 2023/6/29
         */
        public void checkPropertySame(ScheduleBeo scheduleBeo, Integer paramPropertyId) {
            if (!scheduleBeo.getPropertyId().equals(paramPropertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the scheduleBeo;propertyId:" + paramPropertyId + ",scheduleBeoPropertyId:" +
                        scheduleBeo.getPropertyId() + ",scheduleBeoId:" + scheduleBeo.getId());
            }
        }

        /**
         * 校验 scheduleDepartment property 与 参数 property 一致
         *
         * @param scheduleDepartment scheduleDepartment
         * @param propertyId         参数 property
         * @author yyt
         * @date 2023/6/30
         */
        private void checkPropertySame(ScheduleDepartment scheduleDepartment, Integer propertyId) {
            if (!scheduleDepartment.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 scheduleDepartment;propertyId:" + propertyId + ",scheduleDepartmentPropertyId:" +
                        scheduleDepartment.getPropertyId() + ",scheduleDepartmentId:" + scheduleDepartment.getId());
            }
        }

        /**
         * 校验 scheduleDepartmentEmployee property 与 参数 property 一致
         *
         * @param employee   scheduleDepartmentEmployee
         * @param propertyId 参数 property
         * @author yyt
         * @date 2023/6/30
         */
        private void checkPropertySame(ScheduleDepartmentEmployee employee, Integer propertyId) {
            if (!employee.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 scheduleDepartmentEmployee;propertyId:" + propertyId
                        + ",scheduleDepartmentEmployeePropertyId:" + employee.getPropertyId()
                        + ",scheduleDepartmentEmployeeId:" + employee.getId());
            }
        }

        /**
         * 校验 ScheduleDepartmentEmployeeImportLog property 与 参数 property 一致
         *
         * @param importLog  ScheduleDepartmentEmployeeImportLog
         * @param propertyId 参数 property
         * @author yyt
         * @date 2023/11/20
         */
        private void checkPropertySame(ScheduleDepartmentEmployeeImportLog 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 scheduleDepartmentEmployeeImportLog;propertyId:" + propertyId
                        + ",scheduleDepartmentEmployeeImportLogPropertyId:" + importLog.getPropertyId()
                        + ",scheduleDepartmentEmployeeImportLogId:" + importLog.getId());
            }
        }

        /**
         * 校验source是否符合预期
         *
         * @param propertyDTO 资产DTO
         * @param source      来源
         * @author lichen
         * @date 2025/7/29
         */
        private void checkSource(PropertyDTO propertyDTO, String source) {
            String pmsType = propertyDTO.getPmsType();
            //校验source是否符合预期
            if (!Lists.newArrayList(pmsType, SystemPlatformEnum.OTHER.getDesc()).contains(source)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_SOURCE_SYSTEM_NOT_SUPPORTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The source must be in:" + pmsType + "|" + SystemPlatformEnum.OTHER.getDesc());
            }
        }

    }