88 分诊排队管理-》科室叫号显示屏 表triage_queue_item中添加了联合索引queue_date,organization_id,tenant_id,queue_order。添加了room_no,practitioner_id字段。
This commit is contained in:
@@ -69,6 +69,13 @@
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- WebSocket 支持 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- rabbitMQ -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.dto.CallNumberDisplayResp;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
@@ -22,6 +23,9 @@ public interface TriageQueueAppService {
|
||||
R<?> skip(TriageQueueActionReq req);
|
||||
/** 下一患者:当前叫号中 -> 完成,下一位等待 -> 叫号中 */
|
||||
R<?> next(TriageQueueActionReq req);
|
||||
|
||||
/** 叫号显示屏:获取当前叫号和等候队列信息 */
|
||||
CallNumberDisplayResp getDisplayData(Long organizationId, LocalDate date, Integer tenantId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,19 +10,20 @@ import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.CallNumberDisplayResp;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueEncounterItem;
|
||||
import com.openhis.web.triageandqueuemanage.websocket.CallNumberWebSocket;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
@@ -121,6 +122,8 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
.setPatientName(it.getPatientName())
|
||||
.setHealthcareName(it.getHealthcareName())
|
||||
.setPractitionerName(it.getPractitionerName())
|
||||
.setPractitionerId(it.getPractitionerId()) // ✅ 新增字段(可选)
|
||||
.setRoomNo(it.getRoomNo()) // ✅ 新增字段(可选)
|
||||
.setStatus(STATUS_WAITING)
|
||||
.setQueueOrder(++maxOrder)
|
||||
.setDeleteFlag("0")
|
||||
@@ -238,6 +241,10 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
if (STATUS_WAITING.equals(selected.getStatus())) {
|
||||
selected.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(selected);
|
||||
|
||||
// ✅ 叫号后推送 WebSocket 消息
|
||||
pushDisplayUpdate(selected.getOrganizationId(), selected.getQueueDate(), selected.getTenantId());
|
||||
|
||||
return R.ok(true);
|
||||
} else if (STATUS_CALLING.equals(selected.getStatus())) {
|
||||
// 如果已经是"叫号中"状态,直接返回成功(不做任何操作)
|
||||
@@ -321,6 +328,10 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
}
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
|
||||
// ✅ 完成后推送 WebSocket 消息
|
||||
pushDisplayUpdate(actualOrgId, calling.getQueueDate(), tenantId);
|
||||
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@@ -413,6 +424,10 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
|
||||
// ✅ 过号重排后推送 WebSocket 消息
|
||||
pushDisplayUpdate(actualOrgId, calling.getQueueDate(), tenantId);
|
||||
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@@ -551,6 +566,179 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取叫号显示屏数据
|
||||
* @param organizationId 科室ID
|
||||
* @param date 日期
|
||||
* @param tenantId 租户ID
|
||||
* @return 显示屏数据
|
||||
*/
|
||||
@Override
|
||||
public CallNumberDisplayResp getDisplayData(Long organizationId, LocalDate date, Integer tenantId) {
|
||||
// 如果没有传入租户ID,尝试从登录用户获取,否则默认为1
|
||||
if (tenantId == null) {
|
||||
try {
|
||||
tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
} catch (Exception e) {
|
||||
tenantId = 1; // 默认租户ID
|
||||
}
|
||||
}
|
||||
LocalDate qd = date != null ? date : LocalDate.now();
|
||||
|
||||
/**
|
||||
* 查询所有队列项(WAITING 和 CALLING 状态)某天的某个科室的某个状态
|
||||
*
|
||||
*/
|
||||
List<TriageQueueItem> allItems = triageQueueItemService.list(
|
||||
new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getOrganizationId, organizationId)
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.in(TriageQueueItem::getStatus, STATUS_WAITING, STATUS_CALLING)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
);
|
||||
|
||||
CallNumberDisplayResp resp = new CallNumberDisplayResp();
|
||||
|
||||
// 1. 获取科室名称(从第一条数据中取)
|
||||
if (!allItems.isEmpty()) {
|
||||
resp.setDepartmentName(allItems.get(0).getOrganizationName() + " 叫号显示屏");
|
||||
} else {
|
||||
resp.setDepartmentName("叫号显示屏");
|
||||
}
|
||||
|
||||
// 2. 查找当前叫号中的患者(CALLING 状态)
|
||||
TriageQueueItem callingItem = allItems.stream()
|
||||
.filter(item -> STATUS_CALLING.equals(item.getStatus()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (callingItem != null) {
|
||||
CallNumberDisplayResp.CurrentCallInfo currentCall = new CallNumberDisplayResp.CurrentCallInfo();
|
||||
currentCall.setNumber(callingItem.getQueueOrder());
|
||||
currentCall.setName(maskPatientName(callingItem.getPatientName()));
|
||||
currentCall.setRoom(callingItem.getRoomNo() != null ? callingItem.getRoomNo() : "1号");
|
||||
currentCall.setDoctor(callingItem.getPractitionerName());
|
||||
resp.setCurrentCall(currentCall);
|
||||
} else {
|
||||
// 没有叫号中的患者,返回默认值
|
||||
CallNumberDisplayResp.CurrentCallInfo currentCall = new CallNumberDisplayResp.CurrentCallInfo();
|
||||
currentCall.setNumber(null);
|
||||
currentCall.setName("-");
|
||||
currentCall.setRoom("-");
|
||||
currentCall.setDoctor("-");
|
||||
resp.setCurrentCall(currentCall);
|
||||
}
|
||||
|
||||
// 3. 按医生分组(包括 CALLING 和 WAITING 状态)
|
||||
Map<Long, List<TriageQueueItem>> groupedByDoctor = allItems.stream()
|
||||
// 严格按医生分组:仅保留有 practitionerId 的记录
|
||||
.filter(item -> item.getPractitionerId() != null)
|
||||
.collect(Collectors.groupingBy(TriageQueueItem::getPractitionerId));
|
||||
|
||||
// 每个医生的等待队列
|
||||
List<CallNumberDisplayResp.DoctorGroup> waitingList = new ArrayList<>();
|
||||
int totalWaiting = 0;
|
||||
|
||||
for (Map.Entry<Long, List<TriageQueueItem>> entry : groupedByDoctor.entrySet()) {
|
||||
|
||||
List<TriageQueueItem> doctorItems = entry.getValue();
|
||||
String doctorName = doctorItems.get(0).getPractitionerName();
|
||||
if (doctorName == null || doctorName.isEmpty()) {
|
||||
doctorName = "未分配";
|
||||
}
|
||||
// 按排队顺序排序
|
||||
doctorItems.sort(Comparator.comparing(TriageQueueItem::getQueueOrder));
|
||||
|
||||
// 该医生 下边的患者列表 和 诊室号
|
||||
CallNumberDisplayResp.DoctorGroup doctorGroup = new CallNumberDisplayResp.DoctorGroup();
|
||||
doctorGroup.setDoctorName(doctorName);
|
||||
|
||||
// 获取诊室号(从该医生的任一患者中取)
|
||||
String roomNo = doctorItems.stream()
|
||||
.map(TriageQueueItem::getRoomNo)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse("1号");
|
||||
doctorGroup.setRoomNo(roomNo);
|
||||
|
||||
// 转换患者列表
|
||||
List<CallNumberDisplayResp.PatientInfo> patients = new ArrayList<>();
|
||||
for (TriageQueueItem item : doctorItems) {
|
||||
CallNumberDisplayResp.PatientInfo patient = new CallNumberDisplayResp.PatientInfo();
|
||||
patient.setId(item.getId());
|
||||
patient.setName(maskPatientName(item.getPatientName()));
|
||||
patient.setStatus(item.getStatus());
|
||||
patient.setQueueOrder(item.getQueueOrder());
|
||||
patients.add(patient);
|
||||
|
||||
// 统计等待人数(不包括 CALLING 状态)
|
||||
if (STATUS_WAITING.equals(item.getStatus())) {
|
||||
totalWaiting++;
|
||||
}
|
||||
}
|
||||
|
||||
doctorGroup.setPatients(patients);
|
||||
waitingList.add(doctorGroup);
|
||||
}
|
||||
|
||||
|
||||
// 按医生名称排序
|
||||
waitingList.sort(Comparator.comparing(CallNumberDisplayResp.DoctorGroup::getDoctorName));
|
||||
|
||||
resp.setWaitingList(waitingList);
|
||||
resp.setWaitingCount(totalWaiting);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 患者姓名脱敏处理
|
||||
* @param name 原始姓名
|
||||
* @return 脱敏后的姓名(如:张*三)
|
||||
*/
|
||||
private String maskPatientName(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return "-";
|
||||
}
|
||||
if (name.length() == 1) {
|
||||
return name;
|
||||
}
|
||||
if (name.length() == 2) {
|
||||
return name.charAt(0) + "*";
|
||||
}
|
||||
// 3个字及以上:保留首尾,中间用*代替
|
||||
return name.charAt(0) + "*" + name.charAt(name.length() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送显示屏更新消息到 WebSocket
|
||||
* @param organizationId 科室ID
|
||||
* @param queueDate 队列日期
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
private void pushDisplayUpdate(Long organizationId, LocalDate queueDate, Integer tenantId) {
|
||||
try {
|
||||
// 获取最新的显示屏数据
|
||||
CallNumberDisplayResp displayData = getDisplayData(organizationId, queueDate, tenantId);
|
||||
|
||||
// 构造推送消息
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("type", "update");
|
||||
message.put("action", "queue_changed");
|
||||
message.put("data", displayData);
|
||||
message.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
// 推送到该科室的所有 WebSocket 连接
|
||||
CallNumberWebSocket.pushToOrganization(organizationId, message);
|
||||
|
||||
} catch (Exception e) {
|
||||
// WebSocket 推送失败不应该影响业务逻辑
|
||||
System.err.println("推送显示屏更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package com.openhis.web.triageandqueuemanage.controller;
|
||||
|
||||
import com.core.common.annotation.Anonymous;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.CallNumberDisplayResp;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -65,6 +69,62 @@ public class TriageQueueController {
|
||||
public R<?> next(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.next(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 叫号显示屏:获取当前叫号和等候队列信息
|
||||
* @param organizationId 科室ID
|
||||
* @param date 日期(可选,默认今天)
|
||||
* @param tenantId 租户ID(可选,默认1)
|
||||
* @return 显示屏数据
|
||||
*/
|
||||
@Anonymous // 显示屏不需要登录
|
||||
@GetMapping("/display")
|
||||
public R<CallNumberDisplayResp> getDisplayData(
|
||||
@RequestParam(required = false) String organizationId,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date,
|
||||
@RequestParam(required = false) Integer tenantId
|
||||
) {
|
||||
try {
|
||||
Long orgId = resolveOrganizationId(organizationId);
|
||||
if (orgId == null) {
|
||||
return R.fail("organizationId参数不合法或未获取到登录用户科室");
|
||||
}
|
||||
Integer actualTenantId = resolveTenantId(tenantId);
|
||||
CallNumberDisplayResp data = triageQueueAppService.getDisplayData(orgId, date, actualTenantId);
|
||||
return R.ok(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取显示屏数据失败", e);
|
||||
return R.fail("获取显示屏数据失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Long resolveOrganizationId(String organizationId) {
|
||||
if (!StringUtils.hasText(organizationId)) {
|
||||
try {
|
||||
return SecurityUtils.getLoginUser().getOrgId();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(organizationId.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("非法organizationId: {}", organizationId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Integer resolveTenantId(Integer tenantId) {
|
||||
if (tenantId != null) {
|
||||
return tenantId;
|
||||
}
|
||||
try {
|
||||
Integer loginTenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
return loginTenantId != null ? loginTenantId : 1;
|
||||
} catch (Exception e) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@ public class TriageQueueEncounterItem {
|
||||
private String patientName;
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
|
||||
// ========== 新增字段(可选,用于叫号显示屏)==========
|
||||
/** 医生ID(可选) */
|
||||
private Long practitionerId;
|
||||
/** 诊室号(可选) */
|
||||
private String roomNo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user