基于 Quartz 实现定时执行质控任务的功能,支持:
qc_scheduled_taskCREATE TABLE qc_scheduled_task (
id VARCHAR(32) PRIMARY KEY COMMENT '主键ID',
task_name VARCHAR(200) NOT NULL COMMENT '任务名称',
institution_id VARCHAR(32) NOT NULL COMMENT '机构ID',
-- 执行周期配置
schedule_type VARCHAR(20) NOT NULL COMMENT '周期类型: WEEKLY-每周, MONTHLY-每月',
cron_expression VARCHAR(100) COMMENT 'Cron表达式(Quartz使用)',
-- 执行参数配置
sample_count INT DEFAULT 50 COMMENT '每次执行数量(最大100)',
sample_method VARCHAR(20) DEFAULT 'RANDOM' COMMENT '数据提取方式: RANDOM-随机抽取',
-- 质控标准配置
modality VARCHAR(20) COMMENT '检查类型筛选(可选)',
date_range_days INT COMMENT '数据时间范围(天数,最近N天)',
-- 任务状态
status TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
last_execute_time DATETIME COMMENT '上次执行时间',
next_execute_time DATETIME COMMENT '下次执行时间',
total_execute_count INT DEFAULT 0 COMMENT '累计执行次数',
-- 审计字段
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
create_by VARCHAR(50),
update_by VARCHAR(50),
remark TEXT COMMENT '备注说明',
INDEX idx_institution_id (institution_id),
INDEX idx_status (status),
INDEX idx_next_execute_time (next_execute_time)
) COMMENT '定时质控任务配置表';
qc_scheduled_task_historyCREATE TABLE qc_scheduled_task_history (
id VARCHAR(32) PRIMARY KEY COMMENT '主键ID',
scheduled_task_id VARCHAR(32) NOT NULL COMMENT '定时任务ID',
execute_time DATETIME NOT NULL COMMENT '执行时间',
-- 执行结果
qc_task_id VARCHAR(32) COMMENT '生成的质控任务ID',
execute_status VARCHAR(20) COMMENT '执行状态: SUCCESS-成功, FAILED-失败',
error_message TEXT COMMENT '错误信息',
-- 执行统计
sample_count INT COMMENT '实际抽取数量',
pass_count INT COMMENT '通过数量',
fail_count INT COMMENT '失败数量',
-- 执行时长
duration_seconds INT COMMENT '执行时长(秒)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_scheduled_task_id (scheduled_task_id),
INDEX idx_execute_time (execute_time)
) COMMENT '定时任务执行历史表';
com.zskk.qcns.modules.qc.scheduled/
├── entity/
│ ├── QcScheduledTask.java # 定时任务配置实体
│ └── QcScheduledTaskHistory.java # 执行历史实体
│
├── mapper/
│ ├── QcScheduledTaskMapper.java
│ └── QcScheduledTaskHistoryMapper.java
│
├── service/
│ ├── QcScheduledTaskService.java # 定时任务管理服务
│ ├── QcScheduledTaskExecuteService.java # 定时任务执行服务
│ └── impl/
│ ├── QcScheduledTaskServiceImpl.java
│ └── QcScheduledTaskExecuteServiceImpl.java
│
├── job/
│ └── ScheduledQcJob.java # Quartz Job实现
│
├── controller/
│ └── QcScheduledTaskController.java # API接口
│
└── dto/
├── ScheduledTaskCreateDTO.java # 创建定时任务DTO
├── ScheduledTaskUpdateDTO.java # 更新定时任务DTO
└── ScheduledTaskQueryDTO.java # 查询定时任务DTO
@Configuration
@EnableScheduling
public class QuartzConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(
DataSource dataSource,
JobFactory springBeanJobFactory) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
factory.setJobFactory(springBeanJobFactory);
factory.setQuartzProperties(quartzProperties());
return factory;
}
private Properties quartzProperties() {
Properties props = new Properties();
props.put("org.quartz.scheduler.instanceName", "QcScheduler");
props.put("org.quartz.scheduler.instanceId", "AUTO");
props.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
props.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
props.put("org.quartz.threadPool.threadCount", "5");
return props;
}
}
用户填写配置表单
↓
验证输入参数
├─ 机构是否存在
├─ 执行数量是否 ≤ 100
├─ Cron表达式是否有效
└─ 时间周期是否合理
↓
生成Cron表达式
↓
保存到 qc_scheduled_task 表
↓
创建 Quartz Job 和 Trigger
↓
启动定时任务
↓
返回成功
// 每周执行
schedule_type = "WEEKLY"
用户选择: 星期几 + 具体时间
示例: 每周一上午8点 → 0 0 8 ? * MON
// 每月执行
schedule_type = "MONTHLY"
用户选择: 每月第几天 + 具体时间
示例: 每月1号上午8点 → 0 0 8 1 * ?
Quartz 触发任务
↓
ScheduledQcJob.execute()
↓
1. 检查任务状态(是否启用)
↓
2. 记录执行开始(写入历史表)
↓
3. 查询符合条件的检查数据
├─ 机构筛选
├─ 时间范围筛选
└─ 检查类型筛选
↓
4. 随机抽取 N 条数据(N ≤ 100)
↓
5. 创建质控任务
├─ 任务名称: "定时质控-{任务名称}-{时间}"
├─ 任务类型: MANUAL
├─ exam_ids: 抽取的检查ID列表
└─ 机构ID: 配置的机构
↓
6. 自动执行质控任务
↓
7. 更新执行历史
├─ 执行状态
├─ 生成的任务ID
├─ 统计结果
└─ 执行时长
↓
8. 更新定时任务配置
├─ 上次执行时间
├─ 下次执行时间
└─ 累计执行次数
↓
结束
/**
* 随机抽取检查数据
*
* @param allCandidates 所有候选数据
* @param sampleCount 抽取数量
* @return 抽取的数据列表
*/
private List<StudyInfo> randomSample(List<StudyInfo> allCandidates, int sampleCount) {
// 策略1: 候选数 ≤ 抽取数 → 全部返回
if (allCandidates.size() <= sampleCount) {
return allCandidates;
}
// 策略2: 使用 Collections.shuffle() 随机打乱后取前N个
List<StudyInfo> shuffled = new ArrayList<>(allCandidates);
Collections.shuffle(shuffled); // 内部使用 Random
return shuffled.subList(0, sampleCount);
}
┌─────────────────────────────────────────────────────────┐
│ 定时质控任务 [新增] [批量启用] │
├─────────────────────────────────────────────────────────┤
│ │
│ 筛选: [机构▼] [状态▼] [周期▼] [查询] [重置] │
│ │
│ ┌────┬────────┬──────────┬────────┬──────┬──────┬─────┐│
│ │序号│任务名称│ 机构 │执行周期│状态 │下次执行│操作 ││
│ ├────┼────────┼──────────┼────────┼──────┼──────┼─────┤│
│ │ 1 │周一质控│北京协和医院│ 每周一 │✅ 启用│01-27 08:00│编辑│
│ │ 2 │月度质控│上海瑞金医院│ 每月1日│❌ 禁用│02-01 08:00│编辑│
│ └────┴────────┴──────────┴────────┴──────┴──────┴─────┘│
│ │
│ 共2条 [1] 2 3 10 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 定时任务配置 │
├─────────────────────────────────────────────────┤
│ │
│ 任务名称: [_________________________] │
│ │
│ 所属机构: [北京协和医院 ▼] │
│ │
│ 执行周期: ⦿ 每周一次 ⚪ 每月一次 │
│ │
│ ┌─ 每周配置 ────────────────────────┐ │
│ │ 执行时间: 星期一 [上午▼] 08:00 │ │
│ └──────────────────────────────────┘ │
│ │
│ 每次执行数量: [50] 条 (最多100条) │
│ │
│ 数据提取方式: ⦿ 随机抽取 ⚪ 最新数据 │
│ │
│ 数据时间范围: 最近 [30] 天 │
│ │
│ 检查类型: [全部检查类型 ▼] │
│ │
│ 质控标准: [使用默认标准 ▼] │
│ │
│ 任务状态: ⦿ 启用 ⚪ 禁用 │
│ │
│ 备注: [_________________________] │
│ [_________________________] │
│ │
│ 下次执行时间: 2026-01-27 08:00 (自动计算) │
│ │
│ [取消] [保存] │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 执行历史 - 周一质控 │
├─────────────────────────────────────────────────┤
│ │
│ ┌────┬────────────┬──────────┬────────┬──────┐│
│ │序号│执行时间 │执行状态 │任务ID │操作 ││
│ ├────┼────────────┼──────────┼────────┼──────┤│
│ │ 1 │01-20 08:00 │✅ 成功 │TK12345│查看详情││
│ │ 2 │01-13 08:00 │✅ 成功 │TK12340│查看详情││
│ │ 3 │01-06 08:00 │❌ 失败 │ - │查看日志││
│ └────┴────────────┴──────────┴────────┴──────┘│
│ │
└─────────────────────────────────────────────────┘
// 1. 创建定时任务
POST /api/qc/scheduled-task
Request: {
"taskName": "周一质控",
"institutionId": "INST001",
"scheduleType": "WEEKLY",
"weekDay": "MONDAY",
"executeTime": "08:00",
"sampleCount": 50,
"sampleMethod": "RANDOM",
"dateRangeDays": 30,
"modality": null,
"status": 1
}
// 2. 更新定时任务
PUT /api/qc/scheduled-task/{id}
// 3. 删除定时任务
DELETE /api/qc/scheduled-task/{id}
// 4. 查询定时任务列表
GET /api/qc/scheduled-task/list?pageNum=1&pageSize=10
// 5. 启用/禁用定时任务
PUT /api/qc/scheduled-task/{id}/status?status=1
// 6. 手动触发执行(测试用)
POST /api/qc/scheduled-task/{id}/trigger
// 7. 查询执行历史
GET /api/qc/scheduled-task/{id}/history?pageNum=1&pageSize=10
// 8. 获取下次执行时间
GET /api/qc/scheduled-task/{id}/next-time
@DisallowConcurrentExecution // 禁止并发执行
public class ScheduledQcJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String taskId = context.getJobDetail().getJobDataMap().getString("taskId");
try {
// 执行定时质控任务
scheduledTaskExecuteService.executeScheduledTask(taskId);
} catch (Exception e) {
log.error("定时质控任务执行失败: taskId={}", taskId, e);
throw new JobExecutionException(e);
}
}
}
/**
* 根据配置生成 Cron 表达式
*/
public String generateCronExpression(ScheduleType type, String timeConfig, Integer weekDay, Integer monthDay) {
LocalTime time = LocalTime.parse(timeConfig);
switch (type) {
case WEEKLY:
// 每周几几点
// 0 0 8 ? * MON (每周一8点)
return String.format("0 %d %d ? * %s",
time.getMinute(),
time.getHour(),
weekDayMap.get(weekDay));
case MONTHLY:
// 每月几号几点
// 0 0 8 1 * ? (每月1号8点)
return String.format("0 %d %d %d * ?",
time.getMinute(),
time.getHour(),
monthDay);
default:
throw new IllegalArgumentException("不支持的周期类型");
}
}
@DisallowConcurrentExecution 防止并发@Component
public class ScheduledTaskMonitor {
/**
* 检查长时间未执行的任务
*/
@Scheduled(cron = "0 0 * * * ?") // 每小时检查一次
public void checkLongTimeNoExecute() {
// 查询应该执行但未执行的任务
// 发送告警通知
}
/**
* 统计任务执行成功率
*/
public void calculateSuccessRate(String taskId) {
// 统计最近10次的执行成功率
// 低于阈值则告警
}
}
<!-- Quartz 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.2</version>
</dependency>
这个方案提供了完整的定时质控任务实现路径,核心优势:
✅ 灵活配置 - 支持多种周期、多种抽取策略 ✅ 自动化 - 无需人工干预,自动执行 ✅ 可追溯 - 完整的执行历史记录 ✅ 高可靠 - 基于 Quartz,稳定可靠 ✅ 易扩展 - 后续可轻松添加更多策略
文档创建时间: 2026-01-30 版本: v1.0 作者: Claude Code