Files
his/MD/bugs/BUG_743_ANALYSIS.md

163 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
<!-- 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">
```
```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 分析决策
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。