前端代码
const status_created=1;
const status_assigned=2;
const status_communication=3;
const status_finish=4;
var workOrderId = "";
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,
};
sendSocket(p);
}
function sendQuestionText() {
let text = $("#questionCentent").val();
if(text==""){
return;
}
let p = {
workOrderId: workOrderId,
type: 1,
questionCentent: text,
imgList: "",
};
sendSocket(p);
$("#questionCentent").val("");
}
function createSocket() {
var jwtToken=$.cookie(Constant.tokenCookieKey);
var lang=$.cookie(Constant.langCookieKey);
lang=lang?lang:"cn";
socket = new WebSocket(Constant.webSocketUrl+encodeURIComponent(jwtToken)+"/"+lang);
socket.onopen = function() {
};
socket.onmessage = function(event) {
console.log("收到推送:"+event.data);
let msg=JSON.parse(event.data);
if(msg.code==200){
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){
setTimeout(function () {
socket.send(JSON.stringify(p));
}, 2000);
}else if(socket.readyState==1){
socket.send(JSON.stringify(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 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{
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;
@CrossOrigin
@Component
@ServerEndpoint(value = "/websocket/{token}/{lang}")
@Slf4j
public class WebSocketServer {
private static final ConcurrentHashMap<String, Session> userSessionMap = new ConcurrentHashMap<>();
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);
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);
} 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);
}
}
@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");
if (type == null) {
log.info("websocket>>>用户消息>>type参数为空>>" + userName + ">>" + message);
return;
}
if (WorkOrderCommunicationType.question.getCode().equals(type)) {
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)) {
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);
}
}
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);
}
}
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;
}