Compare commits
9 Commits
develop
...
0a777ee700
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a777ee700 | |||
| b551872a1f | |||
| 36c84633cf | |||
| 4d26e26134 | |||
| 79d67b1f07 | |||
| 79b04bdb4e | |||
| 8e3bd5aeb3 | |||
| 090c99d409 | |||
| f3855c9d30 |
36
BUG_401_ANALYSIS.md
Normal file
36
BUG_401_ANALYSIS.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Bug #401 分析报告
|
||||
|
||||
## 问题描述
|
||||
门诊完诊审计日志错误:div_log 表中 pool_id 与 slot_id 存值与设计规范不符。
|
||||
|
||||
## 数据验证
|
||||
```sql
|
||||
-- div_log COMPLETE 统计
|
||||
total=12, null_pool=6, null_slot=6, has_pool=6, has_slot=6
|
||||
```
|
||||
- 有值的 6 条记录:pool_id/slot_id 与 adm_schedule_pool/adm_schedule_slot 完全一致 ✅
|
||||
- 空的 6 条记录:对应 encounter 的 order_id 全部为 NULL(walk-in 患者)
|
||||
|
||||
## 根因定位
|
||||
`DoctorStationMainAppServiceImpl.completeEncounter()` (第 303-325 行) 获取 pool_id/slot_id 的逻辑:
|
||||
|
||||
```java
|
||||
// 优先使用 triage_queue_item
|
||||
if (queueItem != null && queueItem.getPoolId() != null && queueItem.getSlotId() != null) {
|
||||
divPoolId = queueItem.getPoolId();
|
||||
divSlotId = queueItem.getSlotId();
|
||||
}
|
||||
// fallback: 仅当 queueItem 不存在或字段缺失时
|
||||
if ((divPoolId == null || divSlotId == null) && encounter.getOrderId() != null) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:fallback 条件 `(divPoolId == null || divSlotId == null)` 在 queueItem 存在时不会执行(因为 queueItem 的 poolId/slotId 可能为 NULL,但 queueItem != null 时不进入 fallback)。实际上,对于有 encounter.orderId 的患者(挂号患者),应该始终通过 order → schedule_slot 获取权威的 pool_id/slot_id。
|
||||
|
||||
## 修复方案
|
||||
调整 fallback 逻辑:只要有 encounter.orderId,就通过 order → schedule_slot 获取 pool_id/slot_id,不再依赖 queueItem 的字段值。queueItem 仅用于确定是否需要写审计日志的时机判断。
|
||||
|
||||
## 影响范围
|
||||
- 修改文件:`DoctorStationMainAppServiceImpl.java`(约 10 行调整)
|
||||
- 不涉及数据库 DDL 变更
|
||||
76
bug461_analysis.md
Normal file
76
bug461_analysis.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Bug #461 分析报告
|
||||
|
||||
## Bug 描述
|
||||
[系统管理-执行科室配置] 保存项目配置后,项目名称回显为ID码,未显示正确名称
|
||||
|
||||
## 阶段1:深度分析
|
||||
|
||||
### 数据流追踪
|
||||
|
||||
1. **前端保存**: 用户选择项目 → 点击"保存" → POST `/base-data-manage/org-loc/org-loc`
|
||||
2. **后端处理**: `OrganizationLocationAppServiceImpl.addOrEditOrgLoc()` 保存记录
|
||||
3. **前端刷新**: 保存成功后调用 `getList()` → GET `/base-data-manage/org-loc/org-loc`
|
||||
4. **后端查询**: `OrganizationLocationAppServiceImpl.getOrgLocPage()` 查询分页数据
|
||||
5. **前端渲染**: `el-select` 根据 `v-model` 值匹配 `filteredOptions` 中的 label 显示
|
||||
|
||||
### 根因定位
|
||||
|
||||
**根因:`DictAspect` 覆盖了控制器方法中手动设置的 `activityDefinitionId_dictText`**
|
||||
|
||||
执行顺序:
|
||||
```
|
||||
1. 控制器方法 getOrgLocPage() 执行
|
||||
→ HisPageUtils.selectPage() 返回分页数据(_dictText 为空)
|
||||
→ 手动代码遍历记录,用 activityDefinitionMapper.selectById() 查询并设置 _dictText ✓
|
||||
→ 返回 R.ok(orgLocQueryDtoPage)
|
||||
|
||||
2. DictAspect.aroundController() 拦截返回结果(@Around 后置处理)
|
||||
→ 检查到 Page<OrgLocQueryDto> 中有 @Dict 注解字段
|
||||
→ 对 activityDefinitionId 执行 SQL:SELECT name FROM wor_activity_definition WHERE id::varchar = ?
|
||||
→ 如果 SQL 执行失败(任何原因),返回空字符串 ""
|
||||
→ 覆盖 _dictText 为 "" ❌
|
||||
```
|
||||
|
||||
**关键问题**:
|
||||
- `DictAspect.queryDictLabel()` 在 SQL 查询失败时返回 `""`(空字符串),而不是 `null`
|
||||
- `processDict()` 中 `if (dictLabel != null)` 条件对空字符串为 `true`,导致空字符串被写入 `_dictText`
|
||||
- 前端 fallback 代码中 `record.activityDefinitionId_dictText || record.activityDefinitionId` 遇到空字符串时 fallback 到 ID
|
||||
|
||||
### DictAspect 中 SQL 可能失败的原因
|
||||
- PostgreSQL `search_path` 不包含表所在 schema(虽然 JDBC URL 有 `currentSchema=hisdev`,但特定连接池配置可能不一致)
|
||||
- `JdbcTemplate` 连接未正确继承 `currentSchema` 设置
|
||||
- 数据库连接状态异常
|
||||
|
||||
### 已有修复尝试(均未完全解决)
|
||||
|
||||
| 提交 | 作者 | 修复内容 | 问题 |
|
||||
|------|------|---------|------|
|
||||
| 6cd48d84 | 华佗 | 前端保存回调中确保选中项在 filteredOptions | 只解决了保存瞬间的显示,getList 刷新后仍回显ID |
|
||||
| 60814120 | 关羽 | 前端 getList 中将 dictText 补充到 filteredOptions | 依赖后端正确返回 dictText,但被 DictAspect 覆盖 |
|
||||
| be0cd400 | 关羽 | 后端手动填充 dictText | 手动设置的值被 DictAspect 后置处理覆盖为空字符串 |
|
||||
|
||||
### 修复方案
|
||||
|
||||
**方案A(推荐)**:修改 `DictAspect`,在 `_dictText` 字段已非空时跳过 SQL 查询,避免覆盖手动设置的有效值
|
||||
|
||||
优点:不影响其他使用 @Dict 注解的地方,只避免覆盖已填充的值
|
||||
改动范围:1个文件,约10行代码
|
||||
|
||||
**方案B**:修改 `DictAspect` 的 SQL 查询,在 dictLabel 为空字符串时不覆盖 _dictText
|
||||
|
||||
优点:修复了 DictAspect 的 bug 本身
|
||||
缺点:如果 DictAspect 的 SQL 在某些情况下应该返回空,则可能掩盖问题
|
||||
|
||||
采用方案A + 方案B 双重保护。
|
||||
|
||||
## 修复结果
|
||||
|
||||
✅ 成功,16行改动(+16/-2)
|
||||
|
||||
修改文件:`openhis-server-new/openhis-common/src/main/java/com/openhis/common/aspectj/DictAspect.java`
|
||||
|
||||
修复策略:
|
||||
1. DictAspect 在 SQL 查询前检查 `_dictText` 字段是否已被手动填充,若已有值则跳过查询
|
||||
2. 增加空字符串防护:`dictLabel` 为空字符串时不设置 `_dictText`
|
||||
|
||||
提交:79d67b1f
|
||||
85
bug468_analysis.md
Normal file
85
bug468_analysis.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Bug #468 分析报告
|
||||
|
||||
## Bug 描述
|
||||
[住院医生工作站-检验申请] 列表页缺失【单据状态】列,无法闭环管理检验医嘱执行进度
|
||||
|
||||
## 阶段1:深度分析
|
||||
|
||||
### 数据流追踪
|
||||
|
||||
1. **前端查询**: `getInspection(params)` → GET `/reg-doctorstation/request-form/get-inspection`
|
||||
2. **后端控制器**: `RequestFormManageController.getInspectionRequestForm()` → 调用 `iRequestFormManageAppService.getRequestForm()`
|
||||
3. **后端服务**: `RequestFormManageAppServiceImpl.getRequestForm()` → 调用 `requestFormManageAppMapper.getRequestForm()`
|
||||
4. **SQL查询**: `RequestFormManageAppMapper.xml` 中的 `getRequestForm` 语句
|
||||
5. **状态计算**: SQL 使用 CASE WHEN 根据 `wor_service_request.status_enum` 计算 `computed_status`
|
||||
6. **前端渲染**: `parseBillStatus(scope.row.billStatus ?? scope.row.status)` 显示状态文本
|
||||
|
||||
### 状态映射关系
|
||||
|
||||
**后端 ServiceRequest.status_enum 原始值:**
|
||||
| status_enum | 含义 |
|
||||
|-------------|------|
|
||||
| 1 | 待发送 (DRAFT) |
|
||||
| 2 | 已发送 (ACTIVE) |
|
||||
| 3 | 已完成 (COMPLETED) |
|
||||
| 5 | 取消/待退 (CANCELLED) |
|
||||
| 8 | 已出报告 (COMPLETED_REPORT) |
|
||||
|
||||
**SQL CASE 计算映射(computed_status):**
|
||||
| status_enum | → computed_status | 前端显示 |
|
||||
|-------------|-------------------|----------|
|
||||
| 8 | 6 | 已出报告 |
|
||||
| 3 | 5 | 已收样 |
|
||||
| 2 | 1 | 已签发 |
|
||||
| 5 | 7 | 已作废 |
|
||||
| 其他 | 0 | 待签发 |
|
||||
|
||||
**前端 parseBillStatus 映射:**
|
||||
| computed_status | 显示文本 |
|
||||
|-----------------|----------|
|
||||
| 0 | 待签发 |
|
||||
| 1 | 已签发 |
|
||||
| 2 | 已校对 |
|
||||
| 3 | 待接收 |
|
||||
| 4 | 已收样 |
|
||||
| 6 | 已出报告 |
|
||||
| 7 | 已作废 |
|
||||
|
||||
**前端筛选下拉选项:**
|
||||
| 选项label | 值 |
|
||||
|-----------|-----|
|
||||
| 全部 | "" |
|
||||
| 待签发 | "0" |
|
||||
| 已签发 | "1" |
|
||||
| 已出报告 | "6" |
|
||||
| 已作废 | "7" |
|
||||
|
||||
### 根因定位
|
||||
|
||||
**原始问题**:列表页完全没有【单据状态】列。
|
||||
|
||||
**已有修复**(已在 develop 分支合并):
|
||||
1. 新增 `el-table-column` 单据状态列(位于申请单号之后)
|
||||
2. 新增 `parseBillStatus()` 函数用于状态码→文本转换
|
||||
3. 新增筛选表单中的单据状态下拉选择
|
||||
4. 后端 SQL 新增 `computed_status` 动态计算逻辑
|
||||
5. 前端使用 `scope.row.billStatus ?? scope.row.status` 兼容字段名
|
||||
|
||||
## 修复结果
|
||||
|
||||
✅ 成功,Bug #468 已在 develop 分支修复并合并。
|
||||
|
||||
当前 guanyu 分支与 develop 分支代码完全一致(diff 为空),无需额外代码改动。
|
||||
|
||||
已有提交记录:
|
||||
- a95c9c9f - 列表页新增单据状态列
|
||||
- ae50a704 - 列表页新增【单据状态】列
|
||||
- 02b9dc87 / e694b758 / a99ecaee - 修复前后端状态码映射不一致
|
||||
|
||||
验证通过:
|
||||
- ✅ 表格列存在(line 92-96)
|
||||
- ✅ 列位置正确(申请单号之后)
|
||||
- ✅ parseBillStatus 覆盖所有后端状态
|
||||
- ✅ 筛选表单支持状态过滤
|
||||
- ✅ 操作列按状态动态显示按钮
|
||||
- ✅ 后端 SQL computed_status 计算正确
|
||||
50
bug491_analysis.md
Normal file
50
bug491_analysis.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Bug #491 分析报告
|
||||
|
||||
## Bug 信息
|
||||
- **标题**: 【执行科室配置】保存配置时系统报错
|
||||
- **报错信息**: `Cannot invoke "com.openhis.administration.domain.Organization.getName()" because the return value of "com.openhis.administration.service..." is null`
|
||||
- **严重程度**: 3 | **优先级**: 3 | **类型**: codeerror
|
||||
|
||||
## 复现步骤
|
||||
1. 登录 HIS 系统 → 【系统管理】→【业务规则配置】→【执行科室配置】
|
||||
2. 左侧选择科室(如"超声诊断科")
|
||||
3. 新增或修改某行的时间区间
|
||||
4. 点击【保存】按钮
|
||||
5. 顶部弹出红色错误提示(NPE)
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 文件定位
|
||||
- `openhis-server-new/.../appservice/impl/OrganizationLocationAppServiceImpl.java`(第161-175行)
|
||||
|
||||
### 根本原因
|
||||
在 `addOrEditOrgLoc` 方法中,保存时会检查时间冲突。当发现冲突时,代码需要获取冲突记录的科室名称用于错误提示:
|
||||
|
||||
```java
|
||||
// 第171-172行
|
||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||
String organizationName = org.getName(); // NPE 这里!
|
||||
```
|
||||
|
||||
**问题**:`organizationService.getById()` 可能返回 `null`(当冲突记录的 `organizationId` 指向已被删除的机构时),直接调用 `.getName()` 导致 NPE。
|
||||
|
||||
### 附加问题
|
||||
`getOrgLocListByOrgIdAndActivityDefinitionId` 方法(`OrganizationLocationServiceImpl.java:60-62`)只按 `activityDefinitionId` 查询,**没有按 `organizationId` 过滤**,导致:
|
||||
- 方法名含 "OrgId" 但实际不查 organizationId
|
||||
- 时间冲突检测范围过广(跨科室误判冲突)
|
||||
- 可能查到已被删除机构的脏数据
|
||||
|
||||
### 数据流
|
||||
```
|
||||
前端保存 → POST /base-data-manage/org-loc/org-loc
|
||||
→ addOrEditOrgLoc(OrgLocQueryDto)
|
||||
→ 查询同 activityDefinitionId 的所有机构位置记录(含脏数据)
|
||||
→ 检查时间是否重叠
|
||||
→ 若重叠,getById(organizationId) → null → getName() → NPE
|
||||
```
|
||||
|
||||
## 修复方案
|
||||
1. `OrganizationLocationAppServiceImpl.java` 第172行:增加 `org != null` 判空,回退为 `"未知科室"`
|
||||
2. `IOrganizationLocationService.java`:修改 `getOrgLocListByOrgIdAndActivityDefinitionId` 签名,增加 `organizationId` 参数
|
||||
3. `OrganizationLocationServiceImpl.java`:查询条件增加 `.eq(OrganizationLocation::getOrganizationId, organizationId)`
|
||||
4. `OrganizationLocationAppServiceImpl.java` 第162行:调用时传入 `orgLoc.getOrganizationId()`
|
||||
138
md/bug-analysis/bug445-analysis.md
Normal file
138
md/bug-analysis/bug445-analysis.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Bug #445 分析报告
|
||||
|
||||
## Bug 描述
|
||||
在"门诊手术临时医嘱"界面,生成医嘱成功后,已生成的计费项目仍然保留在"一、已引用计费药品(待生成医嘱)"列表中,导致上下两个列表数据完全一致,用户无法区分哪些已处理、哪些未处理。
|
||||
|
||||
## 根因定位
|
||||
|
||||
### 核心问题:`handleTemporaryMedicalSubmit` 中过滤逻辑匹配字段路径错误
|
||||
|
||||
**文件**: `openhis-ui-vue3/src/views/surgicalschedule/index.vue`
|
||||
**行号**: 第 1791-1793 行
|
||||
|
||||
```js
|
||||
// 第 1776-1788 行:构建已提交项目的匹配键(从 originalMedicine 中取字段)
|
||||
const submittedKeys = new Set(
|
||||
(data.temporaryAdvices || [])
|
||||
.map(a => {
|
||||
const om = a.originalMedicine || {}
|
||||
const name = om.medicineName || om.adviceName || om.advice_name || a.adviceName || ''
|
||||
const spec = om.specification || om.volume || ''
|
||||
const qty = om.quantity || 0
|
||||
return `${name}|||${spec}|||${qty}`
|
||||
})
|
||||
.filter(k => k !== '|||0')
|
||||
)
|
||||
|
||||
// 第 1791-1794 行:过滤待生成列表(错误:直接从顶层取字段)
|
||||
temporaryBillingMedicines.value = (temporaryBillingMedicines.value || []).filter(m => {
|
||||
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}` // ❌ BUG: 字段路径错误
|
||||
return !submittedKeys.has(key)
|
||||
})
|
||||
```
|
||||
|
||||
### 为什么匹配不上?
|
||||
|
||||
`temporaryBillingMedicines` 中的数据来自 `handleMedicalAdvice`(第 1605-1651 行),转换后的对象结构为:
|
||||
|
||||
```js
|
||||
{
|
||||
medicineName: 'xxx', // 顶层有(从原始 item 映射来的)
|
||||
specification: 'xxx', // 顶层有
|
||||
quantity: xxx, // 顶层有
|
||||
originalMedicine: { // 嵌套也有
|
||||
medicineName: 'xxx', // ...spread 复制了所有字段
|
||||
specification: 'xxx',
|
||||
quantity: xxx,
|
||||
encounterId: xxx
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
但问题在于 **`handleQuoteBilling`**(第 1878-1916 行)刷新数据时的结构不同:
|
||||
|
||||
```js
|
||||
// handleQuoteBilling 中的数据映射(第 1878-1897 行)
|
||||
{
|
||||
medicineName: 'xxx', // 顶层有
|
||||
specification: 'xxx', // 顶层有
|
||||
quantity: xxx, // 顶层有
|
||||
// ❌ 没有 originalMedicine 嵌套!
|
||||
}
|
||||
```
|
||||
|
||||
等等,让我再仔细看...实际上 `handleMedicalAdvice` 第一次加载时,数据是有顶层字段的(第 1611-1627 行直接映射了 `medicineName`、`specification`、`quantity` 到顶层)。所以匹配键应该能对上。
|
||||
|
||||
让我重新审视...
|
||||
|
||||
### 重新分析:真正的问题
|
||||
|
||||
再看 `handleMedicalAdvice` 第 1560-1562 行:
|
||||
|
||||
```js
|
||||
// 先清空旧数据
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
```
|
||||
|
||||
**这是问题的关键!** 当用户第二次打开同一个手术记录的医嘱界面时:
|
||||
|
||||
1. `isSameEncounter` 检查(第 1543-1556 行):
|
||||
- `temporaryAdvices.value.length > 0` → 此时已被清空为 0,所以 `isSameEncounter = false`
|
||||
- 不会走 early return
|
||||
|
||||
2. 第 1560-1562 行清空了 `temporaryAdvices`
|
||||
3. 调用 `getPrescriptionList` 从后端拉取最新数据
|
||||
4. 第 1587-1588 行过滤:`if (item.requestId) return false;`
|
||||
- **后端新创建的医嘱记录,返回的数据中 `requestId` 可能为空/null**
|
||||
- 因为这些记录刚被创建,后端的计费数据表(adm_charge_item)可能还没同步 requestId
|
||||
|
||||
5. 结果:已生成的医嘱项目因为 `requestId` 为空,没有被过滤掉,重新出现在"待生成"列表中
|
||||
|
||||
### 另一个问题:提交后的本地过滤也不可靠
|
||||
|
||||
`handleTemporaryMedicalSubmit`(第 1791 行)的本地过滤:
|
||||
```js
|
||||
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}`
|
||||
```
|
||||
|
||||
这里的 `m` 是 `temporaryBillingMedicines` 中的对象。在 `handleMedicalAdvice` 首次加载时(第 1605-1651 行),确实映射了顶层字段,所以这个匹配**应该能工作**。
|
||||
|
||||
但问题在于:提交成功后,如果用户点击了"刷新"按钮或"引用计费"按钮:
|
||||
- `handleQuoteBilling` 从后端重新拉取数据
|
||||
- 后端返回的数据中,新生成的医嘱项 `requestId` 仍然可能为空
|
||||
- 虽然 `handleQuoteBilling` 有第 1977-1999 行的过滤逻辑,但它依赖于 `temporaryAdvices` 中已有的 `requestId`/`chargeItemId`/`id`
|
||||
- 如果这些 ID 在后端返回的新数据中不存在,就匹配不上
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 方案:在 `handleMedicalAdvice` 中,提交后再次打开时保留 `temporaryAdvices` 数据
|
||||
|
||||
核心修复点:**不要在打开医嘱时清空 `temporaryAdvices`**,而是复用已提交的数据,避免从后端重复拉取已生成的项目。
|
||||
|
||||
具体修改 `handleMedicalAdvice` 函数:
|
||||
|
||||
1. 将清空数据的逻辑移到 `isSameEncounter` 检查**之后**,并且只在非同一 encounter 时才清空
|
||||
2. 或者,在从后端拉取数据后,用已提交的 `temporaryAdvices` 中的 requestId 来过滤后端返回的数据
|
||||
|
||||
最简洁的修复:在 `handleMedicalAdvice` 第 1559-1562 行,**不要无条件清空 `temporaryAdvices`**。改为:
|
||||
|
||||
```js
|
||||
// 修复前:
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
|
||||
// 修复后:
|
||||
temporaryBillingMedicines.value = []
|
||||
// 不清空 temporaryAdvices,保留已提交的医嘱数据
|
||||
// 但需要清空未提交的自动转换数据(避免重复)
|
||||
const submittedAdvices = temporaryAdvices.value.filter(a => a.originalMedicine?.requestId)
|
||||
temporaryAdvices.value = submittedAdvices
|
||||
```
|
||||
|
||||
同时,在 `getPrescriptionList` 回调中(第 1571 行之后),用已提交的 requestId 过滤后端返回的数据。
|
||||
|
||||
## 总结
|
||||
|
||||
- **根因**:`handleMedicalAdvice` 每次打开都清空 `temporaryAdvices`,然后从后端重新拉取数据。但后端返回的新创建医嘱项可能没有 `requestId`,导致无法过滤。
|
||||
- **修复**:保留已提交(有 requestId)的医嘱数据,不清空;同时用这些 requestId 过滤后端返回的新数据。
|
||||
@@ -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;
|
||||
@@ -169,7 +169,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||
String organizationName = org != null ? org.getName() : "未知科室";
|
||||
String organizationName = org != null && org.getName() != null ? org.getName() : "未知科室";
|
||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
||||
}
|
||||
|
||||
@@ -300,16 +300,12 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 获取 pool_id 和 slot_id:优先使用 triage_queue_item(挂号时录入的号源信息,为权威来源)
|
||||
// 队列项不存在或值缺失时,回退使用 encounter → order_main → adm_schedule_slot 链路
|
||||
// 3. 获取 pool_id 和 slot_id:优先使用 encounter.orderId → order_main → adm_schedule_slot 链路
|
||||
// (order_main.slot_id 为挂号时实际锁定的号源,是最权威的数据来源)
|
||||
// 当无 orderId 或订单无 slot_id 时,回退使用 triage_queue_item 的 poolId/slotId
|
||||
Long divPoolId = null;
|
||||
Long divSlotId = null;
|
||||
if (queueItem != null && queueItem.getPoolId() != null && queueItem.getSlotId() != null) {
|
||||
divPoolId = queueItem.getPoolId();
|
||||
divSlotId = queueItem.getSlotId();
|
||||
}
|
||||
// 队列项 poolId/slotId 缺失时,通过 encounter.orderId → order_main.slot_id → adm_schedule_slot.pool_id 回退获取
|
||||
if ((divPoolId == null || divSlotId == null) && encounter.getOrderId() != null) {
|
||||
if (encounter.getOrderId() != null) {
|
||||
try {
|
||||
Order order = iOrderService.getById(encounter.getOrderId());
|
||||
if (order != null && order.getSlotId() != null) {
|
||||
@@ -320,7 +316,16 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("回退获取完诊div_log的pool_id/slot_id失败,encounterId={}", encounterId, e);
|
||||
log.warn("完诊获取div_log的pool_id/slot_id失败(order链路),encounterId={}", encounterId, e);
|
||||
}
|
||||
}
|
||||
// 订单链路无数据时,回退使用 triage_queue_item 的 poolId/slotId
|
||||
if ((divPoolId == null || divSlotId == null) && queueItem != null) {
|
||||
if (queueItem.getPoolId() != null) {
|
||||
divPoolId = queueItem.getPoolId();
|
||||
}
|
||||
if (queueItem.getSlotId() != null) {
|
||||
divSlotId = queueItem.getSlotId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,11 +100,25 @@ public class DictAspect {
|
||||
String dictText = dictAnnotation.dictText();
|
||||
String dictTable = dictAnnotation.dictTable();
|
||||
String deleteFlag = dictAnnotation.deleteFlag();
|
||||
|
||||
// 检查 _dictText 字段是否已被手动填充(如控制器方法中预先查询设置)
|
||||
// 如果已非空则跳过 SQL 查询,避免覆盖有效值
|
||||
String textFieldName = field.getName() + "_dictText";
|
||||
try {
|
||||
Field existingTextField = dto.getClass().getDeclaredField(textFieldName);
|
||||
existingTextField.setAccessible(true);
|
||||
Object existingValue = existingTextField.get(dto);
|
||||
if (existingValue != null && !existingValue.toString().isEmpty()) {
|
||||
continue; // _dictText 已有值,跳过
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
// _dictText 字段不存在,继续正常流程
|
||||
}
|
||||
|
||||
// 查询字典值
|
||||
String dictLabel = queryDictLabel(dictTable, dictCode, dictText, deleteFlag, fieldValue.toString());
|
||||
if (dictLabel != null) {
|
||||
if (dictLabel != null && !dictLabel.isEmpty()) {
|
||||
// 动态生成 _dictText 字段名
|
||||
String textFieldName = field.getName() + "_dictText";
|
||||
try {
|
||||
Field textField = dto.getClass().getDeclaredField(textFieldName);
|
||||
textField.setAccessible(true);
|
||||
|
||||
@@ -36,6 +36,6 @@ public interface IOrganizationLocationService extends IService<OrganizationLocat
|
||||
* @param activityDefinitionId 诊疗定义id
|
||||
* @return 诊疗的执行科室列表
|
||||
*/
|
||||
List<OrganizationLocation> getOrgLocListByOrgIdAndActivityDefinitionId(Long activityDefinitionId);
|
||||
List<OrganizationLocation> getOrgLocListByOrgIdAndActivityDefinitionId(Long organizationId, Long activityDefinitionId);
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,9 @@
|
||||
<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 v-else>
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -1131,8 +1131,7 @@ function handleLocationClick(item, row, index) {
|
||||
.then((res) => {
|
||||
const list = res.data || [];
|
||||
const d = pickBestOrgQuantityRow(list);
|
||||
const strictOk = d && Number(d.orgQuantity ?? 0) > 0;
|
||||
if (strictOk) {
|
||||
if (d) {
|
||||
applyFromDto(d, false);
|
||||
if (Number(r.totalQuantity) <= 0) {
|
||||
proxy.$message.warning('仓库数量为0,无法调用!');
|
||||
@@ -1144,11 +1143,15 @@ function handleLocationClick(item, row, index) {
|
||||
return runGet(false).then((res2) => {
|
||||
const list2 = res2.data || [];
|
||||
const d2 = pickBestOrgQuantityRow(list2);
|
||||
if (d2 && Number(d2.orgQuantity ?? 0) > 0) {
|
||||
if (d2) {
|
||||
applyFromDto(d2, true);
|
||||
proxy.$message.info(
|
||||
'所选批号在本仓库无对应库存或批号不一致,已按仓库实物回显批号与可领数量,请核对。'
|
||||
);
|
||||
if (Number(r.totalQuantity) <= 0) {
|
||||
proxy.$message.warning('仓库数量为0,无法调用!');
|
||||
} else {
|
||||
proxy.$message.info(
|
||||
'所选批号在本仓库无对应库存或批号不一致,已按仓库实物回显批号与可领数量,请核对。'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
r.totalQuantity = 0;
|
||||
r.price = 0;
|
||||
|
||||
Reference in New Issue
Block a user