91 分诊排队管理-》门诊医生站:【完诊】患者队列状态的变化

This commit is contained in:
Ranyunqiao
2026-04-02 15:23:42 +08:00
parent 08085403b3
commit 4ef9aa07d2
4 changed files with 203 additions and 8 deletions

View File

@@ -1,6 +1,7 @@
package com.openhis.web.doctorstation.appservice.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -16,21 +17,23 @@ import com.openhis.common.constant.CommonConstants;
import com.openhis.common.enums.*;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.web.doctorstation.appservice.*;
import com.openhis.web.doctorstation.dto.PatientInfoDto;
import com.openhis.web.doctorstation.dto.PrescriptionInfoBaseDto;
import com.openhis.web.doctorstation.dto.PrescriptionInfoDetailDto;
import com.openhis.web.doctorstation.dto.ReceptionStatisticsDto;
import com.openhis.web.doctorstation.mapper.DoctorStationMainAppMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
@@ -64,6 +67,9 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private TriageQueueItemService triageQueueItemService;
/**
* 查询就诊患者信息
*
@@ -124,14 +130,40 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> receiveEncounter(Long encounterId) {
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
String currentUsername = SecurityUtils.getUsername();
// 检查就诊记录是否存在
Encounter encounter = encounterMapper.selectById(encounterId);
if (encounter == null) {
return R.fail("就诊记录不存在");
}
// 检查患者状态,防止重复接诊
Integer currentStatus = encounter.getStatusEnum();
if (EncounterStatus.IN_PROGRESS.getValue().equals(currentStatus)) {
return R.fail("已接诊,请勿重复点击,已为您刷新");
}
int update = encounterMapper.update(null,
new LambdaUpdateWrapper<Encounter>().eq(Encounter::getId, encounterId)
.eq(Encounter::getStatusEnum, EncounterStatus.PLANNED.getValue()) // 只更新待诊状态的患者
.set(Encounter::getReceptionTime, new Date())
.set(Encounter::getStatusEnum, EncounterStatus.IN_PROGRESS.getValue())
.set(Encounter::getSubjectStatusEnum, EncounterSubjectStatus.RECEIVING_CARE.getValue()));
// 如果更新失败,说明状态已被其他医生修改
if (update <= 0) {
// 重新查询当前状态
encounter = encounterMapper.selectById(encounterId);
if (EncounterStatus.IN_PROGRESS.getValue().equals(encounter.getStatusEnum())) {
return R.fail("已接诊,请勿重复接诊");
}
return R.fail("接诊失败,请刷新后重试");
}
// 先把之前的接诊记录更新为已完成
iEncounterParticipantService.update(new LambdaUpdateWrapper<EncounterParticipant>()
.eq(EncounterParticipant::getTypeCode, ParticipantType.ADMITTER.getCode())
@@ -148,7 +180,28 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
encounterParticipant.setCreateBy(currentUsername);
encounterParticipant.setCreateTime(new Date());
iEncounterParticipantService.save(encounterParticipant);
return update > 0 ? R.ok() : R.fail();
// 更新 triage_queue_item 队列记录状态为 CALLING
try {
TriageQueueItem queueItem = triageQueueItemService.getOne(
new LambdaQueryWrapper<TriageQueueItem>()
.eq(TriageQueueItem::getTenantId, tenantId)
.eq(TriageQueueItem::getEncounterId, encounterId)
.eq(TriageQueueItem::getDeleteFlag, "0")
);
if (queueItem != null) {
queueItem.setStatus("CALLING");
queueItem.setUpdateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS));
triageQueueItemService.updateById(queueItem);
log.info("接诊时更新队列状态为CALLINGencounterId={}, queueItemId={}", encounterId, queueItem.getId());
} else {
log.warn("接诊时未找到队列记录encounterId={}", encounterId);
}
} catch (Exception e) {
log.error("接诊时更新队列状态失败encounterId={}", encounterId, e);
}
return R.ok();
}
/**
@@ -181,11 +234,54 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
return R.fail("就诊记录不存在");
}
if (!EncounterStatus.IN_PROGRESS.getValue().equals(encounter.getStatusEnum())) {
return R.fail("当前患者不在就诊中状态");
// 检查患者状态,防止重复完诊
Integer currentStatus = encounter.getStatusEnum();
if (EncounterStatus.DISCHARGED.getValue().equals(currentStatus) ||
EncounterStatus.COMPLETED.getValue().equals(currentStatus)) {
// 患者已完成就诊,返回特定提示
return R.fail("患者已完成就诊,已为您自动刷新患者列表");
}
// 2. 更新状态、完成时间以及初复诊标识
if (!EncounterStatus.IN_PROGRESS.getValue().equals(currentStatus)) {
return R.fail("非就诊中患者不能完诊");
}
// 2. 查找队列项
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
TriageQueueItem queueItem = triageQueueItemService.getOne(
new LambdaQueryWrapper<TriageQueueItem>()
.eq(TriageQueueItem::getTenantId, tenantId)
.eq(TriageQueueItem::getEncounterId, encounterId)
.eq(TriageQueueItem::getDeleteFlag, "0")
);
// 如果队列项存在,检查状态并更新
if (queueItem != null && "CALLING".equals(queueItem.getStatus())) {
// 更新队列状态为已完成
java.time.LocalDateTime nowLocal = java.time.LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
queueItem.setStatus("COMPLETED");
queueItem.setUpdateTime(nowLocal);
triageQueueItemService.updateById(queueItem);
// 写入 div_log 审计日志
try {
Long userId = SecurityUtils.getLoginUser().getUserId();
String divLogSql = "INSERT INTO hisdev.div_log "
+ "(pool_id, slot_id, queue_no, op_user_id, action, create_time) "
+ "VALUES (?, ?, ?, ?, 'COMPLETE', NOW()::timestamp(0))";
jdbcTemplate.update(divLogSql,
queueItem.getOrganizationId(), // pool_id: 候选池ID科室
queueItem.getPractitionerId(), // slot_id: 槽位ID医生
queueItem.getQueueOrder(), // queue_no: 队列号
userId); // op_user_id: 操作用户ID
} catch (Exception e) {
log.error("写入div_log审计日志失败", e);
// 审计日志失败不影响主流程
}
}
// 3. 更新状态、完成时间以及初复诊标识
Date now = new Date();
int update = encounterMapper.update(null,
new LambdaUpdateWrapper<Encounter>()
@@ -198,7 +294,7 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
if (update <= 0) return R.fail("完诊失败");
// 3. 审计日志
// 4. 审计日志sys_oper_log
try {
String username = SecurityUtils.getUsernameSafe();
String sql = "INSERT INTO sys_oper_log "

View File

@@ -55,6 +55,8 @@ import com.openhis.web.paymentmanage.dto.CancelPaymentDto;
import com.openhis.web.paymentmanage.dto.Clinic2207OrderResultDto;
import com.openhis.web.paymentmanage.mapper.PaymentMapper;
import com.openhis.web.personalization.dto.ActivityDeviceDto;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IDeviceRequestService;
@@ -81,9 +83,11 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
@@ -149,6 +153,8 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
@Resource
private OutpatientRegistrationAppMapper outpatientRegistrationAppMapper;
@Resource
private TriageQueueItemService triageQueueItemService;
@Resource
private IRegService iRegService;
@Resource
private IPatientService iPatientService;
@@ -1928,6 +1934,80 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
// 就诊ID
Long encounterId = iEncounterService.saveEncounterByRegister(encounter);
// 创建 triage_queue_item 队列记录
try {
Integer tenantId = encounter.getTenantId() != null ? encounter.getTenantId() : SecurityUtils.getLoginUser().getTenantId();
LocalDate queueDate = LocalDate.now();
// 查询当前科室当天的最大排队序号
Integer maxOrder = triageQueueItemService.list(
new LambdaQueryWrapper<TriageQueueItem>()
.eq(TriageQueueItem::getTenantId, tenantId)
.eq(TriageQueueItem::getOrganizationId, encounter.getOrganizationId())
.eq(TriageQueueItem::getQueueDate, queueDate)
.eq(TriageQueueItem::getDeleteFlag, "0")
.ne(TriageQueueItem::getStatus, "COMPLETED")
).stream()
.map(TriageQueueItem::getQueueOrder)
.filter(Objects::nonNull)
.max(Integer::compareTo)
.orElse(0);
// 获取患者信息
Patient patient = iPatientService.getById(encounter.getPatientId());
String patientName = patient != null ? patient.getName() : null;
// 获取挂号医生信息
String practitionerName = null;
Long queuePractitionerId = null;
if (encounterParticipantFormData.getPractitionerId() != null) {
Practitioner practitioner = iPractitionerService.getById(encounterParticipantFormData.getPractitionerId());
if (practitioner != null) {
practitionerName = practitioner.getName();
queuePractitionerId = practitioner.getId();
}
}
// 获取科室信息
Organization organization = iOrganizationService.getById(encounter.getOrganizationId());
String organizationName = organization != null ? organization.getName() : null;
// 获取服务项目信息(挂号类型)
String healthcareName = null;
if (encounter.getServiceTypeId() != null) {
HealthcareService healthcareService = healthcareServiceService.getById(encounter.getServiceTypeId());
if (healthcareService != null) {
healthcareName = healthcareService.getName();
}
}
// 创建队列项
TriageQueueItem queueItem = new TriageQueueItem()
.setTenantId(tenantId)
.setQueueDate(queueDate)
.setOrganizationId(encounter.getOrganizationId())
.setOrganizationName(organizationName)
.setEncounterId(encounterId)
.setPatientId(encounter.getPatientId())
.setPatientName(patientName)
.setHealthcareName(healthcareName)
.setPractitionerId(queuePractitionerId)
.setPractitionerName(practitionerName)
.setRoomNo(null)
.setStatus("WAITING")
.setQueueOrder(maxOrder + 1)
.setDeleteFlag("0")
.setCreateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS))
.setUpdateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS));
triageQueueItemService.save(queueItem);
logger.info("挂号时创建队列记录成功encounterId={}, queueItemId={}", encounterId, queueItem.getId());
} catch (Exception e) {
logger.error("挂号时创建队列记录失败encounterId={}", encounterId, e);
// 队列记录创建失败不影响挂号流程
}
// 保存就诊位置信息
// 挂号时不选Location了
// encounterLocationFormData.setEncounterId(encounterId);

View File

@@ -140,6 +140,12 @@ function handleReceive(row) {
// }
receiveEncounter(row.encounterId).then(() => {
emits('toCurrent', row);
}).catch(error => {
// 如果接诊失败,检查是否是"已接诊"的错误
if (error && error.message && error.message.includes('已接诊')) {
// 自动刷新列表,移除已接诊的患者
getPatientList();
}
});
}

View File

@@ -168,7 +168,7 @@
<div class="overlay" :class="{ 'overlay-disabled': disabled }" v-if="disabled"></div>
</div>
</div>
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen" @closed="handleDrawerClosed">
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
</el-drawer>
<RefundListDialog :open="openRefundListDialog" :encounterId="currentEncounterId"
@@ -562,6 +562,13 @@ function handleOpen() {
patientDrawerRef.value.refreshList();
}
function handleDrawerClosed() {
// 关闭患者队列抽屉时,刷新现诊患者列表和待诊患者数量
// 这样可以确保如果有其他医生接诊了患者,当前医生能看到最新状态
getPatientList();
getWaitPatient();
}
function handleCardClick(item, index) {
console.log('handleCardClick 被调用');
console.log('点击的患者项目:', item);
@@ -709,6 +716,12 @@ async function handleFinish(encounterId) {
firstEnum.value = 1; // 重置为初诊
getPatientList();
}
}).catch(error => {
// 如果完诊失败,检查是否是"已完成"的错误
if (error && error.message && (error.message.includes('已完成') || error.message.includes('完诊'))) {
// 自动刷新现诊患者列表
getPatientList();
}
});
}