7.0 KiB
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 前增加校验和自动填充逻辑:
@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);
}
核心逻辑:
- 如果前端没传
patientId但传了triageId,从EmergencyTriage记录中自动取patientId填入 - 如果两个都没有 → 返回友好错误信息
R.fail("患者ID不能为空...") - 确保
patientId在save前必定有值
修复2:前端表单 — 增加必填校验规则
文件: healthlink-his-ui/src/views/emergency/rescue/index.vue
修改内容:
<!-- 1. el-form 加 :rules -->
<el-form :model="form" label-width="100px" :rules="formRules" ref="formRef">
<!-- 2. 患者ID 加 prop -->
<el-form-item label="患者ID" prop="patientId">
<!-- 3. 分诊ID 加 prop -->
<el-form-item label="分诊ID" prop="triageId">
// 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 分析决策
⚠️ 修复人员请先验证以上分析是否正确,再执行修复。