Compare commits

...

3 Commits

Author SHA1 Message Date
关羽
4ec422fc0e Fix Bug #468: [住院医生工作站-检验申请] 修复单据状态列前后端状态码映射不一致
根因:Bug #468 初次修复时添加了【单据状态】列和筛选功能,但前端状态码映射
与后端 SQL CASE 表达式不一致:
- 后端 SQL 将 status_enum=5,6,7 映射为显示码 7(已作废),前端却用 5
- 后端 SQL 将 status_enum=8 映射为显示码 6(已出报告),前端却用 4
导致已作废/已出报告状态显示为"-"且筛选失效。

修复:前端 filter 选项值和 parseBillStatus 映射表与后端 SQL CASE 对齐。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 14:20:54 +08:00
关羽
fdedad618a Fix Bug #468: 根因+修复方案摘要 2026-05-16 14:20:53 +08:00
赵云
2594e372b8 Fix Bug #469: [住院医生工作站-检验申请] 完善【操作】列临床业务逻辑:支持按状态动态切换修改、删除、撤回等功能
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-16 14:20:53 +08:00
2 changed files with 100 additions and 5 deletions

89
BUG461_ANALYSIS.md Normal file
View File

@@ -0,0 +1,89 @@
# Bug #461 分析报告
## Bug描述
[系统管理-执行科室配置] 保存项目配置后项目名称回显为ID码未显示正确名称
## 根因分析
### 数据流
1. 前端调用 `getDiagnosisTreatmentList()` → GET `/base-data-manage/org-loc/org-loc`
2. 后端 `OrganizationLocationController.getOrgLocPage()` 返回 `Page<OrgLocQueryDto>`
3. `OrgLocQueryDto.activityDefinitionId` 标注了 `@Dict(dictTable="wor_activity_definition", dictCode="id", dictText="name")`
4. `DictAspect` AOP 拦截 GET/POST 请求,对带 `@Dict` 注解的字段执行 SQL 翻译:`SELECT name FROM wor_activity_definition WHERE id::varchar = ? LIMIT 1`
5. 翻译结果写入 `activityDefinitionId_dictText` 字段
6. 前端使用 `record.activityDefinitionId_dictText` 作为项目名称显示
### 根本原因
**`DictAspect.queryDictLabel` 方法在 SQL 查询失败时返回空字符串 `""`,导致 `_dictText` 字段被设置为空值。**
具体代码 (`DictAspect.java:123-149`)
```java
private String queryDictLabel(String dictTable, String dictCode, String dictText, String deleteFlag, String dictValue) {
if (!StringUtils.hasText(dictTable)) {
return DictUtils.getDictLabel(dictCode, dictValue);
} else {
if (!StringUtils.hasText(dictText)) {
return DictUtils.getDictLabel(dictCode, dictValue);
}
String sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ?", dictText, dictTable, dictCode);
// ...
try {
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
} catch (DataAccessException e) {
return ""; // ← 关键问题:查询失败返回空字符串
}
}
}
```
`jdbcTemplate.queryForObject` 查询失败(无结果或异常)时,返回空字符串 `""`。而在 `processDict` 中:
```java
String dictLabel = queryDictLabel(...);
if (dictLabel != null) { // ← 空字符串 "" 不等于 null条件为 true
textField.set(dto, dictLabel); // ← 设置为空字符串
}
```
空字符串 `""` 不是 `null`,所以 `_dictText` 被设为空字符串,而不是保持 `null`(未翻译)。前端收到 `activityDefinitionId_dictText: ""`,当作有效值处理,显示为空或回退到 ID。
### 前端 fallback 的不足
前端代码在 `getList()` 中尝试用 `activityDefinitionId_dictText` 补充选项,但:
```javascript
if (record.activityDefinitionId && !filteredOptions.some(o => o.value === record.activityDefinitionId)) {
filteredOptions.push({
value: record.activityDefinitionId,
label: record.activityDefinitionId_dictText || record.activityDefinitionId // ← 空字符串时显示ID
});
}
```
空字符串是 falsy 值,所以 `"" || record.activityDefinitionId` 回退到 ID仍然显示 ID。
## 影响范围
- **前端**: `openhis-ui-vue3/src/views/basicmanage/implementDepartment/index.vue`
- **后端**: `openhis-server-new/.../basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java`
- **AOP**: `openhis-server-new/.../common/aspectj/DictAspect.java`
- **DTO**: `openhis-server-new/.../basedatamanage/dto/OrgLocQueryDto.java`
- **数据库表**: `adm_organization_location`, `wor_activity_definition`
## 修复方案
### 方案:在 service 层直接 JOIN 查询项目名称
修改 `OrganizationLocationAppServiceImpl.getOrgLocPage()` 方法,在返回结果前手动填充 `activityDefinitionId_dictText`,不依赖 DictAspect 的行为。这样更可靠,避免了 AOP 执行顺序、SQL异常处理等不确定因素。
具体改动:在 `OrganizationLocationAppServiceImpl` 中,利用已有的 `activityDefinitionMapper` 对每条记录补充 `activityDefinitionId_dictText`
## 验证计划
1. 修改代码后编译通过
2. 验证 `activityDefinitionId_dictText` 在 API 响应中正确填充
3. 前端 el-select 能正确显示项目名称而非 ID
---
## 修复结果:✅ 成功12行改动
**修改文件**: `openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java`
**改动内容**: 在 `getOrgLocPage()` 方法中,对分页返回的每条记录,使用已注入的 `activityDefinitionMapper` 查询对应的 `ActivityDefinition` 实体,手动填充 `activityDefinitionId_dictText` 字段。
**策略**: 不依赖 DictAspect 的 AOP 翻译机制,在 service 层直接填充字典翻译值,确保前端能接收到正确的项目名称。

View File

@@ -41,8 +41,8 @@
<el-option label="全部" value="" />
<el-option label="待签发" value="0" />
<el-option label="已签发" value="1" />
<el-option label="报告已出" value="4" />
<el-option label="已作废" value="5" />
<el-option label="已出报告" value="6" />
<el-option label="已作废" value="7" />
</el-select>
</el-form-item>
<el-form-item label="关键字">
@@ -105,15 +105,18 @@
</template>
</el-table-column>
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
<el-table-column label="操作" align="center" fixed="right" width="160">
<el-table-column label="操作" align="center" fixed="right" width="180">
<template #default="scope">
<!-- 待签发可修改删除 -->
<template v-if="scope.row.status == 0">
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
<!-- 已签发可撤回 -->
<template v-else-if="scope.row.status == 1">
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
</template>
<!-- 已校对(2)待接收(3)已接收(4)已检查(6)已作废(7)仅查看详情 -->
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
@@ -328,8 +331,11 @@ const parseBillStatus = (status) => {
const statusMap = {
'0': '待签发',
'1': '已签发',
'4': '报告已出',
'5': '已作废',
'2': '已校对',
'3': '待接收',
'4': '已收样',
'6': '已出报告',
'7': '已作废',
};
return statusMap[String(status)] || '-';
};