From c3f1b105e99550207ede1c65028e23b3eaf0a02b Mon Sep 17 00:00:00 2001 From: Ranyunqiao <2499115710@qq.com> Date: Tue, 7 Apr 2026 15:36:27 +0800 Subject: [PATCH] =?UTF-8?q?301=20=E9=A2=84=E7=BA=A6=E7=AE=A1=E7=90=86-?= =?UTF-8?q?=E3=80=8B=E9=97=A8=E8=AF=8A=E9=A2=84=E7=BA=A6=E6=8C=82=E5=8F=B7?= =?UTF-8?q?=EF=BC=9A=E5=8F=B7=E6=BA=90=E4=BF=A1=E6=81=AF=E7=9A=84=E5=BA=8F?= =?UTF-8?q?=E5=8F=B7=E6=9C=AA=E8=BF=9B=E8=A1=8C=E5=8F=96=E5=80=BC=20316?= =?UTF-8?q?=E9=97=A8=E8=AF=8A=E5=8C=BB=E7=94=9F=E7=AB=99-=E3=80=8B?= =?UTF-8?q?=E5=8C=BB=E5=98=B1TAB=E9=A1=B5=E9=9D=A2=EF=BC=9A=E4=BC=9A?= =?UTF-8?q?=E8=AF=8A=E5=8C=BB=E5=98=B1=E7=8A=B6=E6=80=81=E4=BB=8E=E2=80=9C?= =?UTF-8?q?=E5=B7=B2=E7=AD=BE=E5=8F=91=E2=80=9D=E5=8F=98=E6=88=90=E2=80=9C?= =?UTF-8?q?=E8=8D=89=E7=A8=BF=E2=80=9D=20317=E3=80=90=E9=97=A8=E8=AF=8A?= =?UTF-8?q?=E5=8C=BB=E7=94=9F=E7=AB=99=E3=80=91=E5=B7=B2=E7=AD=BE=E5=8F=91?= =?UTF-8?q?=E4=BC=9A=E8=AF=8A=E5=8C=BB=E5=98=B1=E6=9C=AA=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=87=B3=E9=97=A8=E8=AF=8A=E6=94=B6=E8=B4=B9=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=BE=85=E6=94=B6=E8=B4=B9=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=20344=20=E9=97=A8=E8=AF=8A=E9=A2=84=E7=BA=A6=E6=8C=82=E5=8F=B7?= =?UTF-8?q?=EF=BC=9A=E6=9C=AA=E8=BF=87=E6=BB=A4=E8=BF=87=E6=9C=9F=E5=8F=B7?= =?UTF-8?q?=E6=BA=90=EF=BC=8C=E5=85=81=E8=AE=B8=E9=A2=84=E7=BA=A6=E5=B7=B2?= =?UTF-8?q?=E8=BF=87=E6=97=B6=E7=9A=84=E6=97=B6=E9=97=B4=E6=AE=B5=20347=20?= =?UTF-8?q?=E5=8C=BB=E7=94=9F=E9=97=A8=E8=AF=8A=E5=B7=A5=E4=BD=9C=E5=B7=B2?= =?UTF-8?q?=E5=B0=B1=E8=AF=8A=E7=9A=84=E7=97=85=E4=BA=BA=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=9C=AA=E5=B0=B1=E8=AF=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ConsultationAppServiceImpl.java | 70 +++++++++++-- .../DoctorStationAdviceAppServiceImpl.java | 54 +++++++--- .../outpatientAppointment/index.vue | 98 ++++++++++++++++--- 3 files changed, 189 insertions(+), 33 deletions(-) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java index 0b9fd2f2..f3d04340 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java @@ -421,6 +421,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 新增:更新门诊医嘱表状态为已提交 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.ACTIVE.getValue()); + + // 🎯 更新会诊关联费用项状态为"待收费",提交后即可在收费界面看到 + if (entity.getOrderId() != null) { + LambdaQueryWrapper chargeItemWrapper = new LambdaQueryWrapper<>(); + chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId()) + .eq(ChargeItem::getServiceTable, "wor_service_request"); + List chargeItems = iChargeItemService.list(chargeItemWrapper); + for (ChargeItem chargeItem : chargeItems) { + chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); + iChargeItemService.updateById(chargeItem); + } + log.info("会诊提交,更新关联费用项状态为待收费,更新数量: {}", chargeItems.size()); + } + return true; } catch (Exception e) { log.error("提交会诊申请失败", e); @@ -464,6 +478,18 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 更新门诊医嘱表状态为新开 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.DRAFT.getValue()); + // 更新关联费用项状态为草稿 + if (entity.getOrderId() != null) { + LambdaQueryWrapper chargeItemWrapper = new LambdaQueryWrapper<>(); + chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId()) + .eq(ChargeItem::getServiceTable, "wor_service_request"); + List chargeItems = iChargeItemService.list(chargeItemWrapper); + for (ChargeItem chargeItem : chargeItems) { + chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); + iChargeItemService.updateById(chargeItem); + } + } + } else { // 作废:状态校验 - 已确认(20)、已签名(30)、已完成(40) 状态禁止作废 ConsultationStatusEnum currentStatus = ConsultationStatusEnum.getByCode(entity.getConsultationStatus()); @@ -480,6 +506,18 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 更新门诊医嘱表状态为已作废 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.CANCELLED.getValue()); + // 更新关联费用项状态为终止 + if (entity.getOrderId() != null) { + LambdaQueryWrapper chargeItemWrapper = new LambdaQueryWrapper<>(); + chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId()) + .eq(ChargeItem::getServiceTable, "wor_service_request"); + List chargeItems = iChargeItemService.list(chargeItemWrapper); + for (ChargeItem chargeItem : chargeItems) { + chargeItem.setStatusEnum(ChargeItemStatus.ABORTED.getValue()); + iChargeItemService.updateById(chargeItem); + } + } + } return true; @@ -668,12 +706,14 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { @Override public List getMyInvitations() { try { - // 获取当前登录医生ID + // 获取当前登录医生ID和租户ID Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + Long tenantId = SecurityUtils.getLoginUser().getTenantId().longValue(); // 查询邀请我的会诊申请 LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); - invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) + invitedWrapper.eq(ConsultationInvited::getTenantId, tenantId) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) .orderByDesc(ConsultationInvited::getCreateTime); List invitedList = consultationInvitedMapper.selectList(invitedWrapper); @@ -1201,15 +1241,17 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { @Override public List getPendingConfirmationList() { try { - // 获取当前登录医生ID + // 获取当前登录医生ID和租户ID Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + Long tenantId = SecurityUtils.getLoginUser().getTenantId().longValue(); log.info("获取待确认会诊列表,当前医生ID: {}", currentPhysicianId); // 🎯 关键修改:查询当前医生个人状态为"待确认"、"已确认"或"已签名"的邀请记录 // 10=已提交(待确认)、20=已确认(待签名)、30=已签名,排除40=已完成 LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); - invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) - .in(ConsultationInvited::getInvitedStatus, + invitedWrapper.eq(ConsultationInvited::getTenantId, tenantId) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) + .in(ConsultationInvited::getInvitedStatus, ConsultationStatusEnum.SUBMITTED.getCode(), // 10-待确认 ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认(待签名) ConsultationStatusEnum.SIGNED.getCode()) // 30-已签名 @@ -1233,7 +1275,8 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 🎯 查询会诊申请详情(白名单:只查询正在进行中的会诊,明确业务范围) // 查询已提交、已确认、已签名状态的会诊,排除已完成(40) LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); - requestWrapper.in(ConsultationRequest::getId, requestIds) + requestWrapper.eq(ConsultationRequest::getTenantId, tenantId) + .in(ConsultationRequest::getId, requestIds) .in(ConsultationRequest::getConsultationStatus, ConsultationStatusEnum.SUBMITTED.getCode(), // 10-已提交 ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认 @@ -1633,7 +1676,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 更新医嘱状态为"已完成" updateServiceRequestStatus(request.getOrderId(), RequestStatus.COMPLETED.getValue()); - + + // 🎯 更新会诊关联费用项状态为"待收费",这样收费界面就能看到了 + if (request.getOrderId() != null) { + LambdaQueryWrapper chargeItemWrapper = new LambdaQueryWrapper<>(); + chargeItemWrapper.eq(ChargeItem::getServiceId, request.getOrderId()) + .eq(ChargeItem::getServiceTable, "wor_service_request"); + List chargeItems = iChargeItemService.list(chargeItemWrapper); + for (ChargeItem chargeItem : chargeItems) { + chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); + iChargeItemService.updateById(chargeItem); + } + log.info("会诊完成,更新关联费用项状态为待收费,更新数量: {}", chargeItems.size()); + } + log.info("所有医生都已签名,会诊申请状态更新为:已签名(30)"); } else { // 🎯 关键修改:部分医生签名,整体状态不变(保持为10或20) 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 bb54a60b..0478cf85 100644 --- 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 @@ -573,12 +573,13 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // 1. 校验就诊状态:必须是已接诊状态 Encounter encounterCheck = iEncounterService.getById(adviceSaveDto.getEncounterId()); if (encounterCheck != null) { - // 就诊状态:1001=挂号,1002=已接诊,1003=已收费,1004=已完成 - if (encounterCheck.getStatusEnum() != null && - encounterCheck.getStatusEnum() != 1002 && - encounterCheck.getStatusEnum() != 1003 && - encounterCheck.getStatusEnum() != 1004) { - log.error("BugFix#338: 患者未接诊,禁止划价/保存医嘱:encounterId={}, status={}", + // 就诊状态:1=待诊(PLANNED),允许保存的状态 = 2(IN_PROGRESS在诊)、3(ON_HOLD暂离)、4(DISCHARGED诊毕)、5(COMPLETED完成) + if (encounterCheck.getStatusEnum() != null && + encounterCheck.getStatusEnum() != EncounterStatus.IN_PROGRESS.getValue() && + encounterCheck.getStatusEnum() != EncounterStatus.ON_HOLD.getValue() && + encounterCheck.getStatusEnum() != EncounterStatus.DISCHARGED.getValue() && + encounterCheck.getStatusEnum() != EncounterStatus.COMPLETED.getValue()) { + log.error("BugFix#338: 患者未接诊,禁止划价/保存医嘱:encounterId={}, status={}", adviceSaveDto.getEncounterId(), encounterCheck.getStatusEnum()); return R.fail(null, "患者尚未接诊,无法保存医嘱。请先完成接诊操作!"); } @@ -967,13 +968,20 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // 处理耗材发放 Long dispenseId = iDeviceDispenseService.handleDeviceDispense(deviceRequest, DbOpType.INSERT.getCode()); - // 查询耗材定价信息 - AdviceBaseDto deviceAdviceDto = new AdviceBaseDto(); - deviceAdviceDto.setAdviceDefinitionId(boundDevice.getDevActId()); - deviceAdviceDto.setAdviceTableName(CommonConstants.TableName.ADM_DEVICE_DEFINITION); - IPage devicePage = getAdviceBaseInfo(deviceAdviceDto, null, null, null, - adviceSaveDto.getFounderOrgId(), 1, 1, Whether.NO.getValue(), - List.of(ItemType.DEVICE.getValue()), null, null); + // 查询耗材定价信息 - 直接使用mapper查询,避免递归调用getAdviceBaseInfo导致栈溢出 + IPage devicePage = doctorStationAdviceAppMapper.getAdviceBaseInfo( + new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, 1), + PublicationStatus.ACTIVE.getValue(), + adviceSaveDto.getFounderOrgId(), + null, + CommonConstants.TableName.ADM_DEVICE_DEFINITION, + null, + null, + List.of(boundDevice.getDevActId()), + null, + null, + null, + null); if (devicePage == null || devicePage.getRecords().isEmpty()) { log.warn("无法找到耗材定价信息: deviceDefId={}", boundDevice.getDevActId()); @@ -981,12 +989,19 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp } AdviceBaseDto deviceBaseInfo = devicePage.getRecords().get(0); - if (deviceBaseInfo.getPriceList() == null || deviceBaseInfo.getPriceList().isEmpty()) { + + // 查询价格信息 - 直接查询定价主表 + List mainCharge = doctorStationAdviceAppMapper.getMainCharge( + List.of(deviceBaseInfo.getChargeItemDefinitionId()), PublicationStatus.ACTIVE.getValue()); + + if (mainCharge == null || mainCharge.isEmpty()) { log.warn("耗材没有定价信息: deviceDefId={}", boundDevice.getDevActId()); continue; } - AdvicePriceDto devicePrice = deviceBaseInfo.getPriceList().get(0); + AdvicePriceDto devicePrice = mainCharge.get(0); + devicePrice.setDefinitionId(deviceBaseInfo.getChargeItemDefinitionId()); + // 如果需要定价子表ID,可以从mainCharge中获取 // 创建耗材费用项 ChargeItem deviceChargeItem = new ChargeItem(); @@ -1554,6 +1569,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // } // log.error(e.getMessage(), e); // } + // 签发时将收费项目状态从草稿改为待收费 + Long chargeItemId = adviceSaveDto.getChargeItemId(); + if (chargeItemId != null) { + ChargeItem existingChargeItem = iChargeItemService.getById(chargeItemId); + if (existingChargeItem != null) { + existingChargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); + iChargeItemService.updateById(existingChargeItem); + } + } } } } diff --git a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue index b452581b..38daac7f 100644 --- a/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue +++ b/openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue @@ -315,25 +315,105 @@ export default { computed: { filteredDoctors() { let filtered = [...this.doctors]; - + // 根据号源类型过滤医生列表 if (this.selectedType === 'general') { filtered = filtered.filter(doctor => doctor.type === 'general'); } else if (this.selectedType === 'expert') { filtered = filtered.filter(doctor => doctor.type === 'expert'); } - + // 根据搜索关键词过滤 if (this.searchQuery) { - filtered = filtered.filter(doctor => + filtered = filtered.filter(doctor => doctor.name.includes(this.searchQuery) ); } - + + // 🎯 实时更新余号数量:统计该医生当前筛选条件下剩余可预约(未预约 + 未过期)号源数量 + const availableCountMap = {}; + this.filteredAndSortedTickets.forEach(ticket => { + const doctorId = String(ticket.doctorId || ticket.doctor_id); + if (!availableCountMap[doctorId]) { + availableCountMap[doctorId] = 0; + } + // 只有未预约的号源才算作可预约余号 + if (ticket.status === '未预约') { + availableCountMap[doctorId]++; + } + }); + + // 更新每个医生的余号数量 + filtered = filtered.map(doctor => { + const actualAvailable = availableCountMap[String(doctor.id)] || 0; + return { + ...doctor, + available: actualAvailable + }; + }); + return filtered; }, + // 过滤并排序后的完整号源列表 + filteredAndSortedTickets() { + let filtered = [...this.tickets]; + + // 🎯 精确过滤:过滤掉早于当前时间的号源 + // - 日期早于今天 → 过滤 + // - 日期是今天但号源时段已经开始(开始时间早于当前时间) → 过滤 + const now = new Date(); + + filtered = filtered.filter(ticket => { + // dateTime 格式示例:"2024-01-01 08:00-09:00" + const parts = (ticket.dateTime || '').split(' '); + if (parts.length < 2) return true; // 如果格式不正确,保留显示 + + const dateStr = parts[0]; + const timeRangeStr = parts[1]; + if (!dateStr || !timeRangeStr) return true; + + // 提取开始时间 + const startTimeStr = timeRangeStr.split('-')[0]; // "08:00" + if (!startTimeStr) return true; + + // 构建号源开始时间的完整 Date 对象 + const ticketStartStr = `${dateStr} ${startTimeStr}`; + const ticketStart = new Date(ticketStartStr); + + // 只显示开始时间晚于当前时间的号源 + return ticketStart > now; + }); + + // 🎯 按开始时间升序排序 → 较早的号源排在前面 + filtered.sort((a, b) => { + const getStartTime = (ticket) => { + const parts = (ticket.dateTime || '').split(' '); + if (parts.length < 2) return new Date(0).getTime(); + const dateStr = parts[0]; + const timeRangeStr = parts[1]; + const startTimeStr = (timeRangeStr || '').split('-')[0]; + if (!startTimeStr) return new Date(0).getTime(); + const ticketStartStr = `${dateStr} ${startTimeStr}`; + return new Date(ticketStartStr).getTime(); + }; + + const timeA = getStartTime(a); + const timeB = getStartTime(b); + return timeA - timeB; + }); + + return filtered; + }, + // 🎯 分页:按照用户选择的每页条数分页,返回当前页的数据 filteredTickets() { - return [...this.tickets]; + const filtered = this.filteredAndSortedTickets; + const startIndex = (this.currentPage - 1) * this.pageSize; + const endIndex = startIndex + this.pageSize; + return filtered.slice(startIndex, endIndex); + }, + // 更新总条数为过滤后的实际条数,分页自动处理 + totalTickets() { + return this.filteredAndSortedTickets.length; }, hasSearchCriteria() { return !!this.patientKeyword?.trim(); @@ -733,12 +813,8 @@ export default { const total = Number(payload.total); this.tickets = [...filteredRecords]; this.allTickets = [...filteredRecords]; - // 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部” - if (this.selectedStatus && this.selectedStatus !== 'all') { - this.totalTickets = this.tickets.length; - } else { - this.totalTickets = Number.isFinite(total) ? total : this.tickets.length; - } + // 后端已经分页,总条数由后端返回,始终使用后端返回的total + this.totalTickets = Number.isFinite(total) ? total : this.tickets.length; }, applyStatusFilter(records = []) { if (!Array.isArray(records) || records.length === 0) {