# HotelKpiJobChecker.java

    package cloud.wconnect.workperformance.scheduler.hotelkpi;

    import cloud.wconnect.commonlib.cons.FunctionIdConstant;
    import cloud.wconnect.commonlib.cons.MoudleIdConstant;
    import cloud.wconnect.commonlib.cons.SchedulerCons;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.scheduler.JobBodyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.SchedulerInternalService;
    import cloud.wconnect.commonlib.util.CronRuleUtil;
    import cloud.wconnect.workperformance.entity.hotelkpi.HotelKpiScheduler;
    import cloud.wconnect.workperformance.service.hotelkpi.IHotelKpiSchedulerService;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    /**
     * Hotel KPI 功能 job 检查器
     *
     * @author zc
     * @date 2024/7/1
     */
    @Slf4j
    @Component
    public class HotelKpiJobChecker {

        private final IHotelKpiSchedulerService schedulerService;
        private final SchedulerInternalService schedulerInternalService;
        private final PropertyI18nRedisService propertyI18nRedisService;
        private final ObjectMapper objectMapper;
        private final String defaultTimezone;

        public HotelKpiJobChecker(IHotelKpiSchedulerService schedulerService, SchedulerInternalService schedulerInternalService,
                                  PropertyI18nRedisService propertyI18nRedisService, ObjectMapper objectMapper, @Value("${default.timezone}") String defaultTimezone) {
            this.schedulerService = schedulerService;
            this.schedulerInternalService = schedulerInternalService;
            this.propertyI18nRedisService = propertyI18nRedisService;
            this.objectMapper = objectMapper;
            this.defaultTimezone = defaultTimezone;
        }

        private final static Integer MODULE_ID = MoudleIdConstant.WORK_PERFORMANCE;
        private final static Integer FUNCTION_ID = FunctionIdConstant.HOTEL_KPI;

        /**
         * 每小时检查一次未同步的数据，将未同步的数据同步到调度中心
         *
         * @author zc
         * @date 2024/7/1
         */
        @Scheduled(cron = "${schedule.cron.sync-scheduler-job}", zone = "${default.timezone}")
        public void checkSchedulerJob() {
            // 查询未同步的定时任务
            List<HotelKpiScheduler> unSyncJobs = schedulerService.list(new LambdaQueryWrapper<HotelKpiScheduler>()
                    .eq(HotelKpiScheduler::getSyncState, false));
            if (unSyncJobs.isEmpty()) {
                return;
            }

            List<Integer> propertyIds = unSyncJobs.stream().map(HotelKpiScheduler::getPropertyId).distinct().toList();
            Map<Integer, String> propertyIdToTimezoneMapping = propertyI18nRedisService.list(propertyIds)
                    .stream().collect(Collectors.toMap(PropertyDTO::getId, PropertyDTO::getTimezone));

            List<String> updateJobIds = new ArrayList<>();
            List<String> deleteJobIds = new ArrayList<>();
            unSyncJobs.forEach(job -> {
                if (job.getStatus() == SchedulerCons.STATUS_START) { // 开启
                    try {
                        JobBodyDTO jobBodyDTO = new JobBodyDTO()
                                .setJobId(job.getId())
                                .setModuleId(MODULE_ID)
                                .setFunctionId(FUNCTION_ID)
                                .setStartDate(job.getStartDate())
                                .setCronRule(CronRuleUtil.generateCronRuleForScheduler(objectMapper.readValue(job.getScheduler(), HashMap.class),
                                        job.getStartDate(), propertyIdToTimezoneMapping.get(job.getPropertyId()), defaultTimezone));
                        schedulerInternalService.launchJob(jobBodyDTO);
                        updateJobIds.add(job.getId());
                    } catch (Exception e) {
                        log.error("Launch hotelKpi scheduler job [" + job.getId() + "] failed", e);
                    }
                } else { // 关闭 or 删除中
                    try {
                        JobBodyDTO jobBodyDTO = new JobBodyDTO()
                                .setJobId(job.getId())
                                .setModuleId(MODULE_ID)
                                .setFunctionId(FUNCTION_ID);
                        schedulerInternalService.deleteJob(jobBodyDTO);
                        if (job.getStatus() == SchedulerCons.STATUS_STOP) {
                            updateJobIds.add(job.getId());
                        } else {
                            deleteJobIds.add(job.getId());
                        }
                    } catch (Exception e) {
                        log.error("Delete hotelKpi scheduler job [" + job.getId() + "] failed", e);
                    }
                }
            });

            if (!updateJobIds.isEmpty()) {
                schedulerService.refreshSyncStateByIds(updateJobIds);
            }

            if (!deleteJobIds.isEmpty()) {
                schedulerService.removeByIds(deleteJobIds);
            }
        }

    }