1. Introduction

Based on Spring Boot and Websocket to achieve point-to-point online chat and simple robot automatic reply function, students can choose online idle teacher consultation, no online teacher access robot according to the content and question number of students to get the corresponding answer from the question library to reply to students.

2. The background

2.1. Introduce dependencies

    // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '2.0.4.RELEASE'
Copy the code

2.2. Websocket configuration class

WebsocketConfig.java

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(a) {
        return newServerEndpointExporter(); }}Copy the code

2.3. Encapsulate display message objects

@Data
public class MessageVo {
    @apiModelProperty (value = "user id")
    private Integer userId;

    @apiModelProperty (value = "username ")
    private String username;

    @apiModelProperty (value = "message content ")
    private String msg;

    @apiModelProperty (value = "number of people online ")
    private int count;
}
Copy the code

2.4. Encapsulate the object of the conversation

The user information used to store the session

@Data
@apiModel (description = "webSocket session object ")
public class SocketUserInfo extends BaseEntity {


    / / user sessionId
    private String sessionId;

    / / the user session
    private Session session;

    // Target user sessionID
    private String targetSessionId;

    // User role
    private String userRole;

    / / user id
    private Integer userId;
    / / user name
    private String userName;
    // Contact number
    private String tel;
Copy the code

2.5. Websocket handling classes

@Slf4j
@Component
@ServerEndpoint(value = "/groupChat/{targetSessionId}/{userId}",configurator = SpringContextUtil.class)
public class WebSocketServerController{

    // Use a local thread to save sessions
    private static ThreadLocal<Session> sessions = new ThreadLocal<Session>();
    // Save session sessionids of all connected users as keys
    private static Map<String, SocketUserInfo> userSessionMap = new ConcurrentHashMap<>();
    // Save the teacher's session
    private static Map<String, SocketUserInfo> serverSessionMap = new ConcurrentHashMap<>();

    // Save students online
    private static List<Map<String,Object>> studentUserMapList = new ArrayList<>();

    // Save the teacher information online
    private static List<Map<String,Object>> teacherUserMapList = new ArrayList<>();

    @Autowired
    private CacheProxy cacheProxy;

    @Autowired
    private BuiProblemAnswerServiceImpl buiProblemAnswerService;

    /** * set up the connection call method, group members join **@param session
     * @paramTargetSessionId Session ID of the object consulted by the student */
    @OnOpen
    public void onOpen(Session session,
                       @PathParam("targetSessionId") String targetSessionId,
                       @PathParam("userId") Integer userId) {
        try {
            MessageVo messageVo = new MessageVo();
            // Ensure that variables in each thread are relatively independent of variables in other threads
            sessions.set(session);
            // Get user information
            SysLoginUserServiceImpl sysLoginUserService = SpringContextUtil.getBean(SysLoginUserServiceImpl.class);
            Map<String, Object> map = sysLoginUserService.queryUserInfoById(userId);
            // The teacher is online
            if ("10000".equals(targetSessionId)){
                // If you are the teacher and administrator, create an online chat message
                SocketUserInfo serverInfo = new SocketUserInfo();
                serverInfo.setUserRole("Teacher");
                serverInfo.setSession(session);
                serverInfo.setUserId(userId);

                serverInfo.setSessionId(session.getId());
                serverInfo.setUserName(StrUtil.clearNull(map.get("userName")));
                serverInfo.setTerminalCode(StrUtil.clearNull(map.get("tel")));

                map.put("sessionId",session.getId());

                // Add teacher information to the list
                teacherUserMapList.add(map);
                // Write the online teacher information to the cache
                cacheProxy.set("websocket",teacherUserMapList);

                messageVo.setMsg(session.getId()+"You are online.");
                messageVo.setUserId(userId);
                sendMsg(session,messageVo);

                // Save the teacher's information to map
                serverSessionMap.put(session.getId(),serverInfo);
            }

            if (!"10000".equals(targetSessionId)){// Create an online student profile
                SocketUserInfo userInfo = new SocketUserInfo();
                userInfo.setSessionId(session.getId());
                userInfo.setSession(session);
                userInfo.setUserRole("Students");
                userInfo.setUserId(userId);
                userInfo.setUserName(StrUtil.clearNull(map.get("userName")));
                userInfo.setTerminalCode(StrUtil.clearNull(map.get("tel")));

                // Get the corresponding teacher information
                if(! targetSessionId.equals("kefu")){
                    SocketUserInfo serverInfo = serverSessionMap.get(targetSessionId);
                    // Set the teacher's connection to the current student
                    serverInfo.setTargetSessionId(session.getId());
                    // Set the current student's connection object to the teacher
                    userInfo.setTargetSessionId(targetSessionId);

                    // Remove the currently consulted teacher from the free list
                    for (int i = 0; i < teacherUserMapList.size(); i++) {
                        Map<String, Object> teacherMap = teacherUserMapList.get(i);
                        if (teacherMap.get("sessionId").equals(targetSessionId)){ teacherUserMapList.remove(i); }}// Update the list of teachers available for consultation
                    cacheProxy.set("websocket",teacherUserMapList);

                    // Send a message to students
                    messageVo.setMsg("Teacher"+serverInfo.getUserName()+"Answering your questions!");
                    messageVo.setCount(2);
                    messageVo.setUserId(serverInfo.getUserId());
                    sendMsg(userInfo.getSession(), messageVo);

                    // Send a message to the teacher
                    messageVo.setMsg("For the students"+userInfo.getSessionId()+"Answer!");
                    messageVo.setUserId(userInfo.getUserId());
                    sendMsg(serverInfo.getSession(), messageVo);
                }
                if ("kefu".equals(targetSessionId)){
                    userInfo.setTargetSessionId("kefu");
                    messageVo.setMsg("Please enter the problem number according to the list on the right!!");
                    sendMsg(session,messageVo);
                }
                
                // Save online user information to mapuserSessionMap.put(session.getId(), userInfo); }}catch(Exception e){ e.printStackTrace(); }}/** * Group member sends message ** after receiving the method called by the message@param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session){
        HashMap<String, String> result = new HashMap<>();
        MessageVo messageVo = new MessageVo();
        SocketUserInfo serverInfo = serverSessionMap.get(session.getId());
        if(serverInfo ! =null) {// Teacher message
            log.info("Teacher"+ session.getId()+"Send a message: \""+ message +"\" For students"+serverSessionMap.get(session.getId()).getTargetSessionId());
            result.put("msg"."Teacher"+session.getId()+":"+message);
            messageVo.setMsg(message);
            messageVo.setUserId(serverInfo.getUserId());
            messageVo.setCount(2);
            // The message is passed to the student to determine if the student is bound to
            if (null! = serverInfo.getTargetSessionId()){// Send the message to the bound student
                sendMsg(userSessionMap.get(serverSessionMap.get(session.getId()).getTargetSessionId()).getSession(), messageVo);
            }
            sendMsg(session,messageVo);
        } else {// Student information
            System.out.println("Students"+ session.getId()+"Send a message: \""+ message +"\" To the teacher"+userSessionMap.get(session.getId()).getTargetSessionId());
            result.put("msg"."Students"+session.getId()+":"+message);
            messageVo.setCount(2);
            messageVo.setMsg(message);
            messageVo.setUserId(userSessionMap.get(session.getId()).getUserId());
            // The student sends out a message
            sendMsg(session,messageVo);
            // Check whether the teacher is bound and send a message if so
            String targetSessionId = userSessionMap.get(session.getId()).getTargetSessionId();
            if (null! = targetSessionId && ! targetSessionId.equals("kefu")) {// Send a message to the teacher that the current student is consulting
                messageVo.setUserId(userSessionMap.get(session.getId()).getUserId());
                sendMsg(serverSessionMap.get(userSessionMap.get(session.getId()).getTargetSessionId()).getSession(), messageVo);
            }
            if (null! = targetSessionId && targetSessionId.equals("kefu")) {try {
                    Long problemNumber = Long.valueOf(StrUtil.clearNull(message));
                    String answer = buiProblemAnswerService.getAnswer(problemNumber);
                    messageVo.setMsg(answer);
                    if (answer == null){
                        messageVo.setMsg("Please enter the correct problem number!!"); }}catch (Exception e){
                    List<BuiProblemAnswer> buiProblemAnswerList = buiProblemAnswerService.getAnswerList(message);
                    if (buiProblemAnswerList.size() > 1){
                        String msg = "Guess what you want to ask: \t";
                        for (BuiProblemAnswer buiProblemAnswer : buiProblemAnswerList) {
                            msg = msg + buiProblemAnswer.getProblemTopic()+"\t";
                        }
                        messageVo.setMsg(msg);
                    }else if (buiProblemAnswerList.size() == 1){
                        messageVo.setMsg(buiProblemAnswerList.get(0).getProblemAnswer());
                    }else if (buiProblemAnswerList.size() == 0){
                        messageVo.setMsg("I'm sorry! Didn't find your problem!");
                    }

// messagevo. setMsg(" guess what you want to ask :\t"+);
// messagevo. setMsg(" Please enter the correct problem number!! ") );}}// Reply to student messages
            messageVo.setUserId(null); sendMsg(session,messageVo); }}/** * Closes the method called by the connection, and the group member exits@param session
     * @param targetSessionId
     */
    @OnClose
    public void onClose(Session session, @PathParam("targetSessionId") String targetSessionId, @PathParam("userId") Integer userId) {
        SocketUserInfo serverInfo = serverSessionMap.get(session.getId());
        MessageVo messageVo = new MessageVo();
        if(serverInfo ! =null) {// Remove the teacher from the map
            for (int i = 0; i < teacherUserMapList.size(); i++) {
                Map<String, Object> map = teacherUserMapList.get(i);
                if (userId == map.get("userId")){ teacherUserMapList.remove(i); }}// Update the cached data of the online teacher
            cacheProxy.set("websocket",teacherUserMapList);
            serverSessionMap.remove(session.getId());
            // Check whether there are currently serving objects and send system error messages to the user if there are
            if(serverInfo.getTargetSessionId() ! =null){
                messageVo.setMsg("System error. The teacher has been logged out.");
                messageVo.setUserId(serverInfo.getUserId());
                sendMsg(userSessionMap.get(serverInfo.getTargetSessionId()).getSession(),messageVo);
            }
            log.info("Teacher No. : + session.getId() + "Quit connection, current online teacher total:" + serverSessionMap.size());
        }else if ("10000".equals(targetSessionId)){
            // If there are still students waiting in the queue, bind the student to the current teacher
            userSessionMap.remove(session.getId());

            // Get the current teacher information
            SocketUserInfo serverUserInfo = serverSessionMap.get(targetSessionId);
            HashMap<String, Object> teacherMap = new HashMap<>();
            teacherMap.put("userName",serverUserInfo.getUserName());
            teacherMap.put("serssionId",serverUserInfo.getSessionId());
            teacherMap.put("tel",serverUserInfo.getTerminalCode());
            teacherMap.put("userId",serverUserInfo.getUserId());

            // Add the current teacher to the free teacher list and update the cache
            teacherUserMapList.add(teacherMap);
            cacheProxy.set("websocket",teacherUserMapList);

            log.info("User Id:" + session.getId() + "Disconnected, current online students total:"+ userSessionMap.size()); }}/** * Transfer message error called method **@param error
     */
    @OnError
    public void OnError(Throwable error) {
        log.info("Connection error");
    }


    // Query queued users
    private synchronized String findLineUser(a){
        // Check whether there are users
        if (userSessionMap.size() > 0) {// Iterate over all users to find a queued user
            for (SocketUserInfo UserInfo: userSessionMap.values()) {
                if (null == UserInfo.getTargetSessionId()){
                    returnUserInfo.getSessionId(); }}}return null;
    }

    // Query available teachers online
    private synchronized String findFreeServer(a){
        // Check if there is a teacher
        if (serverSessionMap.size() > 0) {// Walk through all the teachers online to find a free teacher
            for (SocketUserInfo serverInfo: serverSessionMap.values()) {
                if (null == serverInfo.getTargetSessionId()){
                    returnserverInfo.getSessionId(); }}}return null;
    }

    // Unified message sending method
    private synchronized void sendMsg(Session session, MessageVo messageVo) {
        try {
            session.getBasicRemote().sendText(JSON.toJSONString(messageVo));
        } catch(IOException e) { e.printStackTrace(); }}}Copy the code

Can’t inject bean instance in websocket

This allows you to inject instances using @AutoWired

Finally, the front-end can use ‘ws:// IP address: port/item address /groupChat/{parameter 1}/{parameter 2}’ to request a connection

Note: If user authentication is performed in the background and an interceptor is configured, the interface needs to be allowed