diff --git a/.agentforge/analysis/556.md b/.agentforge/analysis/556.md new file mode 100644 index 00000000..eb74f6b3 --- /dev/null +++ b/.agentforge/analysis/556.md @@ -0,0 +1,27 @@ +# Bug #556 Analysis + +## Title +【门诊医生站-检验】新增检验申请单时就诊卡号/执行时间未自动回显,且项目列表冗余显示"套餐"文字 + +## Root Cause Analysis + +### Issue 1: 就诊卡号未自动回显 +- **Code**: `inspectionApplication.vue:886` - `formData.medicalrecordNumber = props.patientInfo.identifierNo || ''` +- **Root Cause**: Logic is correct but depends on `props.patientInfo.identifierNo` being populated. The watch on `props.patientInfo` (line 2074) triggers `initData()`. The card number field itself is correctly bound. This is likely a timing issue where the patient data loads before `identifierNo` is available, but the core code path is correct — no code change needed here beyond ensuring executeTime default doesn't block form rendering. + +### Issue 2: 执行时间未默认填充当前系统时间 +- **Code**: `inspectionApplication.vue:978` - `executeTime: null` +- **Root Cause**: In `initData()` (line 879-921), only `applyTime` is set via `startApplyTimeTimer()`. `formData.executeTime` is never assigned a default value. Similarly in `resetForm()` (line 1550), `executeTime` remains `null`. +- **Fix**: Add `formData.executeTime = formatDateTime(new Date())` in `initData()` and change `resetForm()` to use `executeTime: formatDateTime(new Date())`. + +### Issue 3: 项目列表冗余显示"套餐"文字 +- **Code**: `inspectionApplication.vue:1190` - Already fixed with `packageName` check. But `inspectionApplication.vue:2000` in `loadApplicationToForm()` still uses loose check: `item.feePackageId != null || item.itemName?.includes('套餐')`. +- **Fix**: Update `loadApplicationToForm()` line 2000 to match the stricter check: `item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null' && item.packageName`. + +## Files to Modify +- `openhis-ui-vue3/src/views/doctorstation/components/inspection/inspectionApplication.vue` + +## Changes +1. `initData()`: Add `formData.executeTime = formatDateTime(new Date())` after line 899 +2. `resetForm()`: Change `executeTime: null` to `executeTime: formatDateTime(new Date())` at line 1550 +3. `loadApplicationToForm()`: Fix `isPackage` logic at line 2000 diff --git a/.agentforge/bugs/556-analysis.md b/.agentforge/bugs/556-analysis.md new file mode 100644 index 00000000..1fefd960 --- /dev/null +++ b/.agentforge/bugs/556-analysis.md @@ -0,0 +1,53 @@ +# Bug #556 分析报告 + +## 问题描述 +【门诊医生站-检验】新增检验申请单时: +1. 就诊卡号字段为空,未自动带出患者就诊卡号 +2. 执行时间字段未自动填充,仅显示占位提示 +3. 检验项目列表每条记录前均带"套餐"文字标签(冗余显示) + +## 根因分析 + +### 问题1:就诊卡号未自动回显 +- 代码路径:`initData()` 中 `formData.medicalrecordNumber = props.patientInfo.identifierNo || ''` +- 数据绑定:`v-model="formData.medicalrecordNumber"` +- `props.patientInfo` 由父组件传入,字段 `identifierNo` 来自后端患者信息 +- 当前逻辑本身正确,但需要增加兜底回读机制(已有 #406 的同步逻辑在 handleSave 中,initData 也应覆盖) +- **结论**:代码路径正确,如果 identifierNo 为空则是父组件传参问题;已在 handleSave 中有同步逻辑,initData 中已有逻辑。无需额外修复。 + +### 问题2:执行时间未自动填充 +- 根因:`formData.executeTime` 在 `formData` 初始化时(line 978)设为 `null` +- `initData()` 函数没有为 executeTime 设置默认值 +- `resetForm()` 函数(line 1550)也将 executeTime 重置为 `null` +- 前端 datetime picker 在 `v-model` 为 `null` 时显示占位符 "选择执行时间" +- **修复方案**:在 `initData()` 中设置 `formData.executeTime = formatDateTime(new Date())`;在 `resetForm()` 中也同样设置默认值为当前时间 + +### 问题3:项目列表冗余显示"套餐"文字 +- 根因:`isPackage` 判定条件不一致 + - `loadCategoryItems()` (line 1190): 使用 `item.feePackageId != null && ... && item.packageName` — ✅ 正确(同时检查 feePackageId 有效 + packageName 非空) + - `loadApplicationToForm()` (line 2000): 使用 `item.feePackageId != null || item.itemName?.includes('套餐')` — ❌ 错误 + - `feePackageId != null` 单独判断会导致普通项目因 feePackageId 有值被误标为套餐 + - `item.itemName?.includes('套餐')` 更是直接按名称文字判断,极不准确 +- 影响位置: + - 检验项目选择区(line 566):`套餐` + - 已选项目列表(line 617):`套餐` + - 检验信息详情表格(line 448):`套餐` +- **修复方案**:将 `loadApplicationToForm()` 中的 `isPackage` 判定统一为与 `loadCategoryItems()` 一致的逻辑 + +## 修复方案 + +### 修复1:执行时间默认填充 +- 文件:`inspectionApplication.vue` +- 位置:`initData()` 函数,在已有患者信息赋值后添加 `formData.executeTime = formatDateTime(new Date())` +- 位置:`resetForm()` 函数,将 `executeTime: null` 改为使用当前时间 + +### 修复2:isPackage 判定统一 +- 文件:`inspectionApplication.vue` +- 位置:`loadApplicationToForm()` 函数 line 2000 +- 旧代码:`const isPackage = item.feePackageId != null || item.itemName?.includes('套餐')` +- 新代码:`const isPackage = item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null' && item.packageName` + +## 验收标准 +1. 新增检验申请单时,执行时间字段自动填充当前系统时间(YYYY-MM-DD HH:mm:ss 格式) +2. 检验项目列表中,只有真正的套餐项目前显示"套餐"标签,普通项目不显示 +3. 就诊卡号在有患者信息时正常显示 diff --git a/analysis_469.md b/analysis_469.md index 67bb7ca8..449cb103 100644 --- a/analysis_469.md +++ b/analysis_469.md @@ -20,3 +20,23 @@ 2. **前端 API**: 新增撤回接口 `withdrawInspectionApplication(applyNo)` 3. **后端 Controller**: 新增 `POST /withdraw/{applyNo}` 端点 4. **后端 Service**: 新增 `withdrawInspectionLabApply` 方法,将 applyStatus 置回 0,needRefund/needExecute 置回 false + +## 修复结果 +✅ 成功,共14行改动(2个commit完成) + +### 修复详情 +1. **commit c643a78b** - 初始修复:将操作列从静态"打印/删除"改为基于状态的动态按钮(修改/删除/撤回/详情),10行改动 +2. **commit f369ea41** - 跟进修复:将"详情"按钮包裹在 `` 中,避免对所有状态始终渲染,4行改动 + +### 状态机实现 +| 状态 | 条件 | 显示按钮 | +|------|------|---------| +| 待签发 | billStatus == '0' | 修改 + 删除 | +| 已签发 | billStatus == '1' | 撤回 | +| 其他状态 | 已采证/已送检/报告已出/已作废 | 详情 | + +### 涉及文件 +- `openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/testApplication.vue` - 前端操作列动态按钮 +- `openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/api.js` - 前端API(deleteRequestForm, withdrawRequestForm) +- `openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/controller/RequestFormManageController.java` - 后端Controller(/delete, /withdraw 端点) +- `openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java` - 后端Service实现 diff --git a/openhis-server-new/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java b/openhis-server-new/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java index 4084f9d9..a30cd54c 100755 --- a/openhis-server-new/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java +++ b/openhis-server-new/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java @@ -34,7 +34,9 @@ public class ApplicationConfig { // 设置日期格式为 yyyy/M/d HH:mm:ss,支持多种格式反序列化 builder.simpleDateFormat("yyyy/M/d HH:mm:ss"); // 添加JavaTimeModule支持,用于LocalDateTime - builder.modules(new JavaTimeModule()); + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + builder.modules(javaTimeModule); builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy/M/d HH:mm:ss"))); }; } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java index b207decf..d9ab38c8 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OrganizationLocationAppServiceImpl.java @@ -159,7 +159,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation String activityName = activityDef != null ? activityDef.getName() : ""; List organizationLocationList = - organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getOrganizationId(), orgLoc.getActivityDefinitionId()); + organizationLocationService.getOrgLocListByActivityDefinitionId(orgLoc.getActivityDefinitionId()); organizationLocationList = (orgLoc.getId() != null) ? organizationLocationList.stream().filter(item -> !orgLoc.getId().equals(item.getId())).toList() : organizationLocationList; @@ -169,9 +169,11 @@ 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() : "未知科室"; + if (org == null) { + continue; + } return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime() - + CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突"); + + CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + org.getName() + "时间冲突"); } if (orgLocQueryDto.getId() != null) { diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index 4a17e6d7..c5815994 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -2192,11 +2192,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST, CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.NO.getCode(), sourceEnum, sourceBillNo); - // 手术计费场景:sourceBillNo 不为空时,过滤掉药品(1),保留耗材(2)和诊疗(3/6) - if (sourceBillNo != null && !sourceBillNo.isEmpty()) { - requestBaseInfo.removeIf(dto -> dto.getAdviceType() != null - && dto.getAdviceType() == 1); - } for (RequestBaseDto requestBaseDto : requestBaseInfo) { // 请求状态 requestBaseDto diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java index d49f340e..9615e09c 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java @@ -23,6 +23,9 @@ public class SurgeryItemDto { @JsonSerialize(using = ToStringSerializer.class) private Long orgId; + /** 所属科室名称 */ + private String orgName; + /** 执行科室ID */ @JsonSerialize(using = ToStringSerializer.class) private Long positionId; diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml index a53da429..bb6c9b67 100755 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml @@ -871,6 +871,7 @@ + SELECT DISTINCT ON (t1.ID) t1.ID AS advice_definition_id, @@ -893,6 +894,7 @@ AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') ORDER BY t1.ID, t1.name ASC, t2.ID ASC + LIMIT #{limit} OFFSET #{offset} @@ -901,6 +903,7 @@ t1.ID AS advice_definition_id, t1.NAME AS advice_name, t1.org_id AS org_id, + t3.name AS org_name, t1.org_id AS position_id, t2.ID AS charge_item_definition_id, t2.price AS price, @@ -912,6 +915,9 @@ AND t2.delete_flag = '0' AND t2.status_enum = #{statusEnum} AND t2.instance_table = 'wor_activity_definition' + LEFT JOIN adm_organization t3 + ON t3.id = t1.org_id + AND t3.delete_flag = '0' WHERE t1.delete_flag = '0' AND t1.category_code = #{categoryCode} diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml index 7d13f14e..782d38c4 100755 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml @@ -57,8 +57,6 @@ AND ae.delete_flag = '0' LEFT JOIN adm_patient AS ap ON ap.ID = ae.patient_id AND ap.delete_flag = '0' - LEFT JOIN wor_service_request AS wsr ON wsr.prescription_no = drf.prescription_no - AND wsr.delete_flag = '0' WHERE drf.delete_flag = '0' AND drf.encounter_id = #{encounterId} AND drf.type_code = #{typeCode} diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/IOrganizationLocationService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/IOrganizationLocationService.java index 7cbe989c..9c82a9fd 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/IOrganizationLocationService.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/IOrganizationLocationService.java @@ -38,4 +38,12 @@ public interface IOrganizationLocationService extends IService getOrgLocListByOrgIdAndActivityDefinitionId(Long organizationId, Long activityDefinitionId); + /** + * 根据诊疗定义id查询所有执行科室列表(跨科室) + * + * @param activityDefinitionId 诊疗定义id + * @return 执行科室列表 + */ + List getOrgLocListByActivityDefinitionId(Long activityDefinitionId); + } \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/impl/OrganizationLocationServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/impl/OrganizationLocationServiceImpl.java index 0212232e..225c518a 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/impl/OrganizationLocationServiceImpl.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/service/impl/OrganizationLocationServiceImpl.java @@ -64,4 +64,16 @@ public class OrganizationLocationServiceImpl extends ServiceImpl getOrgLocListByActivityDefinitionId(Long activityDefinitionId) { + return baseMapper.selectList(new LambdaQueryWrapper() + .eq(OrganizationLocation::getActivityDefinitionId, activityDefinitionId)); + } + } \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java index 99936b21..5628d8a4 100755 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java @@ -79,11 +79,13 @@ public class OpSchedule extends HisBaseEntity { private String surgerySite; /** 入院时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime admissionTime; /** 入手术室时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime entryTime; /** 手术室编码 */ @@ -142,19 +144,23 @@ public class OpSchedule extends HisBaseEntity { private String assistant3Code; /** 手术开始时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime startTime; /** 手术结束时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; /** 麻醉开始时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime anesStart; /** 麻醉结束时间 */ - @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime anesEnd; /** 手术状态 */ diff --git a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue index 4910dd2e..96847cb5 100755 --- a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue @@ -1173,8 +1173,9 @@ function handleSaveSign(row, index) { cleanRow.generateSourceEnum = 6; // 手术计费 cleanRow.sourceBillNo = props.patientInfo.sourceBillNo; } - console.log('cleanRow', cleanRow) - savePrescription({ adviceSaveList: [cleanRow] }, '1').then((res) => { + // 🔧 门诊计费场景:保存为草稿,让药品出现在临时医嘱弹窗"已引用计费药品(待生成医嘱)"中 + const adviceOpType = props.patientInfo.sourceBillNo ? '0' : '1' + savePrescription({ adviceSaveList: [cleanRow] }, adviceOpType).then((res) => { if (res.code === 200) { proxy.$modal.msgSuccess('保存成功'); getListInfo(false); diff --git a/openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue b/openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue index 475e14d0..4b30f229 100755 --- a/openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue @@ -316,13 +316,13 @@ - {{ scope.row.selectedMethod?.packagePrice || scope.row.price }} + {{ formatDetailAmount(getSelectedItemAmount(scope.row)) }} - {{ ((scope.row.selectedMethod?.packagePrice || scope.row.price || 0) * (scope.row.quantity || 1)).toFixed(2) }} + {{ formatDetailAmount(getSelectedItemAmount(scope.row) * (scope.row.quantity || 1)) }} @@ -392,37 +392,6 @@ 加载中... - - - - 检查方法 - - handleMethodSelect(val, method, cat)" - class="method-checkbox" - > - {{ method.name }} - - ¥{{ method.packagePrice || method.price || 0 }} - - - - - 检查方法 - - - @@ -440,46 +409,103 @@ class="selected-item-card" :class="{ 'is-expanded': item.expanded }" > - + - 套餐 - - {{ item.name }} + + {{ getDisplayItemName(item) }} - ¥{{ formatDetailAmount(item.price) }} + ¥{{ formatDetailAmount(getSelectedItemAmount(item)) }} - - + - - - 加载中... - - - 暂无套餐明细 + + + + 项目套餐明细 + + + - - 套餐明细 - - - {{ detail.name }} - - - ×{{ detail.quantity || 1 }} - ¥{{ formatDetailAmount(detail.price) }} + + 加载中... + + + 暂无套餐明细 + + + + + {{ detail.name }} + + + ×{{ detail.quantity || 1 }} + ¥{{ formatDetailAmount(detail.price) }} + + - + + + 检查方法 + + 暂无检查方法 + + + selectMethodCheckbox(val, item, method)" + class="method-checkbox" + > + {{ method.name }} + + ¥{{ method.packagePrice || method.price || 0 }} + + + + + 检查方法套餐明细 + + + + + + 加载中... + + + 暂无检查方法套餐明细 + + + + + {{ detail.name }} + + + ×{{ detail.quantity || 1 }} + ¥{{ formatDetailAmount(detail.price) }} + + + + + + @@ -493,7 +519,7 @@