# 修改 /api/dicom/query 接口支持按检查项目查询对应部位 ## 需求说明 当查询接口传入 `exam_item_name` 参数时(如"胸部CT"),返回的 `partExamined` 应该是该检查项目对应的部位(如"CHEST"),而不是拼接后的多部位(如"CHEST+KNEE")。 --- ## 修改内容 ### 文件:`StudyQueryServiceImpl.java` #### 修改1:传递 examItemName 参数(第88行) ```java // 修改前 StudyQueryResponseVO.StudyDetailVO studyDetail = buildStudyDetail( studyInfo, patientInfo, institution, seriesList); // 修改后 StudyQueryResponseVO.StudyDetailVO studyDetail = buildStudyDetail( studyInfo, patientInfo, institution, seriesList, examItemName); ``` #### 修改2:动态决定 partExamined 的值(第180-190行) ```java // ⭐ 新增:根据examItemName参数决定partExamined的值 if (examItemName != null && !examItemName.isEmpty() && seriesList != null && !seriesList.isEmpty()) { // 如果传入了exam_item_name查询参数,从series_info中提取对应的部位 String partExamined = extractPartExaminedFromSeries(seriesList); detail.setPartExamined(partExamined); log.info("使用查询参数对应的部位: examItemName={}, partExamined={}", examItemName, partExamined); } else { // 没有传入exam_item_name,使用study_info中的bodyPart(可能是拼接的) detail.setPartExamined(studyInfo.getBodyPart()); log.info("使用study_info的部位: bodyPart={}", studyInfo.getBodyPart()); } ``` #### 修改3:新增 extractPartExaminedFromSeries 方法(第310-338行) ```java /** * 从series_info列表中提取对应的部位 * 因为已经通过exam_item_name过滤,所以所有片子的部位应该相同 * 取第一个片子的部位即可 */ private String extractPartExaminedFromSeries(List seriesList) { if (seriesList == null || seriesList.isEmpty()) { log.warn("series_list为空,无法提取部位"); return null; } // 从第一个片子中提取部位 String partExamined = seriesList.get(0).getBodyPartExamined(); // 可选:验证所有片子的部位是否一致(用于调试) long uniquePartCount = seriesList.stream() .map(SeriesInfo::getBodyPartExamined) .distinct() .count(); if (uniquePartCount > 1) { log.warn("检测到series_list中存在多个不同的部位: count={}, parts={}", uniquePartCount, seriesList.stream() .map(SeriesInfo::getBodyPartExamined) .distinct() .collect(java.util.stream.Collectors.toList())); } log.info("从series_info中提取部位: partExamined={}, seriesCount={}", partExamined, seriesList.size()); return partExamined; } ``` --- ## 功能说明 ### 查询逻辑 #### 场景1:不带 exam_item_name 参数查询 **请求:** ```http GET /api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001 ``` **返回:** ```json { "partExamined": "CHEST+KNEE", // study_info 中的拼接值 "studyDescribe": "胸部CT+膝关节CT" } ``` **说明:** 返回所有部位的拼接值 --- #### 场景2:带 exam_item_name 参数查询(胸部CT) **请求:** ```http GET /api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001&exam_item_name=胸部CT ``` **处理流程:** 1. 查询 series_info,过滤条件:`exam_item_name = '胸部CT'` 2. 得到的 series_list 中所有片子的 `exam_item_name` 都是 "胸部CT" 3. 从第一个片子提取 `body_part_examined`,假设是 "CHEST" 4. 返回 `partExamined = "CHEST"` **返回:** ```json { "partExamined": "CHEST", // ⭐ 从 series_info 中提取的对应部位 "studyDescribe": "胸部CT+膝关节CT" } ``` **说明:** 只返回查询参数对应的部位 --- #### 场景3:带 exam_item_name 参数查询(膝关节CT) **请求:** ```http GET /api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001&exam_item_name=膝关节CT ``` **返回:** ```json { "partExamined": "KNEE", // ⭐ 从 series_info 中提取的对应部位 "studyDescribe": "胸部CT+膝关节CT" } ``` --- ## 数据表关系 ### study_info 表(检查级别) | study_id | body_part | exam_item_name | |----------|-----------|----------------| | STY001 | CHEST+KNEE | 胸部CT+膝关节CT | ### series_info 表(片子级别) | instance_number | body_part_examined | exam_item_name | |-----------------|-------------------|----------------| | 1 | CHEST | 胸部CT | | 2 | CHEST | 胸部CT | | 3 | KNEE | 膝关节CT | | 4 | KNEE | 膝关节CT | --- ## 查询逻辑详解 ### 不带 exam_item_name 查询 ```sql SELECT * FROM series_info WHERE study_id = 'STY001'; -- 结果:返回所有片子(胸部2张+膝关节2张) ``` **返回值:** ```json { "partExamined": "CHEST+KNEE" // study_info.body_part(拼接值) } ``` --- ### 带 exam_item_name=胸部CT 查询 ```sql SELECT * FROM series_info WHERE study_id = 'STY001' AND exam_item_name = '胸部CT'; -- 结果:只返回胸部片子(2张) ``` **提取逻辑:** 1. series_list 有2条记录 2. 所有记录的 `body_part_examined` 都是 "CHEST" 3. 从第一条记录提取:`partExamined = "CHEST"` **返回值:** ```json { "partExamined": "CHEST" // ⭐ 从 series_info 提取 } ``` --- ## 日志输出 ### 不带 exam_item_name ```log INFO - 使用study_info的部位: bodyPart=CHEST+KNEE ``` ### 带 exam_item_name=胸部CT ```log INFO - 使用查询参数对应的部位: examItemName=胸部CT, partExamined=CHEST INFO - 从series_info中提取部位: partExamined=CHEST, seriesCount=2 ``` ### 检测到多个不同部位(警告) ```log WARN - 检测到series_list中存在多个不同的部位: count=2, parts=[CHEST, KNEE] ``` 这种情况理论上不应该出现,因为已经通过 `exam_item_name` 过滤了。 --- ## 测试用例 ### 测试1:查询所有数据(不带参数) ```bash curl -X GET "http://localhost:8080/api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001" ``` **预期:** ```json { "partExamined": "CHEST+KNEE", "studyDescribe": "胸部CT+膝关节CT" } ``` --- ### 测试2:查询胸部CT ```bash curl -X GET "http://localhost:8080/api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001&exam_item_name=胸部CT" ``` **预期:** ```json { "partExamined": "CHEST", "studyDescribe": "胸部CT+膝关节CT" } ``` --- ### 测试3:查询膝关节CT ```bash curl -X GET "http://localhost:8080/api/dicom/query?study_instance_uid=1.2.3...&institution_id=INST001&exam_item_name=膝关节CT" ``` **预期:** ```json { "partExamined": "KNEE", "studyDescribe": "胸部CT+膝关节CT" } ``` --- ## 总结 ### 核心改进 1. ✅ 修改 `buildStudyDetail` 方法签名,增加 `examItemName` 参数 2. ✅ 动态决定 `partExamined` 的来源: - 有 exam_item_name 参数:从 series_info 提取 - 无 exam_item_name 参数:使用 study_info.body_part 3. ✅ 新增 `extractPartExaminedFromSeries` 方法 4. ✅ 添加详细日志输出 ### 数据一致性 - `partExamined` 与查询参数 `exam_item_name` 对应 - `studyDescribe` 保持完整(仍然是拼接值) - `seriesList` 只包含查询参数对应的序列 ### 效果 - **不传参数:** 返回拼接的部位(如 "CHEST+KNEE") - **传参数:** 返回对应的单个部位(如 "CHEST" 或 "KNEE") **重启应用后即可生效!** 🎯