diff --git a/MD/bugs/BUG_757_ANALYSIS.md b/MD/bugs/BUG_757_ANALYSIS.md new file mode 100644 index 000000000..16b9be083 --- /dev/null +++ b/MD/bugs/BUG_757_ANALYSIS.md @@ -0,0 +1,164 @@ +# Bug #757 诸葛亮分析报告 + +> **文档类型**: Bug分析 +> **分析时间**: 2026-06-12 14:58:17 +> **分析模型**: mimo-v2.5 (LLM深度分析) + +--- + +## 基本信息 +- **Bug #**: 757 +- **标题**: 【门诊医生工作站】中医诊断中没有相同的诊断和症候,但就无法新增中医诊断 +- **模块**: 门诊医生工作站 +- **提出人**: 王栩坤 + +--- + +I have enough information now to produce the analysis. Let me compile the findings. + +--- + +## 一、Bug 理解 + +用户在门诊医生站打开"中医诊断"对话框,选择中医诊断和中医证候后点击确定,**无法成功保存**。同时,已经保存过的中医诊断也无法正确回显诊断详情。期望:能正常选择诊断和证候、保存成功,并回显诊断详情数据。 + +## 二、根因分析 + +核心问题在 `tcmdiagnosisDialog.vue` 的 `submit()` 函数,它向后端发送的字段名与后端 DTO 完全不匹配: + +**前端发送的数据结构:** +```js +const diagnosisChildList = [{ + conditionCode: condition.value, // ← 字段名错误 + syndromeCode: syndrome.value, // ← 字段名错误 +}]; +``` + +**后端 `SaveDiagnosisChildParam` 期望的数据结构:** +- `definitionId` (Long) — 诊断定义 ID +- `ybNo` (String) — 医保编码 +- `syndromeGroupNo` (String) — 中医证候组号(用于关联病和证) +- `maindiseFlag` (Integer) — 主诊断标记 +- `diagSrtNo` (Integer) — 排序号 + +**具体根因列表:** + +| # | 问题 | 位置 | 影响 | +|---|------|------|------| +| 1 | `submit()` 发送 `conditionCode`/`syndromeCode`,后端接收不到 `definitionId` 和 `ybNo` | `tcmdiagnosisDialog.vue:submit()` | 保存时 Condition 记录的 `definitionId` 为 null,保存失败或数据损坏 | +| 2 | 没有传 `syndromeGroupNo` 来关联"病"和"证" | `tcmdiagnosisDialog.vue:submit()` | 后端无法将诊断和证候配对成一组 | +| 3 | `openDialog()` 获取下拉选项时用 `item.ybNo` 作为 value,但没有保存 `definitionId` | `tcmdiagnosisDialog.vue:openDialog()` | 丢失了关键的 `definitionId` | +| 4 | 没有传 `patientInfo` prop(dialog 用 `defineProps` 声明了但父组件可能未传) | `tcmdiagnosisDialog.vue` | `saveTcmDiagnosis` 请求中 `patientId`/`encounterId` 为 null | + +**可能涉及的文件:** +- `healthlink-his-ui/src/views/doctorstation/components/tcm/tcmdiagnosisDialog.vue` — 主要 Bug 所在 +- `healthlink-his-ui/src/views/doctorstation/components/api.js` — API 定义(无误) +- `healthlink-his-server/.../appservice/impl/DoctorStationChineseMedicalAppServiceImpl.java` — 后端 `saveTcmDiagnosis` 方法 + +## 三、修复方案 + +### 修复 1:`tcmdiagnosisDialog.vue` — `openDialog()` 方法 + +将下拉选项的 value 从 `ybNo` 改为同时保存 `definitionId`: + +```js +// 修改前 +conditionOptions.value = res.data.records.map((item) => ({ + value: item.ybNo, + label: item.name, +})); + +// 修改后 +conditionOptions.value = res.data.records.map((item) => ({ + value: item.id, // 用 definition ID 作为 value + ybNo: item.ybNo, // 保留医保编码 + label: item.name, +})); +syndromeOptions.value = res.data.records.map((item) => ({ + value: item.id, + ybNo: item.ybNo, + label: item.name, +})); +``` + +### 修复 2:`tcmdiagnosisDialog.vue` — `submit()` 方法 + +重写提交逻辑,匹配后端 `SaveDiagnosisChildParam` 的字段名: + +```js +function submit() { + if (!condition.value || !syndrome.value) { + proxy.$modal.msgWarning('请选择诊断和证候'); + return; + } + + // 找到选中的诊断和证候的完整信息 + const selectedCondition = conditionOptions.value.find(item => item.value === condition.value); + const selectedSyndrome = syndromeOptions.value.find(item => item.value === syndrome.value); + + // 生成证候组号(时间戳) + const syndromeGroupNo = 'TCM' + Date.now(); + + const diagnosisChildList = [ + { + definitionId: condition.value, // 中医诊断 definition ID + ybNo: selectedCondition?.ybNo, // 中医诊断医保编码 + syndromeGroupNo: syndromeGroupNo, + maindiseFlag: 1, // 主诊断标记 + diagSrtNo: 1, // 排序号(病) + }, + { + definitionId: syndrome.value, // 中医证候 definition ID + ybNo: selectedSyndrome?.ybNo, // 中医证候医保编码 + syndromeGroupNo: syndromeGroupNo, // 同一组号 + maindiseFlag: 0, + diagSrtNo: 2, // 排序号(证) + }, + ]; + + saveTcmDiagnosis({ + patientId: props.patientInfo.patientId, + encounterId: props.patientInfo.encounterId, + diagnosisChildList: diagnosisChildList, + }).then((res) => { + if (res.code === 200) { + proxy.$modal.msgSuccess('保存成功'); + emit('flush'); + close(); + } else { + proxy.$modal.msgError(res.msg || '保存失败'); + } + }).catch((error) => { + console.error('保存中医诊断失败:', error); + proxy.$modal.msgError('保存失败,请重试'); + }); +} +``` + +### 修复 3:确保父组件传递 `patientInfo` prop + +检查父组件(`tcmAdvice.vue` 或使用 `tcmdiagnosisDialog` 的页面)是否正确传递了 `patientInfo` prop。`tcmAdvice.vue` 已有 `patientInfo` prop 定义,所以如果是从 `tcmAdvice` 中使用该 dialog,需要通过 `:patientInfo="patientInfo"` 传递。 + +### 修复 4(可选):后端防御性处理 + +后端 `saveTcmDiagnosis` 方法可以增加对 `definitionId` 为空的校验,避免写入脏数据: + +```java +// 在 saveTcmDiagnosis 方法开头增加 +for (SaveDiagnosisChildParam param : diagnosisChildList) { + if (param.getDefinitionId() == null) { + return R.fail("诊断定义ID不能为空,请重新选择诊断"); + } +} +``` + +## 四、路由决策 + +**FIXER: zhaoyun**(前端开发) +**REASON:** Bug 根因在前端对话框组件 `tcmdiagnosisDialog.vue` 的字段映射错误和提交逻辑缺陷,需要修改 Vue 前端代码(openDialog 数据映射 + submit 参数构建 + 父组件 prop 传递),属于前端界面和 API 调用层修复。 + +--- + +## 路由决策 +- **修复 Agent**: zhaoyun +- **原因**: ** Bug 根因在前端对话框组件 `tcmdiagnosisDialog.vue` 的字段映射错误和提交逻辑缺陷,需要修改 Vue 前端代码(openDialog 数据映射 + submit 参数构建 + 父组件 prop 传递),属于前端界面和 API 调用层修复。