From c1162af5ea108d9d824df578d3b9610f837d2a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E4=BA=91?= <赵云@gentronhealth.com> Date: Sat, 16 May 2026 17:51:54 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#434:=20=E6=A0=B9=E5=9B=A0+?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=B9=E6=A1=88=E6=91=98=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ANALYSIS_434.md | 111 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/ANALYSIS_434.md b/ANALYSIS_434.md index cf1a659ff..6b68c9d9b 100644 --- a/ANALYSIS_434.md +++ b/ANALYSIS_434.md @@ -1,50 +1,87 @@ # Bug #434 分析报告 -## 根因分析 +## 问题:编辑弹窗中"切口类型"字段未正确回显数据 -### 问题:编辑弹窗中"切口类型"字段未正确回显数据 +## 数据流追踪 -**数据流追踪**: -1. 用户点击"编辑"→ 前端调用 `getSurgeryScheduleDetail(row.scheduleId)` -2. 后端 SQL: `cs.incision_level AS incisionLevel` -3. PostgreSQL 返回列名: `incisionlevel` (全小写) -4. MyBatis 尝试将 `incisionlevel` 映射到 `OpScheduleDto.incisionLevel` -5. 映射失败!→ `data.incisionLevel` 为 null → `form.incisionType` 保持 undefined → el-select 显示空白 +### 1. 数据库现状 +- `op_schedule` 表:**没有** `incision_level` 字段(58列,已验证) +- `cli_surgery` 表:**有** `incision_level` 字段,且有数据 +- DDL 脚本 `sql/bug_434_add_incision_level_to_op_schedule.sql` 已写好但**未执行** -### 根因:PostgreSQL 小写化未加引号的列别名 - -PostgreSQL 会将未加双引号的列别名自动转为小写: +### 2. 后端 SQL 查询(getSurgeryScheduleDetail) ```sql --- SQL 写的别名 -cs.incision_level AS incisionLevel --- PostgreSQL 实际返回的列名 -incisionlevel ← 全小写! +SELECT os.*, cs.incision_level AS "incisionLevel", ... +FROM op_schedule os +INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no +``` +- `os.*` 不包含 `incision_level`(因为 `op_schedule` 没有该列) +- 通过 JOIN 从 `cli_surgery` 读取,别名 `"incisionLevel"`(已加双引号保持大小写) +- PostgreSQL 返回列名: `incisionLevel`(驼峰)→ MyBatis 可正确映射到 `OpScheduleDto.incisionLevel` + +### 3. OpSchedule 实体 +- **缺失** `incisionLevel` 字段 → `BeanUtils.copyProperties` 无法将 `incisionLevel` 复制到 `OpSchedule` 实体 +- 结果:`updateSurgerySchedule` 更新时,`incisionLevel` 不会写入 `op_schedule` 表 + +### 4. 前端 handleEdit +```javascript +Object.assign(form, data) // 后端返回 data.incisionLevel +if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel) +``` +- 前端使用 `form.incisionType` 绑定 el-select +- 后端返回 `incisionLevel`,前端转换为 `incisionType` + +### 5. 前端 submitForm +```javascript +const submitData = { ...form, orgId: ..., incisionLevel: form.incisionType } +delete submitData.incisionType +``` +- 提交时将 `incisionType` 转为 `incisionLevel` +- 后端 `updateSurgerySchedule` 接收 `OpScheduleDto`(有 `incisionLevel` 字段) +- `BeanUtils.copyProperties` 复制到 `OpSchedule` 实体(没有 `incisionLevel`)→ **字段丢失** +- `syncSurgeryIncisionLevel` 仅同步到 `cli_surgery`,`op_schedule` 中不存储 + +## 根因(双重问题) + +### 根因 A(SQL 别名 - 已修复) +SQL 中 `cs.incision_level AS "incisionLevel"` 已加双引号,PostgreSQL 保持大小写,MyBatis 可正确映射。 +**状态:已修复,无需改动** + +### 根因 B(实体缺失字段 - 待修复) +`OpSchedule` 实体类中**没有** `incisionLevel` 字段。导致: +1. 编辑时即使后端正确返回数据,保存到 `op_schedule` 时该字段被忽略 +2. 数据仅存储在 `cli_surgery.incision_level`,`op_schedule` 中没有备份 + +## 修复方案 + +### 1. 执行 DDL +```sql +ALTER TABLE op_schedule ADD COLUMN IF NOT EXISTS incision_level INT2; +COMMENT ON COLUMN op_schedule.incision_level IS '手术切口等级 1-I级切口 2-II级切口 3-III级切口 4-IV级切口'; ``` -MyBatis 收到列名 `incisionlevel`(全小写),尝试匹配 Java 属性 `incisionLevel`(驼峰)。由于 `mapUnderscoreToCamelCase` 只对含下划线的列生效(`incisionlevel` 无下划线),匹配失败。 +### 2. 在 OpSchedule 实体中添加字段 +```java +/** 切口类型 */ +private Integer incisionLevel; +``` -**对比 `anes_method` 为什么能工作**: -- SQL: `os.anes_method`(无 AS 别名) -- PostgreSQL 返回: `anes_method`(保留下划线) -- MyBatis `mapUnderscoreToCamelCase`: `anes_method` → `anesMethod` ✅ +### 3. 修改 mapper XML(可选增强) +- `getSurgerySchedulePage` 查询中也加入 `cs.incision_level AS "incisionLevel"`(目前列表没有该字段) +- `getSurgeryScheduleDetail` 已有 `"incisionLevel"` 别名,无需改动 -**对比同 mapper 中的 `surgeryNo` 为什么能工作**: -- SQL: `os.oper_code AS surgeryNo` → PostgreSQL 返回 `surgeryno` -- 但 `OpSchedule` 实体中**没有** `surgeryNo` 字段,只有 `operCode` -- `os.oper_code` 列映射到 `operCode` 是通过 `mapUnderscoreToCamelCase` 正常工作的 -- `surgeryno` 找不到对应属性,被 MyBatis 忽略(不影响功能) +### 4. 从同步到直接保存 +- `syncSurgeryIncisionLevel` 方法仍然保留(用于同步到 `cli_surgery`) +- `updateSurgerySchedule` 中因为实体有了 `incisionLevel`,`BeanUtils.copyProperties` 会自动复制 +- `updateById` 会自动更新 `op_schedule.incision_level` -### 修复方案 - -将 SQL 中的别名加双引号:`cs.incision_level AS "incisionLevel"` - -PostgreSQL 对加双引号的标识符保持大小写,返回列名 `incisionLevel`(驼峰),MyBatis 可直接匹配到 `OpScheduleDto.incisionLevel` 属性。 - -### 影响范围 -- **后端**: `SurgicalScheduleAppMapper.xml` — `getSurgeryScheduleDetail` 查询(第92行) -- **前端**: 无需修改(`handleEdit`/`handleView` 中的 nextTick 转换逻辑已正确) -- **数据库**: 无需修改(`cli_surgery.incision_level` 字段已存在且有数据) +## 影响范围 +- **数据库**: 执行 DDL 添加 `op_schedule.incision_level` 字段 +- **后端**: `OpSchedule.java` 添加 `incisionLevel` 字段 +- **前端**: 无需修改(数据绑定和转换逻辑已正确) ## 验证计划 -1. 修改 SQL 后,运行相同查询验证列名变为 `incisionLevel` -2. 确认前端 `node --check` 语法通过 +1. 执行 DDL 后验证字段存在 +2. 修改 Java 实体后验证编译通过 +3. 验证编辑弹窗回显 +4. 验证保存后数据正确