报告详情接口说明.md 11 KB

报告详情接口实现说明

接口信息

接口路径: POST /api/report/details

功能说明: 获取报告详细信息,用于报告书写页面。支持本地报告和远程报告两种模式。


实现逻辑

1. 参数验证

  • examId: 检查ID(必填)
  • isRemote: 报告类型,1=远程报告,2=本地报告(必填)
  • raId: 远程申请ID(远程报告时必填)

2. 查询报告基础信息(SQL实现)

本地报告查询

SELECT
    -- 检查信息(exams表)
    e.id AS exam_id, e.patient_num, e.name, e.sex, e.age, e.phone,
    e.exam_class, e.exam_datetime, e.exam_status, e.exam_project,

    -- 登记信息(register表)
    reg.body_part, reg.exam_sub_class,

    -- 报告信息(report表,type='1'表示本地报告)
    r.id AS report_id, r.description, r.impression, r.diagnose,
    r.report_result, r.qr_code, r.hr_status,

    -- 医生信息(只查询医生ID和姓名,签名信息单独批量查询)
    r.report_doctor_id, r.report_doctor_name, r.report_datetime,
    r.review_doctor_id, r.review_doctor_name, r.review_datetime,
    r.confirm_doctor_id, r.confirm_doctor_name, r.confirm_datetime
FROM exams e
LEFT JOIN register reg ON reg.exam_id = e.id
LEFT JOIN report r ON e.id = r.exam_id AND r.type = '1'
WHERE e.id = #{examId}

性能优化说明:移除了3次doctors表LEFT JOIN,改为在Service层批量查询医生签名信息,降低SQL复杂度。

远程报告查询

SELECT
    -- 远程申请信息(remote_application表)
    ra.exam_id, ra.patient_num, ra.name, ra.sex, ra.age,
    ra.report_status, ra.local_institution_name, ra.req_doctor_name,

    -- 报告信息(通过 remote_application_id 关联)
    r.id AS report_id, r.description, r.impression, r.report_result,

    -- 医生信息(只查询医生ID和姓名,签名信息单独批量查询)
    r.report_doctor_id, r.report_doctor_name, r.report_datetime,
    r.review_doctor_id, r.review_doctor_name, r.review_datetime,
    r.confirm_doctor_id, r.confirm_doctor_name, r.confirm_datetime
FROM remote_application ra
LEFT JOIN report r ON r.remote_application_id = ra.id
WHERE ra.id = #{raId}

性能优化说明:同本地报告,移除了3次doctors表LEFT JOIN。

3. 自动创建报告记录

如果本地报告不存在(report_id 为空),自动创建一条报告记录:

Report report = new Report();
report.setExamId(examId);
report.setType("1"); // 本地报告
report.setCreateTime(new Date());

// MyBatis-Plus 会自动生成 UUID(实体类配置了 @TableId(type = IdType.ASSIGN_UUID))
reportMapper.insert(report);

4. 补充机构信息

根据 institution_id 查询机构的报告标题、副标题、医院简介:

SysOrg org = sysOrgMapper.selectById(institutionId);
detail.setReportTitle(org.getReportTitle());
detail.setReportSubtitle(org.getReportSubtitle());
detail.setHrInfo(org.getHrInfo());

5. TODO: 获取 DICOM 文件路径

// TODO: 实现逻辑
// 1. 根据 study_id 查询 dcm_path 表
// 2. 检查 effective_date 是否过期
// 3. 根据 dcm_type 生成访问 URL
//    - dcm_type=1: 本地路径,直接返回
//    - dcm_type=2/4: 云存储,调用云存储SDK生成临时访问URL

6. 处理医生签名URL

根据每个医生的签名配置生成访问URL:

// 判断逻辑:
// 1. 是否启用签名?(is_use_autograph == 1)
// 2. 签名文件是否存在?(autograph != null)
// 3. 根据存储类型生成URL
//    - autograph_type=1: 本地存储,直接返回路径
//    - autograph_type=2: 云存储,生成临时访问URL

if (useAutograph == 1 && StringUtils.isNotBlank(autograph)) {
    if (autographType == 2) {
        // TODO: 调用云存储SDK生成签名URL
        url = cloudStorageService.generateSignedUrl(autograph);
    } else {
        url = autograph; // 本地路径
    }
} else {
    url = ""; // 不使用签名
}

7. 查询医生签名信息(批量查询优化)

性能优化: 不在SQL中LEFT JOIN 3次doctors表,改为批量查询:

// 收集需要查询的医生ID(报告医生、审核医生、确认医生)
List<String> doctorIds = new ArrayList<>();
if (reportDoctorId != null) doctorIds.add(reportDoctorId);
if (reviewDoctorId != null) doctorIds.add(reviewDoctorId);
if (confirmDoctorId != null) doctorIds.add(confirmDoctorId);

// 批量查询医生信息(1次数据库查询)
List<Doctors> doctors = doctorsMapper.selectBatchIds(doctorIds);

// 转换为Map,方便查找
Map<String, Doctors> doctorMap = doctors.stream()
    .collect(Collectors.toMap(Doctors::getId, d -> d));

// 填充各医生的签名信息
detail.setReportUseAutograph(doctorMap.get(reportDoctorId).getIsUseAutograph());
detail.setReportAutograph(doctorMap.get(reportDoctorId).getAutograph());
// ... 审核医生和确认医生同理

优化效果:

  • 原方案:SQL中LEFT JOIN 3次doctors表(1次主查询 = 1次DB访问)
  • 新方案:主查询 + 批量查询医生(1次主查询 + 1次批量查询 = 2次DB访问)
  • 对于2000万+数据的exams表,减少JOIN复杂度,提高查询稳定性

8. 查询医生所属机构

// 查询报告医生的机构信息
if (StringUtils.isNotBlank(reportDoctorId)) {
    Doctors doctor = doctorsMapper.selectById(reportDoctorId);
    detail.setIntroduce(doctor.getIntroduce());

    SysOrg org = sysOrgMapper.selectById(doctor.getOid());
    detail.setInstitution(org.getName());
}

9. 查询操作痕迹

report_record 表查询报告的所有修改历史:

SELECT
    rr.description,
    rr.impression,
    rr.report_result,
    rr.create_time AS handle_time,
    d.realname,
    rr.type
FROM report_record rr
LEFT JOIN doctors d ON rr.doctor_id = d.id
WHERE rr.report_id = #{reportId}
ORDER BY rr.create_time ASC

10. 加载草稿内容

如果报告未完成(exam_status != 9 且 report_status != 9),从 report_temp 表加载当前医生的草稿:

// 1. 判断报告是否已完成
if (examStatus == 9 || reportStatus == 9) {
    return; // 已完成,不加载草稿
}

// 2. 获取当前登录医生ID
// 从 Spring Security 上下文获取当前用户,然后查询医生ID
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
String sysUserId = loginUser.getUser().getId();

// 根据系统用户ID查询医生ID(doctors.uid = sys_user.id)
Doctors doctor = doctorsMapper.selectOne(
    new LambdaQueryWrapper<Doctors>()
        .eq(Doctors::getUid, sysUserId)
        .last("LIMIT 1")
);
String currentDoctorId = doctor.getId();

// 3. 查询草稿
ReportTemp draft = reportTempMapper.selectOne(
    new LambdaQueryWrapper<ReportTemp>()
        .eq(ReportTemp::getReportId, reportId)
        .eq(ReportTemp::getDoctorId, currentDoctorId)
        .orderByDesc(ReportTemp::getUpdateTime)
        .last("LIMIT 1")
);

// 4. 用草稿覆盖数据库内容
if (draft != null) {
    detail.setImpression(draft.getImpression());
    detail.setDescription(draft.getDescription());
}

数据表说明

表名 用途
exams 检查信息(患者、检查项目、状态等)
register 登记信息(检查部位、检查子类等)
report 报告信息(印象、描述、医生、时间等)
remote_application 远程申请信息
doctors 医生信息(姓名、签名、所属机构等)
sys_org 机构信息(报告标题、副标题等)
report_record 报告操作记录(历史痕迹)
report_temp 报告草稿(暂存内容)
dcm_path DICOM文件路径(TODO)

请求示例

本地报告

POST /api/report/details
{
  "examId": "exam-uuid-xxx",
  "isRemote": 2,
  "raId": ""
}

远程报告

POST /api/report/details
{
  "examId": "exam-uuid-xxx",
  "isRemote": 1,
  "raId": "ra-uuid-xxx"
}

返回数据示例

{
  "code": 200,
  "message": "查询成功",
  "data": {
    // 检查基础信息
    "examId": "exam-uuid-xxx",
    "isRemote": 2,
    "name": "张三",
    "sex": "男",
    "age": "45",
    "phone": "138****5678",
    "examClass": "CT",
    "examDatetime": "20251216103000",

    // 报告信息
    "reportId": "report-uuid-xxx",
    "description": "影像所见内容...",
    "impression": "诊断印象内容...",
    "reportResult": "1",

    // 医生信息
    "reportDoctorId": "doc-001",
    "reportDoctorName": "李医生",
    "reportDatetime": "2025-12-16 10:30:00",
    "reviewDoctorId": "doc-002",
    "reviewDoctorName": "王医生",
    "reviewDatetime": "2025-12-16 11:00:00",

    // 医生签名
    "reportUseAutograph": 1,
    "reportAutograph": "https://cloud.com/sign/doc001.png?sign=xxx",
    "reviewUseAutograph": 0,
    "reviewAutograph": "",

    // 机构信息
    "institutionId": "org-001",
    "institution": "北京协和医院",
    "introduce": "影像科主任医师,从事影像诊断30年",
    "reportTitle": "医学影像检查报告",
    "reportSubtitle": "北京协和医院放射科",

    // DICOM信息
    "studyId": "study-uuid-xxx&node_type=1",
    "dcmPath": "",  // TODO
    "dcmType": null,  // TODO

    // 操作痕迹
    "trace": [
      {
        "description": "初步描述...",
        "impression": "初步印象...",
        "reportResult": "1",
        "handleTime": "2025-12-16 10:30:00",
        "realname": "李医生",
        "type": 1
      },
      {
        "description": "修改后描述...",
        "impression": "修改后印象...",
        "reportResult": "1",
        "handleTime": "2025-12-16 11:00:00",
        "realname": "王医生",
        "type": 2
      }
    ]
  }
}

与原PHP项目的差异

功能点 PHP项目 Java项目
草稿存储 Redis缓存 数据库表 report_temp
医生签名查询 SQL中LEFT JOIN 3次doctors表 Service层批量查询(selectBatchIds
性能优化 SQL复杂JOIN 拆分为主查询 + 批量查询,适合大表(2000万+)
DICOM路径 查询 dcm_path + 生成URL TODO(待实现)
云存储URL uploadToCloud类 TODO(待实现)
事务处理 @Transactional注解
ID生成 手动调用UUID工具类 MyBatis-Plus自动生成

待实现功能(TODO)

  1. DICOM文件路径获取

    • 查询 dcm_path
    • 检查有效期
    • 根据存储类型生成访问URL
  2. 云存储签名URL生成

    • 集成云存储SDK
    • 实现临时访问URL生成(12小时有效期)

文件清单

新增文件

  • ReportDetailQueryVO.java - 查询参数VO
  • ReportDetailDTO.java - 返回结果DTO
  • ReportTraceDTO.java - 操作痕迹DTO
  • ReportTemp.java - 报告草稿实体
  • ReportRecord.java - 报告记录实体
  • ReportDetailMapper.java - Mapper接口
  • ReportDetailMapper.xml - SQL映射文件
  • ReportTempMapper.java - 草稿Mapper
  • ReportDetailService.java - 服务接口
  • ReportDetailServiceImpl.java - 服务实现

修改文件

  • ReportController.java - 添加 /details 接口

测试要点

  1. ✅ 本地报告查询(有报告记录)
  2. ✅ 本地报告查询(无报告记录,自动创建)
  3. ✅ 远程报告查询
  4. ✅ 草稿加载(报告未完成时)
  5. ✅ 草稿不加载(报告已完成时)
  6. ✅ 操作痕迹查询
  7. ✅ 医生签名处理(启用/未启用)
  8. ⏳ DICOM路径获取(待实现)
  9. ⏳ 云存储URL生成(待实现)