package net.yyc.quartz.service.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.yyc.quartz.entity.JobBriefing; import net.yyc.quartz.entity.SysJob; import net.yyc.quartz.mapper.JobBriefingMapper; import net.yyc.quartz.service.JobBriefingService; import net.yyc.quartz.service.SysJobService; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; import java.util.stream.Collectors; /** * 每日简报服务实现 */ @Slf4j @Service @RequiredArgsConstructor public class JobBriefingServiceImpl implements JobBriefingService { private final JobBriefingMapper jobBriefingMapper; private final SysJobService sysJobService; @Override public JobBriefing generateBriefing(LocalDate date) { LocalDateTime startTime = date.atStartOfDay(); LocalDateTime endTime = date.atTime(LocalTime.MAX); // 1. 获取昨日执行的任务列表 List executedJobIds = jobBriefingMapper.selectExecutedJobIds(startTime, endTime); // 2. 获取当前运行中的任务 List runningJobs = sysJobService.list(null); if (runningJobs == null) { runningJobs = Collections.emptyList(); } List runningJobList = runningJobs.stream() .filter(job -> "2".equals(job.getJobStatus())) .collect(Collectors.toList()); // 3. 计算任务列表并集(昨日执行 + 当前运行中) Set allJobIds = new HashSet<>(); allJobIds.addAll(executedJobIds); runningJobList.forEach(job -> allJobIds.add(job.getJobId())); // 4. 获取任务ID到任务的映射 Map jobMap = new HashMap<>(); for (SysJob job : runningJobList) { jobMap.put(job.getJobId(), job); } // 5. 查询昨日执行统计 List> statSummary = jobBriefingMapper.selectJobStatSummary(startTime, endTime); Map> statMap = new HashMap<>(); for (Map stat : statSummary) { Integer jobId = ((Number) stat.get("job_id")).intValue(); statMap.put(jobId, stat); } // 6. 构建任务统计列表 List jobStats = new ArrayList<>(); for (Integer jobId : allJobIds) { SysJob sysJob = jobMap.get(jobId); Map stat = statMap.get(jobId); String jobName = sysJob != null ? sysJob.getJobName() : String.valueOf(stat != null ? stat.get("job_name") : "未知"); String jobGroup = sysJob != null ? sysJob.getJobGroup() : String.valueOf(stat != null ? stat.get("job_group") : "未知"); int execCount = 0, successCount = 0, failCount = 0; long avgExecuteTime = 0; String lastExecuteTime = null; String lastExecStatus = null; String exceptionInfo = null; if (stat != null) { execCount = ((Number) stat.get("execCount")).intValue(); successCount = ((Number) stat.get("successCount")).intValue(); failCount = ((Number) stat.get("failCount")).intValue(); Object avgObj = stat.get("avgExecuteTime"); if (avgObj != null) { avgExecuteTime = ((Number) avgObj).longValue(); } lastExecuteTime = stat.get("lastExecuteTime") != null ? String.valueOf(stat.get("lastExecuteTime")) : null; } // 查询最新执行记录 if (endTime != null) { Map lastLog = jobBriefingMapper.selectLastExecLog(jobId, LocalDateTime.now()); if (lastLog != null) { lastExecStatus = "1".equals(String.valueOf(lastLog.get("job_log_status"))) ? "失败" : "成功"; Object exInfo = lastLog.get("exception_info"); if (exInfo != null && !"null".equals(String.valueOf(exInfo))) { exceptionInfo = String.valueOf(exInfo); if (exceptionInfo.length() > 200) { exceptionInfo = exceptionInfo.substring(0, 200) + "..."; } } } } double successRate = execCount > 0 ? (double) successCount / execCount * 100 : 0; jobStats.add(JobBriefing.JobStatInfo.builder() .jobId(jobId) .jobName(jobName) .jobGroup(jobGroup) .execCount(execCount) .successCount(successCount) .failCount(failCount) .successRate(Math.round(successRate * 100) / 100.0) .avgExecuteTime(avgExecuteTime) .lastExecuteTime(lastExecuteTime) .lastExecStatus(lastExecStatus) .exceptionInfo(exceptionInfo) .build()); } // 按任务名称排序 jobStats.sort(Comparator.comparing(JobBriefing.JobStatInfo::getJobName)); // 7. 检测错过执行的任务 List missedJobs = detectMissedJobs(date, startTime, endTime, executedJobIds, runningJobList); return JobBriefing.builder() .reportDate(date) .jobStats(jobStats) .missedJobs(missedJobs) .generatedAt(LocalDateTime.now()) .build(); } @Override public JobBriefing generateYesterdayBriefing() { return generateBriefing(LocalDate.now().minusDays(1)); } /** * 检测错过执行的任务 * 条件:任务状态=RUNNING(2) 且 next_time已过 且 昨日无执行记录 */ private List detectMissedJobs(LocalDate date, LocalDateTime startTime, LocalDateTime endTime, List executedJobIds, List runningJobList) { List missed = new ArrayList<>(); Set executedSet = new HashSet<>(executedJobIds); LocalDateTime now = LocalDateTime.now(); for (SysJob job : runningJobList) { // 跳过已删除的任务 if ("4".equals(job.getJobStatus())) { continue; } // 检查昨日是否有执行记录 if (executedSet.contains(job.getJobId())) { continue; } // 检查 next_time 是否已过 if (job.getNextTime() != null && job.getNextTime().isBefore(now)) { missed.add(JobBriefing.MissedJobInfo.builder() .jobName(job.getJobName()) .jobGroup(job.getJobGroup()) .nextExecuteTime(job.getNextTime() != null ? job.getNextTime().toString() : null) .remark("下次执行时间已过,请检查任务调度") .build()); } } return missed; } }