WebSocket实战

前端代码

const status_created=1;
const status_assigned=2;
const status_communication=3;
const status_finish=4;

var workOrderId = "";
// var lastCommunicationId = 0;
// var mytimeout = null;
function appendQuestion(communication) {
    let imgRows = "";
    if (!Utils.isEmpty(communication.imgList)) {
        let imgIds = communication.imgList.split(",");
        if (imgIds.length > 0) {
            for (let index = 0; index < imgIds.length; index++) {
                const imgId = imgIds[index];
                imgRows = imgRows + `<div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
                    <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
                        <img src="`+ Constant.downloadUrl + imgId + `" class="chat_img_custom"/>
                    </div>
                </div>`;
            }
        }
    }
    let text = "";
    if (!Utils.isEmpty(communication.questionCentent)) {
        text = `<div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
            <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
                <p class="chat_p_custom">`+ communication.questionCentent + `</p>
            </div>
        </div>`;
    }
    let html = `<div class="row myrow" style="margin-top: 10px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
            <label class="mylabel">`+ communication.createTime + ` `+i18n.t("我")+`</label>
            <i class="fa fa-user"></i>
        </div>
    </div>`+
        imgRows + text;
    $("#chat_content_div").append(html);
}
function appendAnswer(communication) {
    let imgRows = "";
    if (!Utils.isEmpty(communication.handleImgList)) {
        let imgIds = communication.handleImgList.split(",");
        if (imgIds.length > 0) {
            for (let index = 0; index < imgIds.length; index++) {
                const imgId = imgIds[index];
                imgRows = imgRows + `<div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
                    <div class="col-lg-12 mycol" style="text-align: left;padding: 0px 10px 0px 10px;">
                        <img src="`+ Constant.downloadUrl + imgId + `" class="chat_img_service"/>
                    </div>
                </div>`;
            }
        }
    }
    let text = "";
    if (!Utils.isEmpty(communication.handleCentent)) {
        text = `<div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
            <div class="col-lg-12 mycol" style="text-align: left;padding: 0px 10px 0px 10px;">
                <p class="chat_p_service">`+ communication.handleCentent + `</p>
            </div>
        </div>`;
    }
    let html = `<div class="row myrow" style="margin-top: 10px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: left;padding: 0px 10px 0px 10px;">
            <label class="mylabel">`+ communication.createTime + ` `+i18n.t("在线客服")+`</label>
            <i class="fa fa-user"></i>
        </div>
    </div>`+
        imgRows + text;
    $("#chat_content_div").append(html);
}

function appendQuestionImg(communication) {
    let html = `<div class="row myrow" style="margin-top: 10px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
            <label class="mylabel">`+ communication.createTime + ` `+i18n.t("我")+`</label>
            <i class="fa fa-user"></i>
        </div>
    </div>
    <div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
            <img src="`+ Constant.downloadUrl + communication.imgList + `" class="chat_img_custom"/>
        </div>
    </div>`;
    $("#chat_content_div").append(html);
}
function appendQuestionText(communication) {
    let html = `<div class="row myrow" style="margin-top: 10px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
            <label class="mylabel">`+ communication.createTime + ` `+i18n.t("我")+`</label>
            <i class="fa fa-user"></i>
        </div>
    </div>
    <div class="row myrow" style="margin-top: 0px;margin-bottom: 0px;">
        <div class="col-lg-12 mycol" style="text-align: right;padding: 0px 10px 0px 10px;">
        <p class="chat_p_custom">`+ communication.questionCentent + `</p>
        </div>
    </div>`;
    $("#chat_content_div").append(html);
}
function appendQuestion(p) {
    if(p.imgList==""||p.imgList==0){
        appendQuestionText(p);
    }else{
        appendQuestionImg(p);
    }
}
var socket=null;
function sendQuestionImg(result) {
    let p = {
        workOrderId: workOrderId,
        type: 1,
        questionCentent: "",
        imgList: result.id,
    };
    // OpenApi.appendQuestion(p, function (data) {
    //     lastCommunicationId = data.id;
    //     appendQuestionImg(data);
    // });
    sendSocket(p);
}
function sendQuestionText() {
    let text = $("#questionCentent").val();
    if(text==""){
        return;
    }
    let p = {
        workOrderId: workOrderId,
        type: 1,
        questionCentent: text,
        imgList: "",
    };
    // OpenApi.appendQuestion(p, function (data) {
    //     $("#questionCentent").val("");
    //     lastCommunicationId = data.id;
    //     appendQuestionText(data);
    // });
    sendSocket(p);
    $("#questionCentent").val("");
}
function createSocket() {
    var jwtToken=$.cookie(Constant.tokenCookieKey);
    var lang=$.cookie(Constant.langCookieKey);
    lang=lang?lang:"cn";
    //wss://open.xuexibisai.com/openfrontapi/openfront/websocket/
    socket = new WebSocket(Constant.webSocketUrl+encodeURIComponent(jwtToken)+"/"+lang);
    socket.onopen = function() {
        //socket.send(JSON.stringify(p));
        //appendQuestion(p);
    };
    socket.onmessage = function(event) {
        console.log("收到推送:"+event.data);
        let msg=JSON.parse(event.data);
        if(msg.code==200){
            //如果type=1,是返回值,否则是主动推送
            if(msg.data.type==1){
                appendQuestion(msg.data);
            }else{
                appendAnswer(msg.data);
            }
            var chat_content_div = document.getElementById('chat_content_div');
            chat_content_div.scrollTop = chat_content_div.scrollHeight;
        }else{
            alert(msg.msg);
        }
    };
    socket.onclose = function(e) {

    }
    return socket;
}

function sendSocket(p) {
    if(socket==null){
        createSocket();
    }
    if(socket.readyState==0){//如果为0表示还在连接中,必须等下再发送,不然会报错
        setTimeout(function () {
            socket.send(JSON.stringify(p));
            //appendQuestion(p);
        }, 2000);
    }else if(socket.readyState==1){
        socket.send(JSON.stringify(p));
        //appendQuestion(p);
    }else{
        socket=null;
        sendSocket();
    }
}

function startUploader(up) {
    var params = {
    };
    up.setOption({
        'url': Constant.uploadUrl,
        'multipart_params': params
    });
    up.start();
}
function createUploader() {
    var uploader = new plupload.Uploader({
        runtimes: 'html5',
        browse_button: 'selectfiles',
        container: document.getElementById('container'),
        url: Constant.uploadUrl,
        filters: {
            mime_types: [ //只允许上传图片
                { title: "Image files", extensions: "jpg,gif,png" },
            ],
            max_file_size: '10mb', 
            prevent_duplicates: false //允许选取重复文件
        },
        max_file_size: '10mb',
        init: {
            PostInit: function () {
            },
            FilesAdded: function (up, files) {
                startUploader(up);
            },
            BeforeUpload: function (up, file) {
            },
            UploadProgress: function (up, file) {
            },
            FileUploaded: function (up, file, info) {
                if (info.status == 200) {
                    let resp = JSON.parse(info.response);
                    if (resp.code == 200) {
                        let result = resp.data;
                        sendQuestionImg(result);
                    } else {
                        alert(resp.msg);
                    }
                } else {//无法连接服务器
                    alert(i18n.t("无法连接服务器,请检查网络连接!"));
                }
            },
            Error: function (up, err) {
                alert(i18n.t("上传失败"));
            }
        }
    });
    return uploader;
}
// function refreshCommunication() {
//     OpenApi.findWorkOrderDetail(workOrderId, function (data) {
//         let communicationList = data.communicationList;
//         for (let i = 0; i < communicationList.length; i++) {
//             const communication = communicationList[i];
//             if (communication.id > lastCommunicationId) {
//                 if (communication.type == 1) {
//                     appendQuestion(communication);
//                 } else {
//                     appendAnswer(communication);
//                 }
//                 lastCommunicationId = communication.id;
//             }
//         }
//     })
// }

function showEvaluationModal() {
    $("#evaluation_modal input[name='id']").val(workOrderId);
    $('#evaluation_modal').modal('show');
}

function setStar(i) {
    $(i).siblings().css("color", "gray");
    $(i).prevAll().css("color", "yellowgreen");
    $(i).css("color", "yellowgreen");
    $("#evaluation_modal input[name='satisfaction']").val($(i).attr("value"));
}

function submitEvaluation() {
    let data = Utils.formToObject($("#evaluation_form"));
    if (data.satisfaction == "") {
        alert(i18n.t("您还没有评分哦"));
        return;
    }
    if (data.evaluation == "" || data.evaluation.length < 5) {
        alert(i18n.t("评价不能少于5个字"));
        return;
    }
    OpenApi.finishAndEvaluationWorkOrder(data, function (data) {
        $('#evaluation_modal').modal('hide');
        location.href = "workOrderList.html"
    });
}

jQuery(document).ready(function () {
    let uploader = createUploader();
    uploader.init();

    workOrderId = parseInt(Utils.params()["id"]);
    OpenApi.findWorkOrderDetail(workOrderId, function (data) {
        let communicationList = data.communicationList;
        if(communicationList.length>0){
            lastCommunicationId = communicationList[communicationList.length - 1].id;
        }
        for (let i = 0; i < communicationList.length; i++) {
            const communication = communicationList[i];
            if (communication.type == 1) {
                appendQuestion(communication);
            } else {
                appendAnswer(communication);
            }
        }
        if(data.status==status_finish){
            $("#op_row").hide();
            $("#chat_content_row").css("margin-bottom","20px");
        }else{
            // mytimeout = setInterval(refreshCommunication, 15000);
            createSocket();
        }

        var chat_content_div = document.getElementById('chat_content_div');
        chat_content_div.scrollTop = chat_content_div.scrollHeight;
    })
});

后台代码(spring boot)

pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

spring boot config

@Configuration
public class SpringWebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

WebSocket处理器

package com.xxxx.open.front.websocket;

import com.alibaba.fastjson2.JSONObject;
import com.xxxx.base.bean.dto.result.Result;
import com.xxxx.base.jwt.util.JwtUtil;
import com.xxxx.base.util.Utils;
import com.xxxx.base.util.spring.SpringContextHolder;
import com.xxxx.open.bean.entity.SysUser;
import com.xxxx.open.bean.entity.WorkOrder;
import com.xxxx.open.bean.entity.WorkOrderCommunication;
import com.xxxx.open.bean.status.WorkOrderCommunicationType;
import com.xxxx.open.service.SysUserService;
import com.xxxx.open.service.WorkOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/*
访问路径:ws://ip:port/server.servlet.context-path/ServerEndpoint.value
必要条件:一、shiro需要放行、二、使用spring boot自带tomcat启动,java -jar命令运行项目
* */
@CrossOrigin
@Component
@ServerEndpoint(value = "/websocket/{token}/{lang}")
@Slf4j
public class WebSocketServer {

    /**
     * 使用原子类,可以不加锁,避免繁琐的锁操作
     */
    private static final ConcurrentHashMap<String, Session> userSessionMap = new ConcurrentHashMap<>();

    /**
     * @ServerEndpoint无法使用@Autowired
     */
    private static final WorkOrderService workOrderService= SpringContextHolder.getBean(WorkOrderService.class);
    private static final SysUserService sysUserService=SpringContextHolder.getBean(SysUserService.class);

    private String getUserName(String token) {
        try {
            String userName = JwtUtil.getUserInfo(URLDecoder.decode(token)).getUserName();
            return userName;
        } catch (Exception e) {
            log.error("websocket>>>解析失败>>getUserName" + token, e);
            return null;
        }
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        final String userName = getUserName(token);
        if (!Utils.isEmptyTrim(userName)) {
            if(userSessionMap.containsKey(userName)){
                Session oldSession = userSessionMap.get(userName);
                if (oldSession != null&&oldSession.isOpen()) {
                    try {
                        oldSession.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                userSessionMap.remove(userName);
            }
            userSessionMap.put(userName, session);
            log.info("websocket>>>连接创建>>解析成功>>" + userName);

            //为了确保连接一定会释放,避免userSessionMap爆炸
            Timer timer = new Timer();
            TimerTask timerTask =new TimerTask(){
                public void run() {
                    Session session = userSessionMap.get(userName);
                    if (session != null) {
                        log.info("websocket>>>连接时间超过48小时自动释放1>>" + userName);
                        userSessionMap.remove(userName);
                        if (session.isOpen()){
                            try {
                                log.info("websocket>>>连接时间超过48小时自动释放2>>" + userName);
                                session.close();
                            } catch (Exception e) {
                                log.error("websocket>>>连接时间超过48小时自动释放异常>>" + userName,e);
                            }
                        }
                    }
                }
            };
            timer.schedule(timerTask,1000*60*60*24*2);//毫秒,2天
        } else {
            log.info("websocket>>>连接创建>>解析失败>>" + token);
        }
    }

    @OnClose
    public void onClose(Session session, @PathParam("token") String token) {
        String userName = getUserName(token);
        if (!Utils.isEmptyTrim(userName)) {
            userSessionMap.remove(userName);
            log.info("websocket>>>连接关闭>>解析成功>>" + userName);
        } else {
            log.info("websocket>>>连接关闭>>解析失败>>" + token);
        }
    }

    /**
     * 接收消息
     * @param session
     * @param token
     * @param lang
     * @param message
     */
    @OnMessage
    public void onMessage(Session session, @PathParam("token") String token, @PathParam("lang") String lang, String message) {
        try {
            String userName = getUserName(token);
            if (!Utils.isEmptyTrim(userName)) {
                log.info("websocket>>>用户消息>>解析成功>>" + userName + ">>" + message);
            } else {
                log.info("websocket>>>用户消息>>解析失败>>" + token + ">>" + message);
                return;
            }

            SysUser sysUser = sysUserService.findByName(userName);
            if (sysUser == null) {
                log.info("websocket>>>用户消息>>找不到该用户>>" + userName + ">>" + message);
                return;
            }
            JSONObject msgJson = JSONObject.parseObject(message,null);
            Long workOrderId = msgJson.getLong("workOrderId");
            if (workOrderId == null) {
                log.info("websocket>>>用户消息>>workOrderId参数为空>>" + userName + ">>" + message);
                return;
            }
            WorkOrder workOrder = workOrderService.getById(workOrderId);
            if (workOrder == null) {
                log.info("websocket>>>用户消息>>workOrder不存在>>" + userName + ">>" + message);
                return;
            }
            Integer type = msgJson.getInteger("type");
//            Integer type = msgJson.getInt("type");
            if (type == null) {
                log.info("websocket>>>用户消息>>type参数为空>>" + userName + ">>" + message);
                return;
            }
            //service会检查,这里不再检查
//            //检查工单状态
//            if (WorkOrderStatus.created.getCode().equals(workOrder.getStatus()) || WorkOrderStatus.finish.getCode().equals(workOrder.getStatus())) {
//                //未分配或已结束
//                log.info("websocket>>>用户消息>>workOrder未分配或已结束>>" + userName + ">>" + message);
//                return;
//            }
            if (WorkOrderCommunicationType.question.getCode().equals(type)) {//用户发送的消息
                //service会检查,这里不再检查
//                //检查工单创建人是不是当前用户
//                if (!sysUser.getId().equals(workOrder.getCreateBy())) {
//                    log.info("websocket>>>用户消息>>当前用户不是工单创建人>>" + userName + ">>" + message);
//                    return;
//                }

                //插入数据库
                String questionCentent = msgJson.getString("questionCentent");
                String imgList = msgJson.getString("imgList");
                WorkOrderCommunication appendQuestionData = new WorkOrderCommunication();
                appendQuestionData.setWorkOrderId(workOrderId);
                appendQuestionData.setQuestionCentent(questionCentent);
                appendQuestionData.setImgList(imgList);
                Result result = workOrderService.appendQuestionByWorkSocket(appendQuestionData, lang, sysUser);
                //发送给管理员
                Long handleBy = workOrder.getHandleBy();
                SysUser admin = sysUserService.getById(handleBy);
                sendMessageToUser(admin.getName(), result);
                sendReturnResult(session,userName,result);
            } else if (WorkOrderCommunicationType.answer.getCode().equals(type)) {//管理员发送的消息
                //service会检查,这里不再检查
//                //检查工单处理人是不是当前用户
//                if (!sysUser.getId().equals(workOrder.getHandleBy())) {
//                    log.info("websocket>>>用户消息>>当前用户不是工单处理人>>" + userName + ">>" + message);
//                    return;
//                }
                //插入数据库
                String handleCentent = msgJson.getString("handleCentent");
                String handleImgList = msgJson.getString("handleImgList");
                WorkOrderCommunication appendAnswerData = new WorkOrderCommunication();
                appendAnswerData.setWorkOrderId(workOrderId);
                appendAnswerData.setHandleCentent(handleCentent);
                appendAnswerData.setHandleImgList(handleImgList);
                Result result = workOrderService.appendAnswerByWorkSocket(appendAnswerData,lang,sysUser);
                //发送给用户
                Long createBy = workOrder.getCreateBy();
                SysUser user = sysUserService.getById(createBy);
                sendMessageToUser(user.getName(), result);
                sendReturnResult(session,userName,result);
            } else {
                //不处理
                log.info("websocket>>>用户消息>>type参数不合法>>" + userName + ">>" + message);
            }
        } catch (Exception e) {
            log.error("websocket>>>用户消息>>系统异常>>" + token + ">>" + e.getMessage(), e);
        }
    }

    @OnError
    public void onError(Session session, @PathParam("token") String token, Throwable e) {
        String userName = getUserName(token);
        if (!Utils.isEmptyTrim(userName)) {
            userSessionMap.remove(userName);
            log.error("websocket>>>发生错误>>解析成功" + userName + ">>" + e.getMessage(), e);
        } else {
            log.error("websocket>>>发生错误>>解析失败" + userName + ">>" + e.getMessage(), e);
        }
    }

    /**
     * 发送返回结果
     * @param session
     * @param userName
     * @param result
     */
    public void sendReturnResult(Session session, String userName, Result result) {
        try {
            if (session != null&&session.isOpen()) {
                //同步发送信息
                session.getBasicRemote().sendText(result.toString());
                log.info("websocket>>>发送返回结果成功>>" + userName + ">>" + result);
            } else {
                log.info("websocket>>>发送返回结果失败>>目标用户不在线>>" + userName + ">>" + result);
            }
        } catch (Exception e) {
            log.error("websocket>>>发送返回结果异常>>" + userName + ">>" + result, e);
        }
    }

    /**
     * 向指定用户推送消息
     * @param targetUserName
     * @param message
     */
    public void sendMessageToUser(String targetUserName, Result message) {
        try {
            Session session = userSessionMap.get(targetUserName);
            if (session != null&&session.isOpen()) {
                //同步发送信息
                session.getBasicRemote().sendText(message.toString());
                log.info("websocket>>>推送成功>>" + targetUserName + ">>" + message);
            } else {
                userSessionMap.remove(targetUserName);
                log.info("websocket>>>推送失败>>目标用户不在线>>" + targetUserName + ">>" + message);
            }
        } catch (Exception e) {
            log.error("websocket>>>推送异常>>" + targetUserName + ">>" + message , e);
        }
    }
}

nginx(https或者http都行)

#需要增加的websocket配置
location /openfrontapi/openfront/websocket {
	root /usr/share/nginx/html;
	proxy_pass http://172.31.0.001:9998/openfront/websocket;
	proxy_http_version 1.1;
	proxy_connect_timeout 300;
	proxy_read_timeout 300;
	proxy_send_timeout 300;
	proxy_set_header Upgrade $http_upgrade;#最关键的属性
	proxy_set_header Connection "Upgrade";#最关键的属性
}
#原本的后台配置
location /openfrontapi/ {
	proxy_pass http://172.31.0.001:9998/;
}
#原本的前端配置
location /openfrontweb {
	alias /home/ec2-user/data/openfront;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值