diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/document/appservice/impl/DocRecordAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/document/appservice/impl/DocRecordAppServiceImpl.java index f51b45fdf..34e9628e4 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/document/appservice/impl/DocRecordAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/document/appservice/impl/DocRecordAppServiceImpl.java @@ -979,11 +979,29 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService { "SELECT start_time,end_time FROM adm_encounter WHERE id = ? AND patient_id = ? AND status_enum = ? AND class_enum = ?"; Object[] params = {encounterId, patientId, EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue(), EncounterClass.IMP.getValue()}; - Map result = jdbcTemplate.queryForMap(sql, params); - HashMap map = new HashMap<>(); - map.put("hospDate", (Timestamp)result.get("start_time")); - map.put("outTime", (Timestamp)result.get("end_time")); - return map; + try { + Map result = jdbcTemplate.queryForMap(sql, params); + HashMap map = new HashMap<>(); + map.put("hospDate", (Timestamp)result.get("start_time")); + map.put("outTime", (Timestamp)result.get("end_time")); + return map; + } catch (org.springframework.dao.EmptyResultDataAccessException e) { + try { + String fallbackSql = "SELECT start_time,end_time FROM adm_encounter WHERE id = ? AND patient_id = ? AND class_enum = ?"; + Object[] fallbackParams = {encounterId, patientId, EncounterClass.IMP.getValue()}; + Map result = jdbcTemplate.queryForMap(fallbackSql, fallbackParams); + HashMap map = new HashMap<>(); + map.put("hospDate", (Timestamp)result.get("start_time")); + map.put("outTime", (Timestamp)result.get("end_time")); + return map; + } catch (Exception ex) { + log.warn("Querying adm_encounter failed: ", ex); + HashMap map = new HashMap<>(); + map.put("hospDate", new Date()); + map.put("outTime", null); + return map; + } + } } /** @@ -996,8 +1014,15 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService { String sql = "SELECT ael.start_time FROM adm_encounter ae INNER JOIN adm_encounter_location ael ON ae.ID=ael.encounter_id AND ael.form_enum=? AND ael.status_enum=? AND ael.delete_flag='0' AND ael.tenant_id=1 LEFT JOIN adm_location al ON ael.location_id=al.ID AND al.delete_flag='0' AND al.tenant_id=1 WHERE ae.ID=? AND ae.delete_flag='0' AND ae.tenant_id=1"; Object[] params = {LocationForm.BED.getValue(), EncounterActivityStatus.ACTIVE.getValue(), encounterId}; - Timestamp timestamp = jdbcTemplate.queryForObject(sql, params, Timestamp.class); - return Date.from(timestamp.toInstant()); + try { + List list = jdbcTemplate.queryForList(sql, Timestamp.class, params); + if (list != null && !list.isEmpty() && list.get(0) != null) { + return Date.from(list.get(0).toInstant()); + } + } catch (Exception e) { + log.warn("Querying location admission date failed: ", e); + } + return new Date(); } /** diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java index 3b14df260..0c1182f3c 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java @@ -115,4 +115,13 @@ public interface IATDManageAppService { * */ R getPendingMedication(Long encounterId); + + /** + * 退床 (取消分床) + * + * @param encounterId 住院患者id + * @return 结果 + */ + R cancelBedAssignment(Long encounterId); } + diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IAdviceProcessAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IAdviceProcessAppService.java index 6535d9800..b3c0edac7 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IAdviceProcessAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IAdviceProcessAppService.java @@ -59,6 +59,14 @@ public interface IAdviceProcessAppService { */ R adviceReject(List performInfoList); + /** + * 撤销医嘱校对 + * + * @param performInfoList 医嘱信息集合 + * @return 操作结果 + */ + R adviceCancelVerify(List performInfoList); + /** * 医嘱执行 * diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java index e9dcc595e..4609bd1f8 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java @@ -13,6 +13,8 @@ import com.core.common.utils.DateUtils; import com.core.common.utils.SecurityUtils; import com.core.common.utils.StringUtils; import com.healthlink.his.administration.domain.Encounter; +import com.healthlink.his.administration.domain.ChargeItem; +import com.healthlink.his.administration.service.IChargeItemService; import com.healthlink.his.administration.domain.EncounterLocation; import com.healthlink.his.administration.domain.EncounterParticipant; import com.healthlink.his.administration.domain.Practitioner; @@ -115,6 +117,9 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { @Resource private ApplicationEventPublisher eventPublisher; + @Resource + private IChargeItemService chargeItemService; + /** * 入出转管理页面初始化 * @@ -1002,4 +1007,85 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { docStatisticsAppService.saveOrUpdateAdmissionSigns(list); } } + + /** + * 退床 (取消分床) + * + * @param encounterId 住院患者id + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R cancelBedAssignment(Long encounterId) { + if (encounterId == null) { + return R.fail("退床失败,请选择有效的就诊记录"); + } + Encounter encounter = encounterService.getById(encounterId); + if (encounter == null) { + return R.fail("未找到该住院就诊记录"); + } + // 仅已入院状态允许退床 + if (!EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue().equals(encounter.getStatusEnum())) { + return R.fail("该患者未在科,无法办理退床"); + } + + // 校验是否产生了医嘱或计费 + // 1. 检查药品医嘱 + long medCount = medicationRequestService.count( + new LambdaQueryWrapper() + .eq(MedicationRequest::getEncounterId, encounterId) + .eq(MedicationRequest::getDeleteFlag, DelFlag.NO.getCode())); + if (medCount > 0) { + return R.fail("患者已产生医嘱或计费,无法直接退床"); + } + + // 2. 检查诊疗医嘱 + long svcCount = serviceRequestService.count( + new LambdaQueryWrapper() + .eq(ServiceRequest::getEncounterId, encounterId) + .eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode())); + if (svcCount > 0) { + return R.fail("患者已产生医嘱或计费,无法直接退床"); + } + + // 3. 检查耗材医嘱 + long devCount = deviceRequestService.count( + new LambdaQueryWrapper() + .eq(DeviceRequest::getEncounterId, encounterId) + .eq(DeviceRequest::getDeleteFlag, DelFlag.NO.getCode())); + if (devCount > 0) { + return R.fail("患者已产生医嘱或计费,无法直接退床"); + } + + // 4. 检查计费记录 + long chargeCount = chargeItemService.count( + new LambdaQueryWrapper() + .eq(ChargeItem::getEncounterId, encounterId)); + if (chargeCount > 0) { + return R.fail("患者已产生医嘱或计费,无法直接退床"); + } + + // 更新原病床状态为 空闲 (LocationStatus.IDLE) + List bedLocations = encounterLocationService.getEncounterLocationList(encounterId, + LocationForm.BED, EncounterActivityStatus.ACTIVE); + if (bedLocations != null && !bedLocations.isEmpty()) { + for (EncounterLocation bedLoc : bedLocations) { + locationService.updateStatusById(bedLoc.getLocationId(), LocationStatus.IDLE.getValue()); + } + } + + // 更新病床和病房就诊位置状态为已完成 (EncounterActivityStatus.COMPLETED) + // isTransfer 为 false,不更新病区 WARD,以保留患者的病区归属,从而能继续在入科列表中显示并重新分床 + encounterLocationService.updateEncounterLocationStatus(encounterId, false); + + // 更新医疗参与者(住院医生、责任护士等)状态为已完成 + encounterParticipantService.updateEncounterParticipantsStatus(encounterId); + + // 回滚住院状态为 待入科 (EncounterZyStatus.REGISTERED) + encounter.setStatusEnum(EncounterZyStatus.REGISTERED.getValue()); + encounterService.saveOrUpdateEncounter(encounter); + + return R.ok("退床成功"); + } } + diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/AdviceProcessAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/AdviceProcessAppServiceImpl.java index 1186f23b5..768fa79d5 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/AdviceProcessAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/AdviceProcessAppServiceImpl.java @@ -601,6 +601,102 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService { return R.ok(null, "退回成功"); } + /** + * 撤销医嘱校对 + * + * @param performInfoList 医嘱信息集合 + * @return 操作结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R adviceCancelVerify(List performInfoList) { + if (performInfoList == null || performInfoList.isEmpty()) { + return R.fail("请先选择医嘱信息"); + } + + // 分别创建列表来存储不同类型的请求 + List serviceRequestList = new ArrayList<>(); + List medRequestList = new ArrayList<>(); + List deviceRequestList = new ArrayList<>(); + for (PerformInfoDto item : performInfoList) { + if (CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(item.getRequestTable())) { + serviceRequestList.add(item); + } else if (CommonConstants.TableName.MED_MEDICATION_REQUEST.equals(item.getRequestTable())) { + medRequestList.add(item); + } else if (CommonConstants.TableName.WOR_DEVICE_REQUEST.equals(item.getRequestTable())) { + deviceRequestList.add(item); + } + } + + List allRequestIds = performInfoList.stream().map(PerformInfoDto::getRequestId).toList(); + + // 校验①:校验医嘱是否已执行。若医嘱状态已经是“已执行”,则不允许撤销校对。 + List allProcedures = procedureService.list( + new LambdaQueryWrapper() + .in(Procedure::getRequestId, allRequestIds) + .eq(Procedure::getDeleteFlag, "0")); + Set executedIds = allProcedures.stream() + .filter(p -> EventStatus.COMPLETED.getValue().equals(p.getStatusEnum())) + .map(Procedure::getId) + .collect(Collectors.toSet()); + Set cancelledRefundIds = allProcedures.stream() + .filter(p -> EventStatus.CANCEL.getValue().equals(p.getStatusEnum()) && p.getRefundId() != null) + .map(Procedure::getRefundId) + .collect(Collectors.toSet()); + executedIds.removeAll(cancelledRefundIds); + if (!executedIds.isEmpty()) { + return R.fail("该医嘱已执行,无法撤销校对,请先去医嘱执行模块取消执行"); + } + + // 校验②:校验该医嘱是否已记账扣费。若已扣费,则不允许撤销校对。 + List chargeItems = chargeItemService.getChargeItemInfoByReqId(allRequestIds); + boolean isBilled = chargeItems.stream().anyMatch(ci -> ChargeItemStatus.BILLED.getValue().equals(ci.getStatusEnum())); + if (isBilled) { + return R.fail("该医嘱已记账收费,若需撤销请先进行退费/计账回滚"); + } + + // 校验③:若为药品医嘱,校验药房是否已发药(配药)。若已发药,则不允许撤销校对。 + if (!medRequestList.isEmpty()) { + List medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList(); + List dispenseList = medicationDispenseService.list( + new LambdaQueryWrapper() + .in(MedicationDispense::getMedReqId, medReqIds) + .in(MedicationDispense::getStatusEnum, Arrays.asList( + DispenseStatus.COMPLETED.getValue(), + DispenseStatus.PREPARED.getValue(), + DispenseStatus.PART_COMPLETED.getValue() + ))); + if (!dispenseList.isEmpty()) { + return R.fail("药房已发药,请先进行退药申请"); + } + } + + // 满足所有校验,执行撤销校对(回退至“未校对”,即 ACTIVE 状态) + if (!serviceRequestList.isEmpty()) { + serviceRequestService.update(new LambdaUpdateWrapper() + .in(ServiceRequest::getId, serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList()) + .set(ServiceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue()) + .set(ServiceRequest::getPerformerCheckId, null) + .set(ServiceRequest::getCheckTime, null)); + } + if (!medRequestList.isEmpty()) { + medicationRequestService.update(new LambdaUpdateWrapper() + .in(MedicationRequest::getId, medRequestList.stream().map(PerformInfoDto::getRequestId).toList()) + .set(MedicationRequest::getStatusEnum, RequestStatus.ACTIVE.getValue()) + .set(MedicationRequest::getPerformerCheckId, null) + .set(MedicationRequest::getCheckTime, null)); + } + if (!deviceRequestList.isEmpty()) { + deviceRequestService.update(new LambdaUpdateWrapper() + .in(DeviceRequest::getId, deviceRequestList.stream().map(PerformInfoDto::getRequestId).toList()) + .set(DeviceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue()) + .set(DeviceRequest::getPerformerCheckId, null) + .set(DeviceRequest::getCheckTime, null)); + } + + return R.ok(null, "撤销校对成功"); + } + /** * 医嘱执行 * diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java index b96551b7e..e143098ae 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java @@ -166,4 +166,16 @@ public class ATDManageController { public R getPendingMedication(Long encounterId) { return atdManageAppService.getPendingMedication(encounterId); } + + /** + * 退床 (取消分床) + * + * @param encounterId 住院患者id + * @return 结果 + */ + @PutMapping(value = "/cancel-bed-assignment") + public R cancelBedAssignment(Long encounterId) { + return atdManageAppService.cancelBedAssignment(encounterId); + } } + diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/AdviceProcessController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/AdviceProcessController.java index 81af46739..17d50363e 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/AdviceProcessController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/AdviceProcessController.java @@ -87,6 +87,17 @@ public class AdviceProcessController { return adviceProcessAppService.adviceReject(performInfoList); } + /** + * 撤销医嘱校对 + * + * @param performInfoList 医嘱信息集合 + * @return 操作结果 + */ + @PutMapping(value = "/advice-cancel-verify") + public R adviceCancelVerify(@RequestBody List performInfoList) { + return adviceProcessAppService.adviceCancelVerify(performInfoList); + } + /** * 医嘱执行 * diff --git a/healthlink-his-ui/src/components/TableLayout/Filter.vue b/healthlink-his-ui/src/components/TableLayout/Filter.vue index f96e69b46..1fcb85b0d 100755 --- a/healthlink-his-ui/src/components/TableLayout/Filter.vue +++ b/healthlink-his-ui/src/components/TableLayout/Filter.vue @@ -50,6 +50,7 @@ > 重置 + + + @@ -109,7 +119,7 @@ import Filter from '@/components/TableLayout/Filter.vue'; import {computed, onBeforeMount, onMounted, reactive, ref} from 'vue'; import TransferInDialog from './transferInDialog.vue'; import SignEntryDialog from './signEntryDialog.vue'; -import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard} from './api'; +import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard, cancelBedAssignment} from './api'; import {ElLoading, ElMessage, ElMessageBox} from 'element-plus'; import PendingPatientList from '@/components/PendingPatientList/index.vue'; @@ -161,7 +171,7 @@ const selectHoouseLoding = ref(true); const bedStatusFilter = ref(''); const activePatientId = computed(() => { const active = patientList.value?.find?.((it) => it?.active); - return active?.id || ''; + return active?.encounterId || ''; }); const filterItems = computed(() => [ @@ -389,24 +399,11 @@ const handleTransferInOk = async () => { await getList(); }; -// 单击患者卡片事件 - 直接触发入科选床界面 +// 单击患者卡片事件 - 仅高亮选中该患者 function handleCardClick(item: any, index: number) { - if (item.encounterStatus == 2) { - // 显示提示信息,指导用户如何分配床位 - ElMessage({ - message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位', - type: 'warning', - grouping: true, - showClose: true, - }); - } else { - pendingInfo.value = { - ...item, - entranceType: 1, - }; - - transferInDialogVisible.value = true; - } + patientList.value.forEach((p) => { + p.active = p.encounterId === item.encounterId; + }); } // 双击患者卡片事件 - 保持原有逻辑 @@ -429,6 +426,50 @@ function handleCardDblClick(item: any) { } } +// 退床操作 (取消分床) +const handleCancelBedAssignment = async () => { + const activePatient = patientList.value?.find?.((it) => it?.active); + if (!activePatient) { + ElMessage.warning('请先从左侧患者列表点击选中一名需要退床的已入院患者'); + return; + } + if (activePatient.encounterStatus != 5) { + ElMessage.warning('选中的患者未在科,无法办理退床'); + return; + } + + try { + await ElMessageBox.confirm( + `确定要对患者【${activePatient.patientName || ''}】办理退床操作吗?退床后,该患者将回滚为待入科状态。`, + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + } + ); + + const loadingInstance = ElLoading.service({ fullscreen: true, text: '正在办理退床...' }); + try { + const res = await cancelBedAssignment(activePatient.encounterId); + if (res.code === 200) { + ElMessage.success(res.msg || '退床成功'); + // 重新加载列表数据 + await getList(); + } else { + ElMessage.error(res.msg || '退床失败'); + } + } catch (err) { + console.error('退床失败:', err); + } finally { + loadingInstance.close(); + } + } catch (cancel) { + // 用户取消了操作 + } +}; + + // 拖拽开始事件 function handleDragStart(event: DragEvent, item: any) { if (event.dataTransfer) { diff --git a/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/api.js b/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/api.js index 0a03e6c52..ba2a719cc 100755 --- a/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/api.js +++ b/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/api.js @@ -75,4 +75,15 @@ export function adviceNoExecute(data) { method: 'put', data: data }) +} + +/** + * 撤销医嘱校对 + */ +export function adviceCancelVerify(data) { + return request({ + url: '/nurse-station/advice-process/advice-cancel-verify', + method: 'put', + data: data + }) } \ No newline at end of file diff --git a/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/prescriptionList.vue b/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/prescriptionList.vue index df814cd11..e6aafe214 100755 --- a/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/prescriptionList.vue +++ b/healthlink-his-ui/src/views/inpatientNurse/medicalOrderProofread/components/prescriptionList.vue @@ -67,19 +67,13 @@ 退回 - + @@ -412,7 +406,7 @@