From 353bec2a3c36e97cb3b74edc884da1cf7e001fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Fri, 12 Jun 2026 23:02:08 +0800 Subject: [PATCH] =?UTF-8?q?docs(bug):=20=E8=AF=B8=E8=91=9B=E4=BA=AE?= =?UTF-8?q?=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A=20Bug=20#743?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/bugs/BUG_743_ANALYSIS.md | 162 ++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 MD/bugs/BUG_743_ANALYSIS.md diff --git a/MD/bugs/BUG_743_ANALYSIS.md b/MD/bugs/BUG_743_ANALYSIS.md new file mode 100644 index 000000000..6cd68eb02 --- /dev/null +++ b/MD/bugs/BUG_743_ANALYSIS.md @@ -0,0 +1,162 @@ +# Bug #743 诸葛亮分析报告 + +> **文档类型**: Bug分析 +> **分析时间**: 2026-06-12 23:02:08 +> **分析模型**: mimo-v2.5 (LLM深度分析) + +--- + +## 基本信息 +- **Bug #**: 743 +- **标题**: 【急诊管理】急诊抢救模块下的开始抢救只填分诊id会出现报错null value in column "patient_id" of relation "emergency_rescue" violates not-null constraint +- **模块**: 会诊管理 +- **提出人**: 王栩坤 + +--- + +Now I have a complete picture. Let me produce the analysis. + +--- + +### 一、Bug 理解 + +**禅道 Bug #743 原文:** +> **标题**:【急诊管理】急诊抢救模块下的开始抢救只填分诊id会出现报错 null value in column "patient_id" of relation "emergency_rescue" violates not-null constraint +> +> **重现步骤**:登录内科医生1的账号 doctor1/123456 → 进入急诊管理 → 急诊抢救 → 点击开始抢救 → 只填写分诊ID → 点击确认 +> +> **结果**:报错 `PSQLException: ERROR: null value in column "patient_id" of relation "emergency_rescue" violates not-null constraint` +> +> **期望**:不会出现报错,能够正常实现,或者显示"患者ID的字段未填入,请填入患者ID" + +**附图分析**:截图中表单显示"患者ID"输入框为空(无值),"分诊ID"填入了 `1`,点击确定后页面顶部弹出红色数据库报错横幅。INSERT SQL 为 `INSERT INTO emergency_rescue (id, triage_id, rescue_start, chief_doctor, rescue_team, procedures, medications, create_by, create_time, tenant_id)` —— 完全缺少 `patient_id` 字段。 + +**综合总结**:用户在"开始抢救"弹窗中只填写了分诊ID、未填患者ID就提交,后端没有做参数校验就直接调用 `rescueService.save()`,导致 MyBatis-Plus 生成的 INSERT 语句不含 `patient_id`,PostgreSQL 的 NOT NULL 约束拒绝插入,系统将底层数据库报错直接暴露给用户。正确行为应该是:后端校验 `patient_id` 必填(或从分诊记录自动关联患者ID),前端也应加必填校验。 + +--- + +### 二、根因分析 + +**根因:`addRescue` 方法零校验 + 缺少患者ID自动关联逻辑** + +调用链路:前端 `submitForm()` → POST `/emergency/rescue/add` → `EmergencyController.addRescue()` → `rescueService.save(rescue)` → DB INSERT + +| 问题层 | 具体问题 | +|--------|---------| +| **后端 Controller** | `addRescue()` 方法(第167行)直接 `rescueService.save(rescue)`,**无任何参数校验**,`patientId` 为 null 时仍然保存 | +| **后端 Controller** | 虽然有 `triageId` → `EmergencyTriage` 的联动逻辑(第172行),但这段逻辑在 `save` **之后**,且只更新分诊状态,**没有从 triage 反填 patientId** | +| **前端 Form** | `el-form` 无 `:rules` 校验规则,`el-form-item` 无 `required` 标记,患者ID可为空直接提交 | +| **数据库** | `emergency_rescue.patient_id` 有 NOT NULL 约束,MyBatis-Plus 对 null 字段不生成 INSERT 列,导致约束违反 | + +**涉及文件**: +- `healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emergency/controller/EmergencyController.java` — 第165-180行 `addRescue` 方法 +- `healthlink-his-ui/src/views/emergency/rescue/index.vue` — 表单模板 + `submitForm` 函数 +- `healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emergency/domain/EmergencyTriage.java` — `patientId` 字段(用于反填) + +--- + +### 三、修复方案 + +#### 修复1:后端 Controller — 增加校验 + 从分诊记录自动关联 patientId + +**文件**: `EmergencyController.java` 第165-180行 + +**修改内容**:在 `save` 前增加校验和自动填充逻辑: + +```java +@PostMapping("/rescue/add") +@Transactional(rollbackFor = Exception.class) +public R addRescue(@RequestBody EmergencyRescue rescue) { + // 从分诊记录自动关联患者ID(如果前端未传 patientId) + if (rescue.getPatientId() == null && rescue.getTriageId() != null) { + EmergencyTriage triage = triageService.getById(rescue.getTriageId()); + if (triage == null) { + return R.fail("分诊记录不存在,请检查分诊ID"); + } + if (triage.getPatientId() == null) { + return R.fail("关联的分诊记录中患者ID为空,无法创建抢救记录"); + } + rescue.setPatientId(triage.getPatientId()); + } + // 最终校验 patientId 必填 + if (rescue.getPatientId() == null) { + return R.fail("患者ID不能为空,请填写患者ID或提供有效的分诊ID"); + } + rescue.setRescueStart(new Date()); + rescue.setCreateTime(new Date()); + rescueService.save(rescue); + // 联动更新分诊状态 + if (rescue.getTriageId() != null) { + EmergencyTriage triage = triageService.getById(rescue.getTriageId()); + if (triage != null) { + triage.setStatus("IN_TREATMENT"); + triageService.updateById(triage); + } + } + return R.ok(rescue); +} +``` + +**核心逻辑**: +1. 如果前端没传 `patientId` 但传了 `triageId`,从 `EmergencyTriage` 记录中自动取 `patientId` 填入 +2. 如果两个都没有 → 返回友好错误信息 `R.fail("患者ID不能为空...")` +3. 确保 `patientId` 在 `save` 前必定有值 + +#### 修复2:前端表单 — 增加必填校验规则 + +**文件**: `healthlink-his-ui/src/views/emergency/rescue/index.vue` + +**修改内容**: + +```vue + + + + + + + + +``` + +```js +// 4. 在 script setup 中添加: +import {ref, reactive, onMounted} from 'vue' +const formRef = ref(null) +const formRules = reactive({ + patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }], + triageId: [{ required: true, message: '请输入分诊ID', trigger: 'blur' }] +}) + +// 5. 修改 submitForm 加表单校验: +const submitForm = async () => { + await formRef.value.validate() + if (isEdit.value) { + await (await import('./api')).update(form.value || {}) + } else { + await add(form.value) + } + ElMessage.success('操作成功') + dlgVisible.value = false + loadData() +} +``` + +**注意**:由于后端已做了"只填分诊ID自动反填患者ID"的逻辑,前端可以将 `patientId` 改为非必填(允许用户只填分诊ID即可),但建议至少保留 `triageId` 必填。如果希望更严格,则两个都必填。 + +--- + +### 四、路由决策 + +**FIXER**: `guanyu`(后端)+ `zhaoyun`(前端) + +**REASON**: 核心修复在后端 `EmergencyController.addRescue()` 方法——增加参数校验和从分诊记录自动关联 `patientId`,这是 bug 的根因所在,由 **guanyu** 负责。前端 `rescue/index.vue` 的表单校验规则增强为辅助防御层,由 **zhaoyun** 负责。两个修改互相独立,可并行进行。 + +--- + +## 路由决策 +- **FIXER_ID**: guanyu +- **修复 Agent**: guanyu(后端) +- **原因**: LLM 分析决策 + +> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。