diff --git a/pom.xml b/pom.xml index 6dfd0b5..3fa69bd 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,21 @@ jackson-databind 2.13.3 --> + + jakarta.websocket + jakarta.websocket-api + 2.0.0 + provided + + + cn.hutool + hutool-json + 5.8.0 + + + org.springframework.boot + spring-boot-starter-websocket + org.projectlombok lombok diff --git a/src/main/java/edu/zrh/healthsystem/config/WebConfig.java b/src/main/java/edu/zrh/healthsystem/config/WebConfig.java index e9a0eab..263e7f4 100644 --- a/src/main/java/edu/zrh/healthsystem/config/WebConfig.java +++ b/src/main/java/edu/zrh/healthsystem/config/WebConfig.java @@ -3,16 +3,35 @@ package edu.zrh.healthsystem.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author han */ @Configuration public class WebConfig { - + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("http://localhost:3000"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + + return new CorsFilter(source); + } } diff --git a/src/main/java/edu/zrh/healthsystem/controller/TalkController.java b/src/main/java/edu/zrh/healthsystem/controller/TalkController.java index 92cae19..22e267a 100644 --- a/src/main/java/edu/zrh/healthsystem/controller/TalkController.java +++ b/src/main/java/edu/zrh/healthsystem/controller/TalkController.java @@ -1,11 +1,13 @@ package edu.zrh.healthsystem.controller; +import edu.zrh.healthsystem.entity.TalkInfo; import edu.zrh.healthsystem.model.Talk; import edu.zrh.healthsystem.model.TalkResponse; import edu.zrh.healthsystem.service.TalkService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.util.List; import java.util.Map; /** @@ -36,4 +38,8 @@ public class TalkController { return Map.of("status", "fail", "message", "登录失败"); } } + @GetMapping("/talk/record/all") + public List getHistory() { + return talkService.getAllRecord(); + } } diff --git a/src/main/java/edu/zrh/healthsystem/service/TalkService.java b/src/main/java/edu/zrh/healthsystem/service/TalkService.java index 07a27a3..fd751c9 100644 --- a/src/main/java/edu/zrh/healthsystem/service/TalkService.java +++ b/src/main/java/edu/zrh/healthsystem/service/TalkService.java @@ -9,6 +9,7 @@ import org.springframework.web.client.RestTemplate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Map; /** @@ -38,4 +39,8 @@ public class TalkService { talkInfoRepository.save(talkInfo); return response; } + + public List getAllRecord() { + return talkInfoRepository.findAll(); + } } diff --git a/src/main/java/edu/zrh/healthsystem/service/WebSocketServer.java b/src/main/java/edu/zrh/healthsystem/service/WebSocketServer.java new file mode 100644 index 0000000..a1c33dd --- /dev/null +++ b/src/main/java/edu/zrh/healthsystem/service/WebSocketServer.java @@ -0,0 +1,110 @@ +package edu.zrh.healthsystem.service; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author websocket服务 + */ +@ServerEndpoint(value = "/imserver/{username}") +@Component +public class WebSocketServer { + private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class); + /** + * 记录当前在线连接数 + */ + public static final Map sessionMap = new ConcurrentHashMap<>(); + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session, @PathParam("username") String username) { + sessionMap.put(username, session); + log.info("有新用户加入,username={}, 当前在线人数为:{}", username, sessionMap.size()); + JSONObject result = new JSONObject(); + JSONArray array = new JSONArray(); + result.set("users", array); + for (Object key : sessionMap.keySet()) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("username", key); + // {"username", "zhang", "username": "admin"} + array.add(jsonObject); + } +// {"users": [{"username": "zhang"},{ "username": "admin"}]} + sendAllMessage(JSONUtil.toJsonStr(result)); // 后台发送消息给所有的客户端 + } + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose(Session session, @PathParam("username") String username) { + sessionMap.remove(username); + log.info("有一连接关闭,移除username={}的用户session, 当前在线人数为:{}", username, sessionMap.size()); + } + /** + * 收到客户端消息后调用的方法 + * 后台收到客户端发送过来的消息 + * onMessage 是一个消息的中转站 + * 接受 浏览器端 socket.send 发送过来的 json数据 + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, Session session, @PathParam("username") String username) { + log.info("服务端收到用户username={}的消息:{}", username, message); + JSONObject obj = JSONUtil.parseObj(message); + String toUsername = obj.getStr("to"); // to表示发送给哪个用户,比如 admin + String text = obj.getStr("text"); // 发送的消息文本 hello + // {"to": "admin", "text": "聊天文本"} + Session toSession = sessionMap.get(toUsername); // 根据 to用户名来获取 session,再通过session发送消息文本 + if (toSession != null) { + // 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容 + // {"from": "zhang", "text": "hello"} + JSONObject jsonObject = new JSONObject(); + jsonObject.set("from", username); // from 是 zhang + jsonObject.set("text", text); // text 同上面的text + this.sendMessage(jsonObject.toString(), toSession); + log.info("发送给用户username={},消息:{}", toUsername, jsonObject.toString()); + } else { + log.info("发送失败,未找到用户username={}的session", toUsername); + } + } + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误"); + error.printStackTrace(); + } + /** + * 服务端发送消息给客户端 + */ + private void sendMessage(String message, Session toSession) { + try { + log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message); + toSession.getBasicRemote().sendText(message); + } catch (Exception e) { + log.error("服务端发送消息给客户端失败", e); + } + } + /** + * 服务端发送消息给所有客户端 + */ + private void sendAllMessage(String message) { + try { + for (Session session : sessionMap.values()) { + log.info("服务端给客户端[{}]发送消息{}", session.getId(), message); + session.getBasicRemote().sendText(message); + } + } catch (Exception e) { + log.error("服务端发送消息给客户端失败", e); + } + } +}