package net.yyc.quartz.executor; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.yyc.quartz.entity.JobBriefing; import net.yyc.quartz.service.EmailService; import net.yyc.quartz.service.JobBriefingService; import org.springframework.stereotype.Component; import java.time.format.DateTimeFormatter; /** * 每日简报执行器 * 每天早上9点执行,生成昨日任务执行简报并发送邮件 */ @Slf4j @Component("JobBriefingExecutor") @RequiredArgsConstructor public class JobBriefingExecutor { private final JobBriefingService jobBriefingService; private final EmailService emailService; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); /** * 生成并发送每日简报 * * @return 执行结果(成功返回"success",失败返回异常信息) */ public String sendDailyBriefing() { try { log.info("[简报] 开始生成每日简报..."); // 生成昨日简报 JobBriefing briefing = jobBriefingService.generateYesterdayBriefing(); // 构建邮件内容 String content = buildEmailContent(briefing); String subject = String.format("【定时任务简报】%s", briefing.getReportDate().format(DATE_FORMATTER)); // 发送邮件 boolean success = emailService.sendBriefing(content, subject); if (success) { log.info("[简报] 每日简报发送成功 | 日期={} | 任务数={} | 错过执行={}", briefing.getReportDate(), briefing.getJobStats().size(), briefing.getMissedJobs().size()); return "success"; } else { log.warn("[简报] 每日简报发送失败"); return "send failed"; } } catch (Exception e) { log.error("[简报] 生成每日简报异常", e); throw new RuntimeException("生成每日简报异常: " + e.getMessage(), e); } } private String buildEmailContent(JobBriefing briefing) { StringBuilder sb = new StringBuilder(); sb.append("
"); // 标题 sb.append("报告日期:%s
", briefing.getReportDate().format(DATE_FORMATTER))); // 错过执行告警 if (!briefing.getMissedJobs().isEmpty()) { sb.append("| 任务名称 | "); sb.append("任务组 | "); sb.append("下次执行时间 | "); sb.append("说明 | "); sb.append("
|---|---|---|---|
| %s | ", escapeHtml(job.getJobName()))); sb.append(String.format("%s | ", escapeHtml(job.getJobGroup()))); sb.append(String.format("%s | ", escapeHtml(job.getNextExecuteTime()))); sb.append(String.format("%s | ", escapeHtml(job.getRemark()))); sb.append("
| 任务名称 | "); sb.append("任务组 | "); sb.append("执行次数 | "); sb.append("成功 | "); sb.append("失败 | "); sb.append("成功率 | "); sb.append("平均耗时(ms) | "); sb.append("最后执行 | "); sb.append("状态 | "); sb.append("异常信息 | "); sb.append("
|---|---|---|---|---|---|---|---|---|---|
| %s | ", escapeHtml(job.getJobName()))); sb.append(String.format("%s | ", escapeHtml(job.getJobGroup()))); sb.append(String.format("%d | ", job.getExecCount())); sb.append(String.format("%d | ", job.getSuccessCount())); sb.append(String.format("%d | ", job.getFailCount() > 0 ? "color: red; font-weight: bold;" : "", job.getFailCount())); sb.append(String.format("%.2f%% | ", job.getSuccessRate() < 90 ? "color: orange; font-weight: bold;" : "", job.getSuccessRate())); sb.append(String.format("%d | ", job.getAvgExecuteTime())); sb.append(String.format("%s | ", job.getLastExecuteTime() != null ? job.getLastExecuteTime().substring(0, Math.min(19, job.getLastExecuteTime().length())) : "")); sb.append(String.format("%s | ", "失败".equals(job.getLastExecStatus()) ? "color: red; font-weight: bold;" : "color: green;", job.getLastExecStatus() != null ? job.getLastExecStatus() : "-")); sb.append(String.format("%s | ", escapeHtml(job.getExceptionInfo()))); sb.append("
生成时间:%s
", briefing.getGeneratedAt())); sb.append(""); return sb.toString(); } private String escapeHtml(String text) { if (StrUtil.isBlank(text) || "null".equals(text)) { return ""; } return text.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """) .replace("'", "'"); } }