Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7adb3b3ea4 | |||
| 1e6704928a | |||
| 75e49f0237 | |||
| 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;
|
||||||
|
```
|
||||||
@@ -169,7 +169,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
|||||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||||
String organizationName = org != null ? org.getName() : "未知科室";
|
String organizationName = org != null ? org.getName() : ("科室[" + organizationLocation.getOrganizationId() + "]已删除");
|
||||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,7 +348,8 @@ const adviceTypeList = computed(() => {
|
|||||||
return val === 3 || val === 4;
|
return val === 3 || val === 4;
|
||||||
}).map(item => ({
|
}).map(item => ({
|
||||||
label: item.label,
|
label: item.label,
|
||||||
value: parseInt(item.value)
|
// drord_doctor_type 中耗材是 4,但 /advice-base-info 后端耗材类型是 2
|
||||||
|
value: parseInt(item.value) === 4 ? 2 : parseInt(item.value)
|
||||||
}));
|
}));
|
||||||
return [...filtered, { label: '全部', value: '' }];
|
return [...filtered, { label: '全部', value: '' }];
|
||||||
}
|
}
|
||||||
@@ -483,8 +484,9 @@ watch(
|
|||||||
(visible) => {
|
(visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
||||||
// 弹窗打开时重新加载科室和位置选项,确保数据最新
|
// 弹窗打开时按当前患者科室重新加载,避免复用上一次患者/登录科室的结果
|
||||||
loadDepartmentOptions();
|
loadDepartmentOptions();
|
||||||
|
getAdviceBaseInfos();
|
||||||
getDiseaseInitLoc(16);
|
getDiseaseInitLoc(16);
|
||||||
} else {
|
} else {
|
||||||
resetData();
|
resetData();
|
||||||
@@ -565,6 +567,8 @@ function getAdviceBaseInfos() {
|
|||||||
queryParams.value.adviceTypes = [1, 2, 3];
|
queryParams.value.adviceTypes = [1, 2, 3];
|
||||||
}
|
}
|
||||||
queryParams.value.organizationId = orgId.value;
|
queryParams.value.organizationId = orgId.value;
|
||||||
|
queryParams.value.adviceTypes = normalizeAdviceTypesForQuery(adviceType.value);
|
||||||
|
queryParams.value.organizationId = props.patientInfo.organizationId || orgId.value;
|
||||||
queryParams.value.pricingFlag = 1; // 划价标记
|
queryParams.value.pricingFlag = 1; // 划价标记
|
||||||
getAdviceBaseInfo(queryParams.value)
|
getAdviceBaseInfo(queryParams.value)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@@ -620,6 +624,12 @@ function getItemType_Text(type) {
|
|||||||
const map = { 2: '耗材', 3: '诊疗' };
|
const map = { 2: '耗材', 3: '诊疗' };
|
||||||
return map[type] || '其他';
|
return map[type] || '其他';
|
||||||
}
|
}
|
||||||
|
function normalizeAdviceTypesForQuery(type) {
|
||||||
|
if (type === '' || type === undefined || type === null) {
|
||||||
|
return '2,3';
|
||||||
|
}
|
||||||
|
return Number(type) === 4 ? 2 : type;
|
||||||
|
}
|
||||||
function getUnitCodeOptions(row) {
|
function getUnitCodeOptions(row) {
|
||||||
const unitCodes = [];
|
const unitCodes = [];
|
||||||
// 大单位:优先用 code,code 缺失时用字典文本兜底
|
// 大单位:优先用 code,code 缺失时用字典文本兜底
|
||||||
|
|||||||
@@ -1876,13 +1876,14 @@ function handleQuoteBilling() {
|
|||||||
temporaryBillingMedicines.value = []
|
temporaryBillingMedicines.value = []
|
||||||
temporaryAdvices.value = []
|
temporaryAdvices.value = []
|
||||||
|
|
||||||
// 🔧 修复 Bug #445: 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6)
|
// 🔧 修复 Bug #444: 统一过滤逻辑,与 handleMedicalAdvice 保持一致
|
||||||
// 同时过滤掉已有 requestId 的项目(已生成医嘱的不需要再次显示在"待生成"列表中)
|
// 1. 使用 Number() + snake_case 回退,避免类型转换导致过滤失效
|
||||||
|
// 2. 增加关键词二次过滤,排除手术/检查/诊疗等非药品项目
|
||||||
const filteredItems = res.data.filter(item => {
|
const filteredItems = res.data.filter(item => {
|
||||||
// 匹配 encounterId
|
// 匹配 encounterId
|
||||||
if (item.encounterId !== temporaryPatientInfo.value.visitId) return false;
|
if (item.encounterId !== temporaryPatientInfo.value.visitId) return false;
|
||||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6)
|
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6)
|
||||||
// 🔧 修复 Bug #444: 使用 Number() 显式转换 + snake_case 回退,避免字符串 "1" 匹配失败
|
// 🔧 修复 Bug #444: 使用 Number() 显式转换,增加 snake_case 回退
|
||||||
const at = Number(item.adviceType ?? item.advice_type);
|
const at = Number(item.adviceType ?? item.advice_type);
|
||||||
if (at !== 1) return false;
|
if (at !== 1) return false;
|
||||||
// 过滤掉名称为空的项目
|
// 过滤掉名称为空的项目
|
||||||
|
|||||||
Reference in New Issue
Block a user