# 定时质控任务功能设计方案 ## 📋 功能概述 基于 Quartz 实现定时执行质控任务的功能,支持: - 按机构配置定时任务 - 灵活的执行周期(每周一次、每月一次) - 每次执行数量可配置(最大100条) - 数据随机抽取 - 自动执行并记录历史 --- ## 一、数据库设计 ### 1.1 定时任务配置表 `qc_scheduled_task` ```sql CREATE 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 '定时质控任务配置表'; ``` ### 1.2 定时任务执行历史表 `qc_scheduled_task_history` ```sql CREATE 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 '定时任务执行历史表'; ``` --- ## 二、后端架构设计 ### 2.1 核心类设计 ``` 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 ``` ### 2.2 Quartz 配置类 ```java @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; } } ``` --- ## 三、业务流程设计 ### 3.1 定时任务创建流程 ``` 用户填写配置表单 ↓ 验证输入参数 ├─ 机构是否存在 ├─ 执行数量是否 ≤ 100 ├─ Cron表达式是否有效 └─ 时间周期是否合理 ↓ 生成Cron表达式 ↓ 保存到 qc_scheduled_task 表 ↓ 创建 Quartz Job 和 Trigger ↓ 启动定时任务 ↓ 返回成功 ``` ### 3.2 Cron 表达式生成规则 ```java // 每周执行 schedule_type = "WEEKLY" 用户选择: 星期几 + 具体时间 示例: 每周一上午8点 → 0 0 8 ? * MON // 每月执行 schedule_type = "MONTHLY" 用户选择: 每月第几天 + 具体时间 示例: 每月1号上午8点 → 0 0 8 1 * ? ``` ### 3.3 定时任务执行流程 ``` Quartz 触发任务 ↓ ScheduledQcJob.execute() ↓ 1. 检查任务状态(是否启用) ↓ 2. 记录执行开始(写入历史表) ↓ 3. 查询符合条件的检查数据 ├─ 机构筛选 ├─ 时间范围筛选 └─ 检查类型筛选 ↓ 4. 随机抽取 N 条数据(N ≤ 100) ↓ 5. 创建质控任务 ├─ 任务名称: "定时质控-{任务名称}-{时间}" ├─ 任务类型: MANUAL ├─ exam_ids: 抽取的检查ID列表 └─ 机构ID: 配置的机构 ↓ 6. 自动执行质控任务 ↓ 7. 更新执行历史 ├─ 执行状态 ├─ 生成的任务ID ├─ 统计结果 └─ 执行时长 ↓ 8. 更新定时任务配置 ├─ 上次执行时间 ├─ 下次执行时间 └─ 累计执行次数 ↓ 结束 ``` --- ## 四、随机数据抽取算法 ### 4.1 抽取策略 ```java /** * 随机抽取检查数据 * * @param allCandidates 所有候选数据 * @param sampleCount 抽取数量 * @return 抽取的数据列表 */ private List randomSample(List allCandidates, int sampleCount) { // 策略1: 候选数 ≤ 抽取数 → 全部返回 if (allCandidates.size() <= sampleCount) { return allCandidates; } // 策略2: 使用 Collections.shuffle() 随机打乱后取前N个 List shuffled = new ArrayList<>(allCandidates); Collections.shuffle(shuffled); // 内部使用 Random return shuffled.subList(0, sampleCount); } ``` ### 4.2 数据质量保证 - **种子设置**: 不使用固定种子,保证每次真正随机 - **去重**: 确保不会抽取到重复数据 - **优先级**: 可以考虑优先抽取质控失败的数据 --- ## 五、前端界面设计 ### 5.1 定时任务列表页 ``` ┌─────────────────────────────────────────────────────────┐ │ 定时质控任务 [新增] [批量启用] │ ├─────────────────────────────────────────────────────────┤ │ │ │ 筛选: [机构▼] [状态▼] [周期▼] [查询] [重置] │ │ │ │ ┌────┬────────┬──────────┬────────┬──────┬──────┬─────┐│ │ │序号│任务名称│ 机构 │执行周期│状态 │下次执行│操作 ││ │ ├────┼────────┼──────────┼────────┼──────┼──────┼─────┤│ │ │ 1 │周一质控│北京协和医院│ 每周一 │✅ 启用│01-27 08:00│编辑│ │ │ 2 │月度质控│上海瑞金医院│ 每月1日│❌ 禁用│02-01 08:00│编辑│ │ └────┴────────┴──────────┴────────┴──────┴──────┴─────┘│ │ │ │ 共2条 [1] 2 3 10 │ └─────────────────────────────────────────────────────────┘ ``` ### 5.2 新增/编辑定时任务表单 ``` ┌─────────────────────────────────────────────────┐ │ 定时任务配置 │ ├─────────────────────────────────────────────────┤ │ │ │ 任务名称: [_________________________] │ │ │ │ 所属机构: [北京协和医院 ▼] │ │ │ │ 执行周期: ⦿ 每周一次 ⚪ 每月一次 │ │ │ │ ┌─ 每周配置 ────────────────────────┐ │ │ │ 执行时间: 星期一 [上午▼] 08:00 │ │ │ └──────────────────────────────────┘ │ │ │ │ 每次执行数量: [50] 条 (最多100条) │ │ │ │ 数据提取方式: ⦿ 随机抽取 ⚪ 最新数据 │ │ │ │ 数据时间范围: 最近 [30] 天 │ │ │ │ 检查类型: [全部检查类型 ▼] │ │ │ │ 质控标准: [使用默认标准 ▼] │ │ │ │ 任务状态: ⦿ 启用 ⚪ 禁用 │ │ │ │ 备注: [_________________________] │ │ [_________________________] │ │ │ │ 下次执行时间: 2026-01-27 08:00 (自动计算) │ │ │ │ [取消] [保存] │ └─────────────────────────────────────────────────┘ ``` ### 5.3 执行历史查看 ``` ┌─────────────────────────────────────────────────┐ │ 执行历史 - 周一质控 │ ├─────────────────────────────────────────────────┤ │ │ │ ┌────┬────────────┬──────────┬────────┬──────┐│ │ │序号│执行时间 │执行状态 │任务ID │操作 ││ │ ├────┼────────────┼──────────┼────────┼──────┤│ │ │ 1 │01-20 08:00 │✅ 成功 │TK12345│查看详情││ │ │ 2 │01-13 08:00 │✅ 成功 │TK12340│查看详情││ │ │ 3 │01-06 08:00 │❌ 失败 │ - │查看日志││ │ └────┴────────────┴──────────┴────────┴──────┘│ │ │ └─────────────────────────────────────────────────┘ ``` --- ## 六、API 接口设计 ### 6.1 定时任务管理接口 ```java // 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 ``` --- ## 七、关键技术点 ### 7.1 Quartz Job 实现 ```java @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); } } } ``` ### 7.2 动态 Cron 表达式 ```java /** * 根据配置生成 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("不支持的周期类型"); } } ``` ### 7.3 任务幂等性保证 - **去重机制**: 使用 `@DisallowConcurrentExecution` 防止并发 - **事务控制**: 执行失败回滚 - **重试策略**: 失败后记录日志,不自动重试(避免重复执行) --- ## 八、监控与告警 ### 8.1 执行监控 ```java @Component public class ScheduledTaskMonitor { /** * 检查长时间未执行的任务 */ @Scheduled(cron = "0 0 * * * ?") // 每小时检查一次 public void checkLongTimeNoExecute() { // 查询应该执行但未执行的任务 // 发送告警通知 } /** * 统计任务执行成功率 */ public void calculateSuccessRate(String taskId) { // 统计最近10次的执行成功率 // 低于阈值则告警 } } ``` ### 8.2 告警方式 - **系统日志**: 记录详细的执行日志 - **邮件通知**: 任务失败时发送邮件 - **界面提示**: 在定时任务列表中显示告警图标 --- ## 九、安全考虑 ### 9.1 权限控制 - 只有管理员可以创建/修改定时任务 - 用户只能查看自己机构的定时任务 - 执行历史需要相应权限才能查看 ### 9.2 数据隔离 - 定时任务只能抽取本机构的数据 - 机构A不能操作机构B的定时任务 --- ## 十、性能优化 ### 10.1 批量执行 - 如果定时任务配置了大量数据,考虑分批执行 - 使用线程池并行处理(但要控制并发数) ### 10.2 缓存优化 - 缓存机构信息、质控标准等不常变化的数据 - 减少数据库查询次数 --- ## 十一、实施步骤建议 ### 阶段一: 基础功能(1-2天) - 数据库表创建 - Quartz 集成 - 基本的定时任务创建和执行 ### 阶段二: 完善功能(2-3天) - 前端界面开发 - 执行历史记录 - 随机抽取算法 ### 阶段三: 增强功能(1-2天) - 监控告警 - 性能优化 - 测试和调优 --- ## 十二、依赖配置 ### Maven 依赖 ```xml org.quartz-scheduler quartz 2.3.2 org.quartz-scheduler quartz-jobs 2.3.2 ``` --- ## 总结 这个方案提供了完整的定时质控任务实现路径,核心优势: ✅ **灵活配置** - 支持多种周期、多种抽取策略 ✅ **自动化** - 无需人工干预,自动执行 ✅ **可追溯** - 完整的执行历史记录 ✅ **高可靠** - 基于 Quartz,稳定可靠 ✅ **易扩展** - 后续可轻松添加更多策略 --- **文档创建时间**: 2026-01-30 **版本**: v1.0 **作者**: Claude Code