定时质控任务功能设计方案.md 18 KB

定时质控任务功能设计方案

📋 功能概述

基于 Quartz 实现定时执行质控任务的功能,支持:

  • 按机构配置定时任务
  • 灵活的执行周期(每周一次、每月一次)
  • 每次执行数量可配置(最大100条)
  • 数据随机抽取
  • 自动执行并记录历史

一、数据库设计

1.1 定时任务配置表 qc_scheduled_task

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

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 配置类

@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 表达式生成规则

// 每周执行
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 抽取策略

/**
 * 随机抽取检查数据
 *
 * @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);
}

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 定时任务管理接口

// 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 实现

@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 表达式

/**
 * 根据配置生成 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 执行监控

@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 依赖

<!-- 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