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); + } + } + } +} +