Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b6b00b6c2 | |||
| 1ddf8a2ccd | |||
| 0a37b05aab | |||
| 20817d6dc4 |
44
bug444_analysis.md
Normal file
44
bug444_analysis.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Bug #444 分析报告
|
||||
|
||||
## Bug 描述
|
||||
【手术管理-门诊手术安排】生成临时医嘱界面,"已引用计费药品"列表未正常显示药品详细名称信息,且错误地带出了非药品类的计费信息(如手术诊疗项目"小腿烧伤扩创交腿皮瓣修复术"、检查项目"心脏彩色多普勒超声")。
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 数据流
|
||||
1. 用户点击"医嘱"按钮 → `handleMedicalAdvice()` → 调用 `getPrescriptionList()` 获取计费数据
|
||||
2. 用户对数据进行过滤后展示在"已引用计费药品"列表
|
||||
3. 用户点击"引用计费"按钮 → `handleQuoteBilling()` → 再次调用 `getPrescriptionList()` 获取最新计费数据
|
||||
|
||||
### 根因定位
|
||||
**`handleQuoteBilling()` 方法(index.vue:1866-1877)缺少非药品关键词过滤逻辑。**
|
||||
|
||||
`handleMedicalAdvice()` 中有两层过滤:
|
||||
1. `adviceType !== 1` 过滤(只保留药品类型)
|
||||
2. **关键词排除过滤**(排除名称中包含"术"、"超声"、"检查"等非药品关键词的项目)
|
||||
|
||||
但 `handleQuoteBilling()` 中只有第一层过滤(`adviceType !== 1`),**缺少关键词排除过滤**。
|
||||
|
||||
当后端返回的计费数据中某些非药品项目被错误标注为 `adviceType=1` 时:
|
||||
- `handleMedicalAdvice()` 能通过关键词过滤排除这些项目
|
||||
- `handleQuoteBilling()` 无法排除,导致非药品项目出现在"已引用计费药品"列表中
|
||||
|
||||
### 代码对比
|
||||
|
||||
| 过滤条件 | handleMedicalAdvice (L1576-1597) | handleQuoteBilling (L1866-1877) |
|
||||
|---------|:---:|:---:|
|
||||
| encounterId 匹配 | ✓ | ✓ |
|
||||
| adviceType === 1 | ✓ | ✓ |
|
||||
| 名称非空 | ✓ | ✓ |
|
||||
| **关键词排除** | ✓ **有** | ✗ **缺失** |
|
||||
| requestId 过滤 | ✓ | ✓ |
|
||||
|
||||
## 修复方案
|
||||
|
||||
在 `handleQuoteBilling()` 方法的过滤逻辑中,添加与 `handleMedicalAdvice()` 一致的关键词排除逻辑:
|
||||
|
||||
```javascript
|
||||
// 🔧 修复 Bug #444: 排除名称中包含手术/检查/诊疗关键词的非药品项目
|
||||
const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影'];
|
||||
if (excludedKeywords.some(kw => medicineName.includes(kw))) return false;
|
||||
```
|
||||
30
md/bug-analysis/bug444-analysis.md
Normal file
30
md/bug-analysis/bug444-analysis.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Bug #444 分析报告
|
||||
|
||||
## Bug 描述
|
||||
生成临时医嘱界面,"已引用计费药品"列表未正常显示药品详细名称信息。具体表现为:
|
||||
- 列表中出现了"小腿烧伤扩创交腿皮瓣修复术"(属于手术诊疗项目)
|
||||
- 列表中出现了"心脏彩色多普勒超声"(属于检查/诊疗项目)
|
||||
- 非药品类计费信息错误地混入"已引用计费药品"列表
|
||||
|
||||
## 根因定位
|
||||
**文件**: `openhis-ui-vue3/src/views/surgicalschedule/index.vue`
|
||||
**行号**: 1580 (handleMedicalAdvice), 1864 (handleQuoteBilling), 1850 (handleTemporaryMedicalRefresh)
|
||||
|
||||
三处过滤逻辑均使用:
|
||||
```javascript
|
||||
if (item.adviceType !== 1) return false;
|
||||
```
|
||||
|
||||
**问题1(主因)**: `adviceType` 字段命名兼容不完整。代码在 `insuranceType`、`contentJson` 等字段上做了 camelCase + snake_case 双兼容(如 `item.insuranceType || item.insurance_type`),但 `adviceType` 只检查了 camelCase。若后端返回 snake_case 数据(`advice_type`),`item.adviceType` 为 `undefined`,`undefined !== 1` 为 `true`,导致所有非药品项目全部放行。
|
||||
|
||||
**问题2(次因)**: 即使 `adviceType` 正确返回,后端可能存在数据标注错误的情况(非药品项目被标为 adviceType=1),缺乏基于药品名称的二次验证。
|
||||
|
||||
## 修复方案
|
||||
1. `adviceType` 检查增加 snake_case 回退:`const at = item.adviceType ?? item.advice_type; if (at !== 1) return false;`
|
||||
2. 增加药品名称关键字二次过滤:排除名称中包含"术"、"检查"、"超声"、"多普勒"等关键词的非药品项目
|
||||
|
||||
## 验收标准
|
||||
1. "已引用计费药品"列表中只显示药品类项目
|
||||
2. 不显示手术诊疗项目(如"小腿烧伤扩创交腿皮瓣修复术")
|
||||
3. 不显示检查项目(如"心脏彩色多普勒超声")
|
||||
4. 药品名称正常显示
|
||||
@@ -132,7 +132,22 @@ temporaryAdvices.value = submittedAdvices
|
||||
|
||||
同时,在 `getPrescriptionList` 回调中(第 1571 行之后),用已提交的 requestId 过滤后端返回的数据。
|
||||
|
||||
## 总结
|
||||
## 修复结果
|
||||
|
||||
- **根因**:`handleMedicalAdvice` 每次打开都清空 `temporaryAdvices`,然后从后端重新拉取数据。但后端返回的新创建医嘱项可能没有 `requestId`,导致无法过滤。
|
||||
- **修复**:保留已提交(有 requestId)的医嘱数据,不清空;同时用这些 requestId 过滤后端返回的新数据。
|
||||
### 实际根因
|
||||
`handleQuoteBilling` 函数中:
|
||||
1. **第1856行**:在调用 `getPrescriptionList` 之前先清空了 `temporaryAdvices.value = []`
|
||||
2. **第1997-2019行(旧代码)**:ID 匹配过滤逻辑依赖已被清空的 `temporaryAdvices.value`,因此过滤形同虚设
|
||||
3. 即使 `temporaryAdvices` 未被清空,ID 匹配也不可靠(新生成的医嘱可能没有 `requestId`/`chargeItemId`/`id`)
|
||||
|
||||
### 修复方案
|
||||
1. 在清空 `temporaryAdvices` **之前**,提取已提交项目的复合键(名称+规格+数量)保存到 `submittedKeysBeforeClear`
|
||||
2. 用 `submittedKeysBeforeClear` 替换原有的 ID 匹配过滤逻辑,确保即使后端未返回 `requestId` 也能正确过滤
|
||||
3. 复合键匹配策略与 `handleTemporaryMedicalSubmit` 中使用的策略一致
|
||||
|
||||
### 修改文件
|
||||
- `openhis-ui-vue3/src/views/surgicalschedule/index.vue`
|
||||
- 第1853-1864行:新增 `submittedKeysBeforeClear` 提取逻辑
|
||||
- 第1997-2004行:替换 ID 匹配为复合键匹配
|
||||
|
||||
### 修复结果:✅ 成功,~20行改动(+20/-21)
|
||||
|
||||
@@ -159,7 +159,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
String activityName = activityDef != null ? activityDef.getName() : "";
|
||||
|
||||
List<OrganizationLocation> organizationLocationList =
|
||||
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getActivityDefinitionId());
|
||||
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getOrganizationId(), orgLoc.getActivityDefinitionId());
|
||||
organizationLocationList = (orgLoc.getId() != null)
|
||||
? organizationLocationList.stream().filter(item -> !orgLoc.getId().equals(item.getId())).toList()
|
||||
: organizationLocationList;
|
||||
|
||||
@@ -53,12 +53,14 @@ public class OrganizationLocationServiceImpl extends ServiceImpl<OrganizationLoc
|
||||
/**
|
||||
* 查询诊疗的执行科室列表
|
||||
*
|
||||
* @param organizationId 机构id
|
||||
* @param activityDefinitionId 诊疗定义id
|
||||
* @return 诊疗的执行科室列表
|
||||
*/
|
||||
@Override
|
||||
public List<OrganizationLocation> getOrgLocListByOrgIdAndActivityDefinitionId(Long activityDefinitionId) {
|
||||
public List<OrganizationLocation> getOrgLocListByOrgIdAndActivityDefinitionId(Long organizationId, Long activityDefinitionId) {
|
||||
return baseMapper.selectList(new LambdaQueryWrapper<OrganizationLocation>()
|
||||
.eq(OrganizationLocation::getOrganizationId, organizationId)
|
||||
.eq(OrganizationLocation::getActivityDefinitionId, activityDefinitionId));
|
||||
}
|
||||
|
||||
|
||||
@@ -1576,12 +1576,18 @@ function handleMedicalAdvice(row) {
|
||||
const filteredItems = res.data.filter(item => {
|
||||
// 匹配 encounterId
|
||||
if (item.encounterId !== row.visitId) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
if (item.adviceType !== 1) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6)
|
||||
// 🔧 修复 Bug #444: 使用 Number() 显式转换,避免字符串 "1" 被 !== 1 放行
|
||||
const at = Number(item.adviceType ?? item.advice_type);
|
||||
if (at !== 1) return false;
|
||||
// 过滤掉名称为空的项目
|
||||
const medicineName = item.adviceName || item.advice_name;
|
||||
if (!medicineName || medicineName.trim() === '') return false;
|
||||
// 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目(已有 requestId 的不应出现在"待生成"列表)
|
||||
// 🔧 修复 Bug #444: 二次过滤,排除名称中包含手术/检查/诊疗关键词的非药品项目
|
||||
// 某些计费项目可能在 adm_charge_item 中被错误标注为 adviceType=1
|
||||
const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影'];
|
||||
if (excludedKeywords.some(kw => medicineName.includes(kw))) return false;
|
||||
// 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目(已有 requestId 的不应出现在"待生成"列表中)
|
||||
if (item.requestId) return false;
|
||||
// 根据药品请求ID去重,避免重复显示
|
||||
const itemId = item.requestId || item.id;
|
||||
@@ -1855,16 +1861,22 @@ function handleQuoteBilling() {
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
|
||||
// 🔧 修复 Bug #445: 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
// 同时过滤掉已有 requestId 的项目(已生成医嘱的不需要再次显示在"待生成"列表中)
|
||||
// 🔧 修复 Bug #444: 统一过滤逻辑,与 handleMedicalAdvice 保持一致
|
||||
// 1. 使用 Number() + snake_case 回退,避免类型转换导致过滤失效
|
||||
// 2. 增加关键词二次过滤,排除手术/检查/诊疗等非药品项目
|
||||
const filteredItems = res.data.filter(item => {
|
||||
// 匹配 encounterId
|
||||
if (item.encounterId !== temporaryPatientInfo.value.visitId) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
if (item.adviceType !== 1) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6)
|
||||
// 🔧 修复 Bug #444: 使用 Number() 显式转换,增加 snake_case 回退
|
||||
const at = Number(item.adviceType ?? item.advice_type);
|
||||
if (at !== 1) return false;
|
||||
// 过滤掉名称为空的项目
|
||||
const medicineName = item.adviceName || item.advice_name;
|
||||
if (!medicineName || medicineName.trim() === '') return false;
|
||||
// 🔧 修复 Bug #444: 二次过滤,排除名称中包含手术/检查/诊疗关键词的非药品项目
|
||||
const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影'];
|
||||
if (excludedKeywords.some(kw => medicineName.includes(kw))) return false;
|
||||
// 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目(已有 requestId)
|
||||
if (item.requestId) return false;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user