Compare commits

...

4 Commits

6 changed files with 115 additions and 12 deletions

44
bug444_analysis.md Normal file
View 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;
```

View 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. 药品名称正常显示

View File

@@ -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

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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;