From c5db4042908061f0b36192a27e2aeaa594ea5757 Mon Sep 17 00:00:00 2001 From: weixin_45799331 Date: Tue, 27 Jan 2026 13:28:44 +0800 Subject: [PATCH] =?UTF-8?q?88=20=E5=88=86=E8=AF=8A=E6=8E=92=E9=98=9F?= =?UTF-8?q?=E7=AE=A1=E7=90=86-=E3=80=8B=E7=A7=91=E5=AE=A4=E5=8F=AB?= =?UTF-8?q?=E5=8F=B7=E6=98=BE=E7=A4=BA=E5=B1=8F=20=E8=A1=A8triage=5Fqueue?= =?UTF-8?q?=5Fitem=E4=B8=AD=E6=B7=BB=E5=8A=A0=E4=BA=86=E8=81=94=E5=90=88?= =?UTF-8?q?=E7=B4=A2=E5=BC=95queue=5Fdate,organization=5Fid,tenant=5Fid,qu?= =?UTF-8?q?eue=5Forder=E3=80=82=E6=B7=BB=E5=8A=A0=E4=BA=86room=5Fno?= =?UTF-8?q?=EF=BC=8Cpractitioner=5Fid=E5=AD=97=E6=AE=B5=E3=80=82=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=9A=84=E5=AE=9E=E4=BD=93=E7=B1=BB=E5=88=9A=E5=88=9A?= =?UTF-8?q?=E6=B2=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/CallNumberDisplayResp.java | 67 +++++++++++++++ .../sse/CallNumberSseManager.java | 81 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/dto/CallNumberDisplayResp.java create mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/sse/CallNumberSseManager.java diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/dto/CallNumberDisplayResp.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/dto/CallNumberDisplayResp.java new file mode 100644 index 00000000..2fc0efef --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/dto/CallNumberDisplayResp.java @@ -0,0 +1,67 @@ +package com.openhis.web.triageandqueuemanage.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 叫号显示屏响应DTO + */ +@Data +public class CallNumberDisplayResp { + /** 科室名称 */ + private String departmentName; + + /** 当前叫号信息 */ + private CurrentCallInfo currentCall; + + /** 等候患者列表(按医生分组) */ + private List waitingList; + + /** 等待总人数 */ + private Integer waitingCount; + + /** + * 当前叫号信息 + */ + @Data + public static class CurrentCallInfo { + /** 排队号 */ + private Integer number; + /** 患者姓名(脱敏) */ + private String name; + /** 诊室号 */ + private String room; + /** 医生姓名 */ + private String doctor; + } + + /** + * 医生分组信息 + */ + @Data + public static class DoctorGroup { + /** 医生姓名 */ + private String doctorName; + /** 诊室号 */ + private String roomNo; + /** 该医生的患者列表 */ + private List patients; + } + + /** + * 患者信息 + */ + @Data + public static class PatientInfo { + /** 队列项ID */ + private Long id; + /** 患者姓名(脱敏) */ + private String name; + /** 状态:CALLING=就诊中,WAITING=等待 */ + private String status; + /** 排队号 */ + private Integer queueOrder; + } +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/sse/CallNumberSseManager.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/sse/CallNumberSseManager.java new file mode 100644 index 00000000..e29ead4e --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/triageandqueuemanage/sse/CallNumberSseManager.java @@ -0,0 +1,81 @@ +package com.openhis.web.triageandqueuemanage.sse; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * 叫号显示屏 SSE 管理器(服务端推送) + */ +@Slf4j +@Component +public class CallNumberSseManager { + + private static final long NO_TIMEOUT = 0L; + private static final Map> emitterMap = new ConcurrentHashMap<>(); + + /** + * 创建并注册一个 SSE 连接(按科室分组保存) + */ + public SseEmitter addEmitter(Long organizationId) { + SseEmitter emitter = new SseEmitter(NO_TIMEOUT); + emitterMap.computeIfAbsent(organizationId, k -> new CopyOnWriteArraySet<>()).add(emitter); + + emitter.onCompletion(() -> removeEmitter(organizationId, emitter)); + emitter.onTimeout(() -> removeEmitter(organizationId, emitter)); + emitter.onError((ex) -> removeEmitter(organizationId, emitter)); + + log.info("SSE连接建立:科室ID={}, 当前该科室连接数={}", + organizationId, emitterMap.get(organizationId).size()); + return emitter; + } + + /** + * 向指定科室的所有 SSE 连接推送消息 + */ + public void pushToOrganization(Long organizationId, Object message) { + CopyOnWriteArraySet emitters = emitterMap.get(organizationId); + if (emitters == null || emitters.isEmpty()) { + log.debug("科室{}没有SSE连接,跳过推送", organizationId); + return; + } + + for (SseEmitter emitter : emitters) { + if (!sendToEmitter(emitter, message)) { + removeEmitter(organizationId, emitter); + } + } + } + + /** + * 向单个 SSE 连接发送数据 + */ + public boolean sendToEmitter(SseEmitter emitter, Object data) { + try { + emitter.send(SseEmitter.event().data(data)); + return true; + } catch (IOException e) { + log.warn("SSE推送失败:{}", e.getMessage()); + return false; + } + } + + /** + * 断开或异常时移除 SSE 连接 + */ + private void removeEmitter(Long organizationId, SseEmitter emitter) { + CopyOnWriteArraySet emitters = emitterMap.get(organizationId); + if (emitters != null) { + emitters.remove(emitter); + if (emitters.isEmpty()) { + emitterMap.remove(organizationId); + } + } + } +} +