docs(bug): 诸葛亮分析报告 Bug #743

This commit is contained in:
2026-06-12 23:02:08 +08:00
parent 4e8a9ece41
commit 353bec2a3c

162
MD/bugs/BUG_743_ANALYSIS.md Normal file
View File

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