开发大纲.md 41 KB

医学影像智能质控系统 - 开发大纲 V2.0

项目概述

项目名称:医学影像智能质控系统(QConline) 项目定位:演示型质控系统,支持真实质控和预制结果模拟 技术栈

  • 后端:Spring Boot 2.x + MyBatis-Plus + MySQL 8.0 + Spring Security + JWT + Redis
  • 前端:Vue 3 + Element Plus + TypeScript + Vite + Pinia
  • 部署:Docker + Docker Compose(前后端分离部署)
  • 集成:现有阅片器(iframe内嵌)

一、系统功能点清单

1.1 系统管理模块

1.1.1 用户管理

  • 用户列表(分页、搜索)
  • 新增用户(用户名、真实姓名、手机号、邮箱、初始密码)
  • 编辑用户信息
  • 启用/禁用用户
  • 重置密码
  • 删除用户(软删除)
  • 设置用户机构权限(多选)
  • 设置用户菜单权限(树形勾选)

1.1.2 机构管理

  • 机构树形展示(支持多级)
  • 新增机构(机构编码、名称、上级机构、联系人、电话、地址)
  • 编辑机构
  • 删除机构(检查是否有关联数据)
  • 启用/禁用机构

1.1.3 权限管理

  • 菜单列表(树形)
  • 用户登录后根据菜单权限动态加载路由
  • 数据权限:普通用户只能查看所属机构的数据
  • 按钮权限:支持按钮级别的权限控制

1.1.4 认证授权

  • 登录(用户名/密码,返回JWT Token)
  • 登出(清除Token和Redis缓存)
  • Token续期机制
  • 登录日志记录

1.2 患者与检查管理模块

1.2.1 患者信息管理

  • 患者列表(分页、支持患者ID、姓名、性别、日期范围搜索)
  • 患者详情查看
  • 关联检查列表
  • 数据来源:PACS系统通过HTTP接口推送

1.2.2 检查信息管理

  • 检查列表(分页、支持检查ID、患者、检查方式、日期范围搜索)
  • 检查详情查看
  • 内嵌阅片器查看影像(iframe方式)
    • 点击"查看影像"按钮,在弹窗中打开阅片器
    • 阅片器URL从前端配置文件读取
  • 关联质控结果展示

1.2.3 PACS接口对接

  • 提供HTTP接口接收PACS推送的检查数据
    • 接口路径:POST /api/pacs/study
    • 接收参数:患者信息 + 检查信息(JSON格式)
    • 数据校验和去重
    • 自动创建/更新患者和检查记录

1.3 质控配置模块

1.3.1 质控因子管理

  • 质控因子列表(支持分类筛选:数据质控/影像质控/报告质控)
  • 新增质控因子
    • 因子编码(唯一)
    • 因子名称
    • 所属分类(1数据质控 2影像质控 3报告质控)
    • 数据类型(字符串/数字/日期/布尔)
    • 检查规则(JSON格式配置)
    • 描述说明
  • 编辑质控因子
  • 删除质控因子(检查是否被标准引用)
  • 启用/禁用因子

内置质控因子示例

因子编码 因子名称 分类 数据类型 规则示例
DATA_001 检查范围 数据质控 string {"operator":"notEmpty"}
IMAGE_001 体位 影像质控 string {"operator":"in","value":["正位","侧位","斜位"]}
IMAGE_002 图像伪影 影像质控 string {"operator":"equals","value":"无"}
IMAGE_003 中心线 影像质控 string {"operator":"equals","value":"居中"}
IMAGE_004 图像等级 影像质控 number {"operator":">=","value":3}
IMAGE_005 图像数量 影像质控 number {"operator":">=","value":10}
DATA_002 患者姓名完整性 数据质控 string {"operator":"notEmpty"}
DATA_003 检查日期 数据质控 date {"operator":"notNull"}
REPORT_001 报告完整性 报告质控 boolean {"operator":"equals","value":true}
REPORT_002 报告字数 报告质控 number {"operator":">=","value":50}
REPORT_003 诊断结论 报告质控 string {"operator":"notEmpty"}
REPORT_004 报告时效性 报告质控 number {"operator":"<=","value":24,"unit":"小时"}

1.3.2 质控标准管理

  • 质控标准列表(支持分类筛选)
  • 新增质控标准
    • 标准编码(唯一)
    • 标准名称
    • 所属分类
    • 描述说明
  • 编辑质控标准
  • 删除质控标准(检查是否被任务引用)
  • 质控标准配置(关键功能)
    • 从质控因子库中选择因子(支持多选、拖拽排序)
    • 设置每个因子的权重(用于评分)
    • 设置是否必检
    • 设置阈值(覆盖因子默认规则)
    • 实时预览总权重(合计100%)

标准配置示例

标准名称:CT检查质控标准
包含因子:
  - 检查范围(权重10%,必检)
  - 体位(权重15%,必检)
  - 图像伪影(权重20%,必检)
  - 中心线(权重15%)
  - 图像等级(权重20%,阈值≥4)
  - 图像数量(权重20%,阈值≥15)
合格分数线:80分

1.4 质控任务模块

1.4.1 任务管理

  • 质控任务列表
    • 支持任务名称、状态、创建时间筛选
    • 显示任务进度(已检/总数)
    • 显示任务结果统计(合格率)
  • 创建质控任务(关键流程)
    1. 步骤1:填写任务基本信息
      • 任务名称
      • 选择质控标准(下拉选择)
      • 选择所属机构
    2. 步骤2:设置数据范围
      • 方式1:按时间范围(开始日期-结束日期)
      • 方式2:按患者列表(手动输入患者ID,逗号分隔)
      • 预览将要检查的数据(显示检查数量)
    3. 步骤3:确认提交
      • 显示任务摘要
      • 提交后立即开始执行
  • 查看任务详情
    • 任务基本信息
    • 执行进度条(百分比)
    • 实时统计:总数、已检、合格、不合格、合格率
    • 执行日志
  • 停止任务(仅限执行中的任务)
  • 删除任务(已完成/已停止的任务)
  • 重新执行任务

1.4.2 任务执行引擎(后台异步)

  • 异步执行机制(Spring @Async
  • 任务队列管理(防止并发执行过多)
  • 执行流程:
    1. 根据数据范围查询检查列表
    2. 遍历每条检查数据
    3. 应用质控标准中的所有因子
    4. 执行规则引擎判断
    5. 计算评分(加权求和)
    6. 判断合格/不合格(对比合格分)
    7. 保存质控结果
    8. 更新任务进度
  • 进度更新(每检查10条更新一次进度到Redis)
  • 异常处理和重试机制

1.5 质控结果模块

1.5.1 结果查询

  • 结果列表(分页)
    • 支持任务、患者、检查ID、合格状态、日期范围筛选
    • 显示:患者信息、检查信息、质控分类、得分、合格状态
    • 支持导出Excel
  • 结果详情
    • 基本信息:患者、检查、任务、质检时间
    • 评分明细:总分、合格分、实际得分
    • 因子检查结果列表:
    • 因子名称
    • 期望值
    • 实际值
    • 是否合格
    • 扣分
    • 不合格因子高亮显示
    • 关联检查的影像查看(内嵌阅片器)

1.5.2 统计分析

  • 质控概览(Dashboard)
    • 今日质控任务数
    • 本月质控检查数
    • 总体合格率(饼图)
    • 不合格因子排行(柱状图)
  • 质控报表
    • 按时间维度统计(日/月)
    • 按机构维度统计
    • 按质控标准维度统计
    • 趋势图(折线图)

1.6 预制结果管理模块(演示专用)

1.6.1 预制结果配置

  • [ ] 预制结果模板管理

    • 创建预制结果模板
    • 模板名称
    • 关联质控标准
    • 预设总数(如100条)
    • 预设合格数(如75条)
    • 预设不合格数(如25条)
    • 配置不合格因子分布
    • 选择哪些因子会出现不合格
    • 设置每个因子的不合格比例
    • 示例:

      图像伪影不合格:10条(40%)
      中心线不合格:8条(32%)
      图像等级不合格:7条(28%)
      
    • 编辑/删除预制模板

1.6.2 预制结果生成

  • 创建任务时,新增"使用预制结果"选项
    • 开关按钮:是否使用预制结果
    • 选择预制模板
    • 预览预制结果统计
  • 执行预制任务
    • 模拟执行过程(延迟3-5秒,显示进度条动画)
    • 根据预制模板生成结果数据
    • 自动关联真实的检查数据(从数据范围中随机抽取)
    • 生成符合预制规则的质控结果
  • 预制结果与真实结果无差异(前端展示一致)

预制结果生成逻辑

1. 获取数据范围内的检查列表(如100条)
2. 按照预制模板设定:
   - 前75条标记为合格(随机生成高分:85-100分)
   - 后25条标记为不合格(随机生成低分:50-79分)
3. 不合格的25条按因子分布随机分配不合格因子
4. 保存到质控结果表(与真实结果表结构一致)
5. 更新任务状态为已完成

1.7 系统设置模块

1.7.1 参数配置

  • 系统参数设置
    • 阅片器URL配置(前端配置文件)
    • 合格分数线默认值(如80分)
    • 任务并发数限制
    • 单次任务最大检查数(默认100)

1.7.2 日志管理

  • 操作日志查询
    • 记录用户操作(登录、新增、修改、删除等)
    • 支持用户、操作类型、时间筛选
  • 任务执行日志
    • 记录任务执行过程
    • 记录异常和错误

二、系统使用流程逻辑

2.1 用户登录与权限流程

┌─────────────┐
│  用户登录    │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ 输入用户名/密码      │
│ 后端验证(Spring    │
│ Security + JWT)    │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 验证成功            │
│ 1. 生成JWT Token   │
│ 2. 查询用户菜单权限 │
│ 3. 查询用户机构权限 │
│ 4. 存入Redis缓存    │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 返回前端            │
│ - Token             │
│ - 用户信息          │
│ - 菜单列表          │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 前端动态加载路由    │
│ 根据菜单权限生成    │
│ 侧边栏菜单          │
└─────────────────────┘

2.2 PACS数据推送流程

┌─────────────┐
│ PACS系统     │
│ 解析DICOM   │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ HTTP POST接口       │
│ /api/pacs/study     │
│ 推送JSON数据:      │
│ {                   │
│   patientId,        │
│   patientName,      │
│   studyId,          │
│   studyDate,        │
│   modality,         │
│   imageCount,       │
│   ...               │
│ }                   │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 质控系统后端        │
│ 1. 数据校验         │
│ 2. 检查是否重复     │
│    (根据studyId)    │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 保存/更新数据库     │
│ - patient_info      │
│ - study_info        │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 返回成功响应        │
│ PACS系统记录推送日志│
└─────────────────────┘

2.3 质控标准配置流程

┌─────────────────┐
│ 用户进入质控    │
│ 标准管理页面    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 创建新标准      │
│ - 填写基本信息  │
│ - 选择分类      │
└────────┬────────┘
         │
         ▼
┌───────────────────────┐
│ 进入标准配置页面       │
│ 左侧:质控因子库       │
│ (按分类展示)           │
│ 右侧:已选因子列表     │
└────────┬──────────────┘
         │
         ▼
┌───────────────────────┐
│ 从因子库拖拽/选择因子 │
│ 添加到右侧列表         │
└────────┬──────────────┘
         │
         ▼
┌───────────────────────┐
│ 配置每个因子           │
│ - 权重(自动计算总和) │
│ - 是否必检             │
│ - 阈值(可选)         │
└────────┬──────────────┘
         │
         ▼
┌───────────────────────┐
│ 实时校验               │
│ - 总权重是否=100%     │
│ - 必检因子至少1个     │
└────────┬──────────────┘
         │
         ▼
┌───────────────────────┐
│ 保存标准               │
│ 后端保存:             │
│ - qc_standard 表      │
│ - qc_standard_factor表│
└───────────────────────┘

2.4 质控任务创建与执行流程(真实质控)

┌─────────────────┐
│ 用户创建质控任务│
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ 步骤1:基本信息     │
│ - 任务名称          │
│ - 选择质控标准      │
│ - 选择机构          │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 步骤2:数据范围     │
│ 选择方式:          │
│ ○ 时间范围          │
│ ○ 患者列表          │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 预览检查数据        │
│ 显示将要质控的      │
│ 检查数量(≤100)   │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 步骤3:确认提交     │
│ 显示任务摘要        │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 后端创建任务记录    │
│ 状态:待执行        │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 异步执行任务        │
│ (@Async线程池)      │
└────────┬────────────┘
         │
         ▼
┌─────────────────────────┐
│ 执行引擎流程:          │
│ 1. 更新任务状态为执行中 │
│ 2. 查询数据范围内的检查 │
│ 3. 获取质控标准配置     │
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 遍历每条检查数据        │
│ FOR EACH study:         │
└────────┬────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 应用质控标准中的所有因子   │
│ FOR EACH factor:          │
│   1. 获取检查数据中的字段  │
│   2. 应用规则引擎判断      │
│   3. 记录是否合格          │
│   4. 计算得分(权重)      │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 计算总分                  │
│ score = Σ(因子得分×权重)  │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 判断合格/不合格            │
│ isPass = (score >= 80)    │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 保存质控结果              │
│ - qc_result表             │
│ - 记录不合格因子详情      │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 更新任务进度              │
│ - 已检数量+1              │
│ - 合格数/不合格数         │
│ - 进度写入Redis           │
│   (每10条更新一次)         │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 循环结束                  │
│ 所有检查都已质控完成      │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 更新任务状态为已完成      │
│ - complete_time           │
│ - total_count             │
│ - pass_count              │
│ - fail_count              │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 前端轮询获取最新进度      │
│ (每2秒查询一次任务状态)    │
└───────────────────────────┘

2.5 质控任务创建与执行流程(预制结果)

┌─────────────────┐
│ 用户创建质控任务│
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ 步骤1:基本信息     │
│ - 任务名称          │
│ - 选择质控标准      │
│ - 选择机构          │
│ ☑ 使用预制结果     │ ← 勾选此选项
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 选择预制结果模板    │
│ 下拉选择:          │
│ "CT质控演示模板"    │
│ (总数100,合格75)    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 步骤2:数据范围     │
│ (同真实质控)        │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 预览预制结果统计    │
│ - 总数:100         │
│ - 合格:75 (75%)    │
│ - 不合格:25 (25%)  │
│ - 不合格因子分布    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 提交任务            │
│ 标记为预制任务      │
└────────┬────────────┘
         │
         ▼
┌─────────────────────────┐
│ 后端模拟执行流程        │
│ 1. 创建任务,状态=执行中│
│ 2. 查询数据范围内的检查 │
│ 3. 延迟3-5秒(模拟耗时)│
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 根据预制模板生成结果    │
│ 1. 随机抽取100条检查    │
│ 2. 前75条生成合格结果   │
│    - 随机分数85-100     │
│    - 所有因子都合格     │
│ 3. 后25条生成不合格结果 │
│    - 随机分数50-79      │
│    - 按模板分配不合格因子│
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 保存到qc_result表       │
│ (与真实结果表结构一致)  │
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 更新任务状态为已完成    │
│ 前端轮询获取完成状态    │
└─────────────────────────┘

2.6 查看质控结果流程

┌─────────────────┐
│ 用户进入质控    │
│ 结果列表页面    │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ 筛选条件            │
│ - 任务名称          │
│ - 患者姓名/ID       │
│ - 合格状态          │
│ - 日期范围          │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 结果列表(分页)    │
│ 显示:              │
│ - 患者信息          │
│ - 检查信息          │
│ - 质控分数          │
│ - 合格状态          │
│ - 操作按钮          │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ 点击"查看详情"      │
└────────┬────────────┘
         │
         ▼
┌───────────────────────────┐
│ 结果详情页面              │
│ ┌─────────────────────┐   │
│ │ 基本信息区           │   │
│ │ - 患者、检查、任务   │   │
│ └─────────────────────┘   │
│ ┌─────────────────────┐   │
│ │ 评分卡片             │   │
│ │ - 总分/合格分/实际分 │   │
│ │ - 合格状态(标签)   │   │
│ └─────────────────────┘   │
│ ┌─────────────────────┐   │
│ │ 因子检查结果表格     │   │
│ │ 不合格项红色高亮     │   │
│ └─────────────────────┘   │
│ ┌─────────────────────┐   │
│ │ [查看影像] 按钮      │   │
│ └─────────────────────┘   │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 点击"查看影像"            │
│ 打开弹窗/新页面           │
└────────┬──────────────────┘
         │
         ▼
┌───────────────────────────┐
│ 内嵌阅片器(iframe)      │
│ URL构建:                 │
│ baseUrl + studyId + 其他参数│
│ baseUrl从配置文件读取      │
└───────────────────────────┘

2.7 数据权限过滤流程

┌─────────────────┐
│ 用户登录        │
│ is_admin=0      │ (普通用户)
└────────┬────────┘
         │
         ▼
┌─────────────────────────┐
│ 查询用户关联的机构列表  │
│ SELECT institution_id   │
│ FROM sys_user_institution│
│ WHERE user_id = ?       │
│ 结果:[101, 102, 103]   │
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 存入ThreadLocal         │
│ institutionIds = [...]  │
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│ 执行业务查询            │
│ 如:查询患者列表        │
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────────┐
│ MyBatis拦截器自动拼接条件   │
│ SELECT * FROM patient_info  │
│ WHERE ...                   │
│ AND institution_id IN       │
│   (101, 102, 103)           │
└─────────────────────────────┘

三、数据库设计

3.1 核心表清单(13张表)

系统管理(5张)

  1. sys_user - 系统用户表
  2. sys_institution - 机构表
  3. sys_user_institution - 用户机构关联表
  4. sys_menu - 菜单表
  5. sys_user_menu - 用户菜单权限表

患者检查(2张)

  1. patient_info - 患者信息表
  2. study_info - 检查信息表

质控核心(5张)

  1. qc_factor - 质控因子表
  2. qc_standard - 质控标准表
  3. qc_standard_factor - 质控标准因子关联表
  4. qc_task - 质控任务表
  5. qc_result - 质控结果表

演示功能(1张)

  1. qc_preset_template - 预制结果模板表

四、技术实现要点

4.1 数据权限实现

方案:MyBatis-Plus 数据权限插件 + ThreadLocal

// 拦截器配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new DataPermissionInterceptor());
    return interceptor;
}

// 数据权限处理器
public class DataPermissionHandler implements DataPermissionHandler {
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        // 从ThreadLocal获取用户机构列表
        List<String> institutionIds = DataScopeContext.getInstitutionIds();
        if (管理员) {
            return where; // 不过滤
        }
        // 拼接 AND institution_id IN (...)
    }
}

4.2 规则引擎实现

规则格式(JSON)

{
  "operator": ">=",     // 运算符:>= <= == != in notEmpty notNull contains
  "value": 10,          // 期望值
  "dataType": "number", // 数据类型:string/number/date/boolean
  "unit": "小时"        // 单位(可选,用于时效性)
}

规则引擎核心代码

public class QcRuleEngine {
    public QcCheckResult check(String factorCode, Object actualValue, String ruleJson) {
        QcRule rule = JSON.parseObject(ruleJson, QcRule.class);

        switch (rule.getOperator()) {
            case ">=":
                return compareNumber(actualValue, rule.getValue(), ">=");
            case "notEmpty":
                return checkNotEmpty(actualValue);
            case "in":
                return checkIn(actualValue, rule.getValue());
            // ... 其他运算符
        }
    }
}

4.3 任务异步执行

@Service
public class QcExecuteService {

    @Async("qcTaskExecutor")
    public void executeTask(String taskId) {
        // 1. 更新任务状态为执行中
        // 2. 查询数据范围内的检查列表
        // 3. 获取质控标准配置
        // 4. 遍历执行质控
        for (Study study : studyList) {
            QcResult result = checkStudy(study, standard);
            qcResultMapper.insert(result);

            // 每10条更新一次进度
            if (count % 10 == 0) {
                updateProgress(taskId, count, totalCount);
            }
        }
        // 5. 更新任务状态为已完成
    }
}

线程池配置

@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Bean("qcTaskExecutor")
    public Executor qcTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("qc-task-");
        return executor;
    }
}

4.4 前端轮询机制

// 任务详情页面轮询
const pollTaskStatus = () => {
  const timer = setInterval(async () => {
    const res = await getTaskDetail(taskId)

    if (res.data.status === 2) { // 已完成
      clearInterval(timer)
      ElMessage.success('质控任务执行完成!')
      loadResults() // 加载结果
    } else if (res.data.status === 3) { // 失败
      clearInterval(timer)
      ElMessage.error('任务执行失败')
    } else {
      // 更新进度条
      progress.value = res.data.progress
    }
  }, 2000) // 每2秒轮询一次
}

4.5 内嵌阅片器实现

前端配置文件(.env.production)

VITE_VIEWER_BASE_URL=https://ppacsview.pacsonline.cn/#/pc
VITE_VIEWER_STUDY_URL=https%3A%2F%2Fquery.pacsonline.cn%2Fquery%2F%3Faddress%3D

阅片器组件(ViewerDialog.vue)

<template>
  <el-dialog
    v-model="visible"
    title="影像查看"
    width="90%"
    fullscreen
  >
    <iframe
      :src="viewerUrl"
      style="width:100%; height:80vh; border:none;"
    />
  </el-dialog>
</template>

<script setup lang="ts">
const viewerUrl = computed(() => {
  const baseUrl = import.meta.env.VITE_VIEWER_BASE_URL
  const studyUrl = import.meta.env.VITE_VIEWER_STUDY_URL
  return `${baseUrl}?studyurl=${studyUrl}&study_id=${props.studyId}&node_type=1&version=V1.2.0.0`
})
</script>

4.6 预制结果生成算法

public void generatePresetResults(QcTask task, PresetTemplate template) {
    List<Study> studies = getStudiesByRange(task);

    // 洗牌随机化
    Collections.shuffle(studies);

    int totalCount = template.getTotalCount();
    int passCount = template.getPassCount();

    // 生成合格结果
    for (int i = 0; i < passCount; i++) {
        QcResult result = new QcResult();
        result.setStudyId(studies.get(i).getStudyId());
        result.setActualScore(RandomUtils.nextInt(85, 101)); // 85-100分
        result.setIsPass(1);
        result.setFailFactors("[]"); // 空数组
        qcResultMapper.insert(result);
    }

    // 生成不合格结果
    List<FactorDistribution> distributions = template.getDistributions();
    for (int i = passCount; i < totalCount; i++) {
        // 随机选择不合格因子
        FactorDistribution factor = distributions.get(i % distributions.size());

        QcResult result = new QcResult();
        result.setStudyId(studies.get(i).getStudyId());
        result.setActualScore(RandomUtils.nextInt(50, 80)); // 50-79分
        result.setIsPass(0);

        // 构造不合格因子JSON
        List<FailFactor> failFactors = Arrays.asList(
            new FailFactor(factor.getFactorId(), factor.getFactorName(),
                          "实际值", "期望值")
        );
        result.setFailFactors(JSON.toJSONString(failFactors));

        qcResultMapper.insert(result);
    }
}

五、前后端项目结构

5.1 后端项目结构

QConline/
├── doc/
│   ├── sql/
│   │   ├── init.sql                 # 初始化建表
│   │   ├── menu_data.sql            # 菜单初始数据
│   │   └── qc_factor_data.sql       # 质控因子初始数据
│   ├── 开发大纲.md
│   └── API文档.md
├── src/main/java/com/zskk/qconline/
│   ├── modules/
│   │   ├── system/                  # 系统管理
│   │   ├── patient/                 # 患者管理
│   │   ├── qc/                      # 质控管理
│   │   └── pacs/                    # PACS接口
│   ├── security/                    # 认证授权
│   ├── config/                      # 配置类
│   ├── component/                   # 通用组件
│   └── utils/                       # 工具类
├── src/main/resources/
│   ├── mapper/                      # MyBatis XML
│   ├── application.yml
│   └── logback-spring.xml
├── Dockerfile
└── pom.xml

5.2 前端项目结构

qconline-web/
├── public/
├── src/
│   ├── api/                         # API接口
│   ├── assets/                      # 静态资源
│   ├── components/                  # 全局组件
│   │   └── ViewerDialog/            # 阅片器组件
│   ├── layout/                      # 布局组件
│   ├── router/                      # 路由配置
│   ├── stores/                      # Pinia状态管理
│   ├── utils/                       # 工具类
│   │   ├── request.ts               # axios封装
│   │   └── auth.ts                  # token处理
│   ├── views/                       # 页面组件
│   │   ├── login/
│   │   ├── dashboard/
│   │   ├── system/
│   │   ├── patient/
│   │   └── qc/
│   ├── App.vue
│   └── main.ts
├── .env.development                 # 开发环境配置
├── .env.production                  # 生产环境配置
├── Dockerfile
├── nginx.conf                       # Nginx配置
├── package.json
└── vite.config.ts

六、Docker部署方案

6.1 项目部署架构

┌────────────────────────────────┐
│      服务器(单机部署)         │
│                                │
│  ┌──────────────────────────┐  │
│  │   Nginx (前端静态资源)    │  │
│  │   端口:80               │  │
│  └──────────┬───────────────┘  │
│             │                  │
│  ┌──────────▼───────────────┐  │
│  │   Spring Boot (后端)     │  │
│  │   端口:8080             │  │
│  └──────────┬───────────────┘  │
│             │                  │
│  ┌──────────▼───────────────┐  │
│  │   MySQL                  │  │
│  │   端口:3306             │  │
│  └──────────────────────────┘  │
│                                │
│  ┌──────────────────────────┐  │
│  │   Redis                  │  │
│  │   端口:6379             │  │
│  └──────────────────────────┘  │
└────────────────────────────────┘

6.2 Docker Compose配置

docker-compose.yml

version: '3.8'

services:
  # MySQL数据库
  mysql:
    image: mysql:8.0
    container_name: qconline-mysql
    environment:
      MYSQL_ROOT_PASSWORD: your_password
      MYSQL_DATABASE: qconline
    ports:
      - "3306:3306"
    volumes:
      - ./mysql-data:/var/lib/mysql
      - ./doc/sql:/docker-entrypoint-initdb.d
    networks:
      - qconline-net

  # Redis缓存
  redis:
    image: redis:7-alpine
    container_name: qconline-redis
    ports:
      - "6379:6379"
    networks:
      - qconline-net

  # 后端服务
  backend:
    build:
      context: ./QConline
      dockerfile: Dockerfile
    container_name: qconline-backend
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      MYSQL_HOST: mysql
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis
    networks:
      - qconline-net

  # 前端服务
  frontend:
    build:
      context: ./qconline-web
      dockerfile: Dockerfile
    container_name: qconline-frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - qconline-net

networks:
  qconline-net:
    driver: bridge

6.3 后端Dockerfile

QConline/Dockerfile

FROM openjdk:11-jre-slim

WORKDIR /app

COPY target/qconline-*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "app.jar"]

6.4 前端Dockerfile

qconline-web/Dockerfile

# 构建阶段
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

nginx.conf

server {
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://qconline-backend:8080/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

七、开发计划与里程碑

阶段1:基础框架(2天)

后端

  • Spring Security + JWT 已完成
  • 数据库建表SQL(13张表)
  • MyBatis-Plus代码生成
  • 登录/登出接口完善
  • 数据权限拦截器

前端

  • 创建Vue 3项目
  • 配置Element Plus
  • 主布局搭建
  • 登录页面
  • axios拦截器
  • 路由守卫

阶段2:系统管理(3天)

  • 用户管理(CRUD + 权限配置)
  • 机构管理(树形结构)
  • 菜单权限(树形勾选)
  • 数据权限测试

阶段3:患者管理 + PACS对接(2天)

  • 患者列表
  • 检查列表
  • PACS推送接口
  • 内嵌阅片器组件
  • 测试阅片器调用

阶段4:质控配置(3天)

  • 质控因子管理
  • 质控标准管理
  • 标准-因子配置(拖拽、权重)
  • 规则引擎开发

阶段5:质控任务与结果(4天)

  • 任务创建流程(3步向导)
  • 任务列表与详情
  • 任务执行引擎(异步)
  • 前端轮询进度
  • 结果列表与详情
  • 统计报表

阶段6:预制结果功能(1天)

  • 预制模板管理
  • 预制结果生成算法
  • 模拟执行流程

阶段7:Docker部署(1天)

  • 编写Dockerfile
  • Docker Compose配置
  • 部署测试

阶段8:测试与优化(2天)

  • 功能测试
  • 性能优化
  • Bug修复
  • 文档完善

总计:18天


八、初始化数据

8.1 管理员账号

INSERT INTO sys_user (username, password, realname, is_admin, status)
VALUES ('admin', '{加密后的密码}', '系统管理员', 1, 1);

8.2 菜单数据

系统管理
  - 用户管理
  - 机构管理
  - 菜单权限
患者管理
  - 患者列表
  - 检查列表
质控管理
  - 质控因子
  - 质控标准
  - 质控任务
  - 质控结果
统计分析
  - 质控概览

8.3 质控因子初始数据

(参考前文因子列表,共12个内置因子)


九、待确认事项

需要您最终确认:

  1. 数据库命名规范

    • 数据库名称:qconline
    • 表名前缀:无前缀 / 统一前缀 ?
  2. 阅片器参数

    • 除了 studyId,还需要哪些参数?
    • studyurl 参数的完整值是什么?
  3. 质控因子数据来源

    • 检查数据中的哪些字段对应质控因子?
    • 例如:"体位" 字段对应检查表的哪个字段?
  4. 预制结果

    • 是否需要支持多个预制模板?
    • 演示时主要展示哪种场景(高合格率/低合格率)?
  5. AI质控

    • 是否有具体的AI模型需要集成?
    • 还是暂时预留接口?

请您确认以上开发大纲,我将根据您的反馈开始开发!