消息系统,输血申请消息,发血消息

This commit is contained in:
jiangs 2026-08-31 17:09:54 +08:00
parent afe13d5bc3
commit e3c6eae37e
12 changed files with 202 additions and 58 deletions

View File

@ -9,4 +9,5 @@ public interface XkTransfuseApplyBloodbreedMapper {
List<XkTransfuseApplyBloodbreed> queryOne(String bill_no);
void insert(XkTransfuseApplyBloodbreed xkTransfuseApplyBloodbreed);
void update(XkTransfuseApplyBloodbreed xkTransfuseApplyBloodbreed);
void deleteByBillNo(String bill_no);
}

View File

@ -1,5 +1,6 @@
package com.czblood.bus.pojo.websocket;
import com.czblood.common.core.domain.entity.SysUser;
import com.czblood.common.core.domain.model.LoginUser;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -13,10 +14,10 @@ import java.util.concurrent.ConcurrentHashMap;
public class WebSocketParam {
private Set<Session> sessionSet;
private final String userId;
private LoginUser loginUser;
private SysUser loginUser;
// 方便快速构建对象
public static WebSocketParam create(String userId, LoginUser loginUser){
public static WebSocketParam create(String userId, SysUser loginUser){
// ConcurrentHashMap.newKeySet()线程安全set
Set<Session> set = ConcurrentHashMap.newKeySet();
return new WebSocketParam(set, userId, loginUser);

View File

@ -8,7 +8,7 @@ public enum MsgType {
//取血通知消息
BLOOD_OUT_NOTICE("BLOOD_OUT_NOTICE"),
//申请单消息
//输血申请单保存消息
APPLY_NOTICE("APPLY_NOTICE"),
/** 单发消息 */

View File

@ -0,0 +1,68 @@
package com.czblood.bus.websocket;
import com.czblood.bus.mapper.XkMsgDetailMapper;
import com.czblood.bus.mapper.XkMsgMainMapper;
import com.czblood.bus.mapper.XkTransfuseApplyMapper;
import com.czblood.bus.pojo.XkMsgDetail;
import com.czblood.bus.pojo.XkMsgMain;
import com.czblood.bus.pojo.XkTransfuseApply;
import com.czblood.bus.utils.BloodBankUtil;
import com.czblood.common.utils.spring.SpringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
@Component
public class SendMessageUtil {
@Resource
XkTransfuseApplyMapper xkTransfuseApplyMapper;
@Resource
BloodBankUtil bloodBankUtil;
@Resource
XkMsgMainMapper xkMsgMainMapper;
@Resource
XkMsgDetailMapper xkMsgDetailMapper;
public void saveMessage(String billNo,String content) {
XkTransfuseApply xkTransfuseApply = xkTransfuseApplyMapper.queryOne(billNo);
if (xkTransfuseApply == null) return ;
Date currentTimeDate = bloodBankUtil.getCurrentTimeDate();
//保存消息数据,写一条消息记录
//写消息数据主表
XkMsgMain xkMsgMainTemp = new XkMsgMain();
xkMsgMainTemp.setAccept_Dept("输血科");
xkMsgMainTemp.setSend_Dept(xkTransfuseApply.getDept_id());
xkMsgMainTemp.setSend_Person(xkTransfuseApply.getApply_medic());
xkMsgMainTemp.setSend_Date(xkTransfuseApply.getApply_date());
xkMsgMainTemp.setAccept_Person(""); //接收人
//xkMsgMainTemp.setAccept_Date(currentTimeDate); //接收时间,先默认一个,后面确认之后在更新
xkMsgMainTemp.setRead_Flag("N"); //是否已读
xkMsgMainTemp.setSend_Flag("Y"); //是否已发送
xkMsgMainTemp.setAccept_Flag("N"); //是否已接收
xkMsgMainTemp.setMsg_Title("输血申请单保存");
xkMsgMainTemp.setMsg_Type(MsgType.APPLY_NOTICE.getCode()); //发血消息
xkMsgMainTemp.setWrite_Date(currentTimeDate);
//写消息数据明细表
XkMsgDetail xkMsgDetailTemp = new XkMsgDetail();
xkMsgDetailTemp.setMsg_Body(content);
xkMsgDetailTemp.setAccept_Dept("输血科");
xkMsgDetailTemp.setSend_Dept(xkTransfuseApply.getDept_id());
SpringUtils.getBean(SendMessageUtil.class).saveData(xkMsgMainTemp,xkMsgDetailTemp);
}
@Transactional(rollbackFor = Exception.class)
public void saveData(XkMsgMain xkMsgMain, XkMsgDetail xkMsgDetail) {
xkMsgMainMapper.insert(xkMsgMain);
xkMsgDetailMapper.insert(xkMsgDetail);
}
}

View File

@ -1,5 +1,6 @@
package com.czblood.bus.websocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@ -8,10 +9,32 @@ import org.springframework.web.socket.server.standard.ServerEndpointExporter;
* WebSocket配置类(Spring Boot 原生方式)
*/
@Configuration
@Slf4j
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
return new ServerEndpointExporter() {
@Override
public void afterPropertiesSet() {
try {
super.afterPropertiesSet();
} catch (IllegalStateException e) {
// 测试环境或无WebSocket容器时ServerContainer不可用,跳过注册
log.error("WebSocket ServerContainer不可用,跳过端点注册(测试环境可忽略此警告)");
}
}
@Override
public void afterSingletonsInstantiated() {
try {
super.afterSingletonsInstantiated();
} catch (IllegalStateException e) {
// 测试环境或无WebSocket容器时ServerContainer不可用,跳过注册
log.warn("WebSocket ServerContainer不可用,跳过端点注册(测试环境可忽略)");
} catch (Exception e) {
log.warn("WebSocket afterSingletonsInstantiated 跳过(测试环境可忽略): {}", e.getMessage());
}
}
};
}
}

View File

@ -1,15 +1,22 @@
package com.czblood.bus.websocket;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.czblood.bus.pojo.XkMsgDetail;
import com.czblood.bus.pojo.XkMsgMain;
import com.czblood.bus.pojo.websocket.WebSocketParam;
import com.czblood.common.core.domain.model.LoginUser;
import com.czblood.common.utils.SecurityUtils;
import com.czblood.bus.utils.BloodBankUtil;
import com.czblood.common.core.domain.entity.SysDept;
import com.czblood.common.core.domain.entity.SysUser;
import com.czblood.common.utils.spring.SpringUtils;
import com.czblood.system.mapper.SysDeptMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -32,10 +39,22 @@ public class WebSocketServer {
public void onOpen(Session session, @PathParam("userId") String userId) {
log.info("客户端:{} 建立连接", userId);
session.setMaxTextMessageBufferSize(8192000);
LoginUser loginUser = SecurityUtils.getLoginUser();
WebSocketParam param = onlineUsers.computeIfAbsent(userId, k -> WebSocketParam.create(k, loginUser));
boolean addOk = param.getSessionSet().add(session);
log.info("客户端:{} 加入sessionSet:{}", userId, addOk);
SysUser loginUser = null;
try{
loginUser = SpringUtils.getBean(BloodBankUtil.class).getUserInfo(userId);
}catch (Exception e){
log.error("获取登录用户信息失败 userId={}", userId, e);
}finally {
if(loginUser != null){
SysUser finalLoginUser = loginUser;
WebSocketParam param = onlineUsers.computeIfAbsent(userId, k -> WebSocketParam.create(k, finalLoginUser));
boolean addOk = param.getSessionSet().add(session);
log.info("客户端:{} 加入sessionSet:{}", userId, addOk);
}
}
}
/**
@ -62,7 +81,7 @@ public class WebSocketServer {
case BLOOD_OUT_NOTICE:
case APPLY_NOTICE:
//给指定的一批人发消息
//sendToSomeOne(message,userId);
sendToSomeOne(msg);
break;
case NOTICE:
// 群发 / 系统通知:发给所有在线用户
@ -97,11 +116,11 @@ public class WebSocketServer {
log.info("用户[{}]ws连接关闭", userId);
}
@OnError
public void onError(Session session, @PathParam("userId") String userId, Throwable throwable) {
onClose(session, userId);
log.error("ws错误 userId={}", userId, throwable);
}
// @OnError
// public void onError(Session session, @PathParam("userId") String userId, Throwable throwable) {
// //onClose(session, userId);
// log.error("ws错误 userId={}", userId, throwable);
// }
/**
* 群发:给所有在线用户发消息
@ -140,32 +159,56 @@ public class WebSocketServer {
}
}
// /**
// * 给指定的人发消息
// * @param msg
// */
// public static void sendToSomeOne(WsMessage msg){
// MsgType type = msg.getType();
// String content = msg.getContent();
// String userId = msg.getTo();
// WebSocketParam param = onlineUsers.get(userId);
// if (param == null || param.getSessionSet().isEmpty()) {
// return;
// }
// for (Session wsSession : param.getSessionSet()) {
// if (wsSession.isOpen()) {
// try {
// wsSession.getBasicRemote().sendText(content);
// } catch (Exception e) {
// log.error("单发消息失败 userId={}: {}", userId, e.getMessage());
// }
// }
// }
//
// switch (type) {
// //取血通知,传入的人,是取血人 ,但是我们需要通知这个取血人所对应的整个科室,比较合理
// case BLOOD_OUT_NOTICE:
//
// }
// }
/**
* 给指定的人发消息
* @param msg
*/
public static void sendToSomeOne(WsMessage msg){
MsgType type = msg.getType();
String content = msg.getContent();
String userId = msg.getTo();
WebSocketParam param = onlineUsers.get(userId);
String billNo = "";
try{
JSONObject data = (JSONObject)msg.getData();
billNo = data.getStr("billNo");
}catch (Exception e){
log.error("消息数据解析异常:", e);
return;
}
if (param == null || param.getSessionSet().isEmpty()) {
return;
}
for (Session wsSession : param.getSessionSet()) {
if (wsSession.isOpen()) {
try {
wsSession.getBasicRemote().sendText(content);
} catch (Exception e) {
log.error("单发消息失败 userId={}: {}", userId, e.getMessage());
}
}
}
switch (type) {
//输血申请单保存通知
case APPLY_NOTICE:
//需要通知整个输血科人员
SysDept sysDept = new SysDept();
sysDept.setDeptType("002"); //输血科室
List<SysDept> sysDeptList = SpringUtils.getBean(SysDeptMapper.class).selectDeptList(sysDept);
for (SysDept dept : sysDeptList) {
Long deptId = dept.getDeptId();
SysUser sysUserQuery = new SysUser();
sysUserQuery.setDeptId(deptId);
List<SysUser> sysUserList = SpringUtils.getBean(BloodBankUtil.class).queryUserList(sysUserQuery);
for (SysUser sysUser : sysUserList) {
sendMsg(sysUser.getUserName(), content);
}
}
break;
}
//保存消息数据,写一条消息记录
SpringUtils.getBean(SendMessageUtil.class).saveMessage(billNo,content);
}
}

View File

@ -15,9 +15,11 @@ import java.io.Serializable;
* "to": "1002", // 接收者 userId;群发时可为 null 或 "ALL"
* "title": "你好", // 可选,消息标题
* "content": "在吗?", // 业务正文(字符串)
* "data": {...}, // 业务扩展字段(对象),不同 type 结构不同
* "timestamp": 1729999999999,// 时间戳
* "extra": {...} // 预留扩展
* "data": {
* "billNo": ""
* }, // 业务扩展字段(对象),不同 type 结构不同
* "timestamp": "2023-04-01 13:59:59",// 时间格式:yyyy-MM-dd HH:mm:ss,例如:2023-04-01 13:59:59
* "extra": {} // 预留扩展
* }
*/
@Data

View File

@ -48,6 +48,9 @@
where bill_no = #{bill_no}
<if test="blood_breed != null and blood_breed != ''">and blood_breed = #{blood_breed}</if>
</update>
<delete id="deleteByBillNo">
delete from xk_transfuse_apply_bloodbreed where bill_no = #{billNo}
</delete>
<select id="queryList" resultType="com.czblood.bus.pojo.XkTransfuseApplyBloodbreed">
select * from xk_transfuse_apply_bloodbreed where bill_no = #{bill_no}
<if test="blood_breed != null and blood_breed != ''">and blood_breed = #{blood_breed}</if>

View File

@ -478,9 +478,9 @@ public class BloodOutServiceImpl implements BloodOutService {
xkMsgMainTemp.setSend_Person(xkOutMain.getCheck_person());
xkMsgMainTemp.setSend_Date(xkOutMain.getCheck_date());
xkMsgMainTemp.setAccept_Person(xkOutMain.getTake_person()); //接收人
xkMsgMainTemp.setAccept_Date(currentTimeDate); //接收时间,先默认一个,后面确认之后在更新
//xkMsgMainTemp.setAccept_Date(currentTimeDate); //接收时间,先默认一个,后面确认之后在更新
xkMsgMainTemp.setRead_Flag("N"); //是否已读
xkMsgMainTemp.setSend_Flag("N"); //是否已发送
xkMsgMainTemp.setSend_Flag("Y"); //是否已发送
xkMsgMainTemp.setAccept_Flag("N"); //是否已接收
xkMsgMainTemp.setMsg_Title("取血通知");
xkMsgMainTemp.setMsg_Type(MsgType.BLOOD_OUT_NOTICE.getCode()); //发血消息

View File

@ -291,21 +291,19 @@ public class TransfuseApplyUtil {
//保存病人信息表
xkPatientInfoMapper.deleteXkPatientInfo(xkPatientInfo.getBeinhos_id());
xkPatientInfoMapper.insertXkPatientInfo(xkPatientInfo);
if(!isNew){
//保存申请单信息表
xkTransfuseApplyMapper.update(xkTransfuseApply);
//保存申请单品种表
for (XkTransfuseApplyBloodbreed xkTransfuseApplyBloodbreed : xkTransfuseApplyBloodbreedList) {
xkTransfuseApplyBloodbreedMapper.update(xkTransfuseApplyBloodbreed);
}
}else{
//保存申请单信息表
xkTransfuseApplyMapper.insert(xkTransfuseApply);
//保存申请单品种表
for (XkTransfuseApplyBloodbreed xkTransfuseApplyBloodbreed : xkTransfuseApplyBloodbreedList) {
xkTransfuseApplyBloodbreedMapper.insert(xkTransfuseApplyBloodbreed);
}
}
//保存申请单品种表
xkTransfuseApplyBloodbreedMapper.deleteByBillNo(xkTransfuseApply.getBill_no());
for (XkTransfuseApplyBloodbreed xkTransfuseApplyBloodbreed : xkTransfuseApplyBloodbreedList) {
xkTransfuseApplyBloodbreedMapper.insert(xkTransfuseApplyBloodbreed);
}
//保存输血指征表

View File

@ -1,6 +1,7 @@
package com.czblood.common.core.domain.entity;
import com.czblood.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -58,6 +59,7 @@ public class SysDept extends BaseEntity
private String deptCodeOld;
private String hos_id;
@ApiModelProperty("001 临床科室,002 输血科室")
private String deptType;
/** 子部门 */

View File

@ -49,6 +49,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="deptCode != null and deptCode != ''">
AND dept_code = #{deptCode}
</if>
<if test="deptType != null and deptType != ''">
AND dept_type = #{deptType}
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
order by d.parent_id, d.order_num