1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-08-01 16:38:02 +08:00
This commit is contained in:
Hai Liang Wang 2019-11-12 20:05:23 +08:00
parent 063e05ca1f
commit 2633da217c
11 changed files with 85 additions and 84 deletions

View File

@ -14,7 +14,7 @@ RUN chmod +x /opt/install-corretto-8.sh && /opt/install-corretto-8.sh
# install other lib and configure timezone # install other lib and configure timezone
RUN apt-get update && \ RUN apt-get update && \
apt-get install --no-install-recommends -y tzdata mysql-client-5.7 zip unzip vim-tiny && \ apt-get install --no-install-recommends -y tzdata mysql-client-5.7 zip unzip vim-tiny libfontconfig1 libfreetype6 && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure --frontend noninteractive tzdata && \ DEBIAN_FRONTEND=noninteractive dpkg-reconfigure --frontend noninteractive tzdata && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*

View File

@ -42,7 +42,7 @@ public class AgentSessionSubscription {
// 把登出消息通知给浏览器 // 把登出消息通知给浏览器
NettyClients.getInstance().publishLeaveEventMessage( NettyClients.getInstance().publishLeaveEventMessage(
json.get("agentno").getAsString(), json.get("agentno").getAsString(),
json.get("post").getAsString()); json.get("expired").getAsString());
} catch (Exception e) { } catch (Exception e) {
logger.warn("[onMessage] error", e); logger.warn("[onMessage] error", e);
} }

View File

@ -24,6 +24,7 @@ import com.chatopera.cc.config.MessagingServerConfigure;
import com.chatopera.cc.model.Dict; import com.chatopera.cc.model.Dict;
import com.chatopera.cc.model.SystemConfig; import com.chatopera.cc.model.SystemConfig;
import com.chatopera.cc.model.User; import com.chatopera.cc.model.User;
import com.chatopera.cc.proxy.AgentSessionProxy;
import com.chatopera.cc.proxy.UserProxy; import com.chatopera.cc.proxy.UserProxy;
import com.chatopera.cc.util.Menu; import com.chatopera.cc.util.Menu;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -40,7 +41,7 @@ import javax.servlet.http.HttpServletResponse;
public class UserInterceptorHandler extends HandlerInterceptorAdapter { public class UserInterceptorHandler extends HandlerInterceptorAdapter {
private final static Logger logger = LoggerFactory.getLogger(UserInterceptorHandler.class); private final static Logger logger = LoggerFactory.getLogger(UserInterceptorHandler.class);
private static UserProxy userProxy; private static UserProxy userProxy;
private static AgentSessionProxy agentSessionProxy;
private static Integer webimport; private static Integer webimport;
@Override @Override
@ -48,12 +49,14 @@ public class UserInterceptorHandler extends HandlerInterceptorAdapter {
throws Exception { throws Exception {
boolean filter = false; boolean filter = false;
User user = (User) request.getSession(true).getAttribute(Constants.USER_SESSION_NAME); User user = (User) request.getSession(true).getAttribute(Constants.USER_SESSION_NAME);
if (handler instanceof HandlerMethod) { if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler; HandlerMethod handlerMethod = (HandlerMethod) handler;
Menu menu = handlerMethod.getMethod().getAnnotation(Menu.class); Menu menu = handlerMethod.getMethod().getAnnotation(Menu.class);
if (user != null || (menu != null && menu.access()) || handlerMethod.getBean() instanceof BasicErrorController) { if (user != null || (menu != null && menu.access()) || handlerMethod.getBean() instanceof BasicErrorController) {
filter = true; filter = true;
if (user != null && StringUtils.isNotBlank(user.getId())) { if (user != null && StringUtils.isNotBlank(user.getId())) {
/** /**
* 每次刷新用户的组织机构角色和权限 * 每次刷新用户的组织机构角色和权限
* TODO 此处代码执行频率高但是并不是每次都要执行存在很多冗余 * TODO 此处代码执行频率高但是并不是每次都要执行存在很多冗余
@ -164,7 +167,7 @@ public class UserInterceptorHandler extends HandlerInterceptorAdapter {
} }
public static Integer getWebimport() { private static Integer getWebimport() {
if (webimport == null) { if (webimport == null) {
webimport = MainContext.getContext().getBean(MessagingServerConfigure.class).getWebIMPort(); webimport = MainContext.getContext().getBean(MessagingServerConfigure.class).getWebIMPort();
} }
@ -172,10 +175,18 @@ public class UserInterceptorHandler extends HandlerInterceptorAdapter {
} }
public static UserProxy getUserProxy() { private static UserProxy getUserProxy() {
if (userProxy == null) { if (userProxy == null) {
userProxy = MainContext.getContext().getBean(UserProxy.class); userProxy = MainContext.getContext().getBean(UserProxy.class);
} }
return userProxy; return userProxy;
} }
private static AgentSessionProxy getAgentSessionProxy() {
if (agentSessionProxy == null) {
agentSessionProxy = MainContext.getContext().getBean(AgentSessionProxy.class);
}
return agentSessionProxy;
}
} }

View File

@ -48,31 +48,42 @@ public class AgentSessionProxy {
* @param orgi * @param orgi
*/ */
public void updateUserSession(final String agentno, final String sessionId, final String orgi) { public void updateUserSession(final String agentno, final String sessionId, final String orgi) {
// logger.info("[updateUserSession] agentno {}, sessionId {}, orgi {}", agentno, sessionId, orgi); logger.info("[updateUserSession] agentno {}, sessionId {}, orgi {}", agentno, sessionId, orgi);
if (cache.existUserSessionByAgentnoAndOrgi(agentno, orgi)) { if (cache.existUserSessionByAgentnoAndOrgi(agentno, orgi)) {
final String preSessionId = cache.findOneSessionIdByAgentnoAndOrgi(agentno, orgi); final String preSessionId = cache.findOneSessionIdByAgentnoAndOrgi(agentno, orgi);
if (StringUtils.equals(preSessionId, sessionId)) { if (StringUtils.equals(preSessionId, sessionId)) {
// 现在的session和之前的是一样的忽略更新 // 现在的session和之前的是一样的忽略更新
// logger.info( logger.info(
// "[updateUserSession] agentno {}, sessionId {} is synchronized, skip update.", agentno, "[updateUserSession] agentno {}, sessionId {} is synchronized, skip update.", agentno,
// sessionId); sessionId);
return; return;
} }
if (StringUtils.isNotBlank(preSessionId)) { if (StringUtils.isNotBlank(preSessionId)) {
// 通知浏览器登出 publishAgentLeaveEvent(agentno, sessionId, orgi);
// logger.info("[updateUserSession] notify logut browser");
JsonObject payload = new JsonObject();
payload.addProperty("agentno", agentno); // 坐席ID
payload.addProperty("pre", preSessionId); // 之前的Session
payload.addProperty("orgi", orgi); // 租户Id
payload.addProperty("post", sessionId); // 之后的Id
brokerPublisher.send(Constants.MQ_TOPIC_WEB_SESSION_SSO, payload.toString(), true);
} }
} }
cache.putUserSessionByAgentnoAndSessionIdAndOrgi(agentno, sessionId, orgi); cache.putUserSessionByAgentnoAndSessionIdAndOrgi(agentno, sessionId, orgi);
} }
/**
* 通知浏览器登出
*
* @param agentno
* @param expired 过期的SessionID
* @param orgi
*/
public void publishAgentLeaveEvent(final String agentno, final String expired, final String orgi) {
//
logger.info("[publishAgentLeaveEvent] notify logut browser, expired session {}", expired);
JsonObject payload = new JsonObject();
payload.addProperty("agentno", agentno); // 坐席ID
payload.addProperty("orgi", orgi); // 租户Id
payload.addProperty("expired", expired); // 之后的Id
brokerPublisher.send(Constants.MQ_TOPIC_WEB_SESSION_SSO, payload.toString(), true);
}
/** /**
* 是否是"不合法"的Session信息 * 是否是"不合法"的Session信息
* *

View File

@ -186,8 +186,13 @@ public class AgentEventHandler {
final SocketIOClient client, final SocketIOClient client,
final AckRequest request, final AckRequest request,
final InterventMessage received) throws JsonProcessingException { final InterventMessage received) throws JsonProcessingException {
final String agentno = client.get("agentno");
final String session = client.get("session");
final String connectid = client.get("connectid");
logger.info( logger.info(
"[onEvent] intervention: {}", received.toJsonObject()); "[onIntervetionEvent] intervention: agentno {}, session {}, connectid {}, payload {}", agentno, session, connectid,
received.toJsonObject());
if (received.valid()) { if (received.valid()) {
// 获得AgentUser // 获得AgentUser
@ -195,9 +200,11 @@ public class AgentEventHandler {
// 验证当前的SSO中的session是否和传入的session匹配 // 验证当前的SSO中的session是否和传入的session匹配
if (getAgentSessionProxy().isInvalidSessionId( if (getAgentSessionProxy().isInvalidSessionId(
received.getSupervisorid(), received.getSession(), agentUser.getOrgi())) { agentno, session, agentUser.getOrgi())) {
// 该session信息不合法 // 该session信息不合法
logger.info("[onConnect] invalid sessionId {}", received.getSession()); logger.info("[onIntervetionEvent] invalid sessionId {}", session);
// 强制退出
client.sendEvent(MainContext.MessageType.LEAVE.toString());
return; return;
} }
@ -265,19 +272,35 @@ public class AgentEventHandler {
final SocketIOClient client, final SocketIOClient client,
final AckRequest request, final AckRequest request,
final ChatMessage received) throws IOException { final ChatMessage received) throws IOException {
final String agentno = client.get("agentno");
final String session = client.get("session");
final String connectid = client.get("connectid");
received.setSessionid(session);
// 此处user代表坐席的ID // 此处user代表坐席的ID
String agentno = client.getHandshakeData().getSingleUrlParam("userid"); // String agentno = client.getHandshakeData().getSingleUrlParam("userid");
logger.info( logger.info(
"[onEvent] message: agentUserId {}, agentno {}, toUser {}, channel {}, orgi {}, appId {}, userId {}, sessionId {}", "[onMessageEvent] message: agentUserId {}, agentno {}, toUser {}, channel {}, orgi {}, appId {}, userId {}, sessionId {}, connectid {}",
received.getAgentuser(), agentno, received.getTouser(), received.getAgentuser(), agentno, received.getTouser(),
received.getChannel(), received.getOrgi(), received.getAppid(), received.getUserid(), received.getChannel(), received.getOrgi(), received.getAppid(), received.getUserid(),
received.getSessionid()); session, connectid);
// 验证当前的SSO中的session是否和传入的session匹配
if (getAgentSessionProxy().isInvalidSessionId(
agentno, session, received.getOrgi())) {
// 该session信息不合法
logger.info("[onMessageEvent] invalid sessionId {}", session);
// 强制退出
client.sendEvent(MainContext.MessageType.LEAVE.toString());
return;
}
AgentUser agentUser = MainContext.getCache().findOneAgentUserByUserIdAndOrgi( AgentUser agentUser = MainContext.getCache().findOneAgentUserByUserIdAndOrgi(
received.getTouser(), received.getOrgi()).orElseGet(null); received.getTouser(), received.getOrgi()).orElseGet(null);
/** /**
* 判断用户在线状态如果用户在线则通过webim发送 * 判断用户在线状态如果用户在线则通过webim发送
* 检查收发双方的信息匹配 * 检查收发双方的信息匹配

View File

@ -70,7 +70,7 @@ public class IMEventHandler {
try { try {
final String user = client.getHandshakeData().getSingleUrlParam("userid"); final String user = client.getHandshakeData().getSingleUrlParam("userid");
final String orgi = client.getHandshakeData().getSingleUrlParam("orgi"); final String orgi = client.getHandshakeData().getSingleUrlParam("orgi");
final String session = client.getHandshakeData().getSingleUrlParam("session"); final String session = MainUtils.getContextID(client.getHandshakeData().getSingleUrlParam("session"));
final String appid = client.getHandshakeData().getSingleUrlParam("appid"); final String appid = client.getHandshakeData().getSingleUrlParam("appid");
final String agent = client.getHandshakeData().getSingleUrlParam("agent"); final String agent = client.getHandshakeData().getSingleUrlParam("agent");
final String skill = client.getHandshakeData().getSingleUrlParam("skill"); final String skill = client.getHandshakeData().getSingleUrlParam("skill");
@ -88,6 +88,11 @@ public class IMEventHandler {
"[onConnect] user {}, orgi {}, session {}, appid {}, agent {}, skill {}, title {}, url {}, traceid {}, nickname {}", "[onConnect] user {}, orgi {}, session {}, appid {}, agent {}, skill {}, title {}, url {}, traceid {}, nickname {}",
user, orgi, session, appid, agent, skill, title, url, traceid, nickname); user, orgi, session, appid, agent, skill, title, url, traceid, nickname);
// save connection info
client.set("session", session);
client.set("userid", user);
client.set("appid", appid);
if (StringUtils.isNotBlank(user)) { if (StringUtils.isNotBlank(user)) {
InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress(); InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress();
String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString()); String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString());
@ -189,7 +194,7 @@ public class IMEventHandler {
, orgi); , orgi);
}); });
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.warn("[onDisconnect] error", e);
} }
NettyClients.getInstance().removeIMEventClient( NettyClients.getInstance().removeIMEventClient(
user, MainUtils.getContextID(client.getSessionId().toString())); user, MainUtils.getContextID(client.getSessionId().toString()));

View File

@ -101,8 +101,11 @@ $(document).ready(function(){
$('#agentdesktop').attr('data-href', '/agent/index.html?userid='+data.userid).click(); $('#agentdesktop').attr('data-href', '/agent/index.html?userid='+data.userid).click();
} }
}).on('leave', function(data){ }).on('leave', function(data){
// 执行登出 top.layer.msg('当前会话已经过期,稍后将自动登出!',{icon: 1, time: 2000});
window.location.href = "/logout.html?code=2"; setTimeout(function(){
// 执行登出
window.location.href = "/logout.html?code=2";
}, 2000);
}); });
/****每分钟执行一次,与服务器交互,保持会话****/ /****每分钟执行一次,与服务器交互,保持会话****/
setInterval(function(){ setInterval(function(){

View File

@ -57,13 +57,13 @@ function restApiRequest(opts) {
// 操作成功的 // 操作成功的
function handleRestApiSucc(msg) { function handleRestApiSucc(msg) {
layer.msg( msg || '操作成功',{icon: 1, offset: 'b', time: 1000}) layer.msg( msg || '操作成功',{icon: 1, time: 1000})
} }
// 操作失败的 // 操作失败的
function handleRestApiFail(status, reason) { function handleRestApiFail(status, reason) {
if(status && status === 'AUTH_ERROR'){ if(status && status === 'AUTH_ERROR'){
layer.msg('会话过期,请重新登录!',{icon: 2, offset: 'b', time: 3000}); layer.msg('会话过期,请重新登录!',{icon: 2, time: 3000});
setTimeout(function(){ setTimeout(function(){
// 执行登出 // 执行登出
window.location.href = "/logout.html"; window.location.href = "/logout.html";

View File

@ -36,13 +36,6 @@
<a href="/apps/contacts/add.html<#if ckind??>?ckind=${ckind}</#if>" title="新建联系人" data-toggle="ajax" data-width="950" data-height="600" class="layui-btn layui-btn-small"><i class="layui-icon">&#xe608;</i>新建联系人</a> <a href="/apps/contacts/add.html<#if ckind??>?ckind=${ckind}</#if>" title="新建联系人" data-toggle="ajax" data-width="950" data-height="600" class="layui-btn layui-btn-small"><i class="layui-icon">&#xe608;</i>新建联系人</a>
</div> </div>
</#if> </#if>
<#if user?? && (user.roleAuthMap[ "A02_A01_A02_B08"]?? || user.admin)>
<div class="layui-btn-group ukefu-btn-group">
<button class="layui-btn layui-btn-small" id="mass" title="群发" data-toggle="ajax" data-width="700" data-height="495">
<i class="kfont">&#xe654;</i> 群发
</button>
</div>
</#if>
<#if user?? && (user.roleAuthMap[ "A02_A01_A02_B08"]?? || user.admin)> <#if user?? && (user.roleAuthMap[ "A02_A01_A02_B08"]?? || user.admin)>
<div class="layui-btn-group ukefu-btn-group"> <div class="layui-btn-group ukefu-btn-group">
<button class="layui-btn layui-btn-small" href="/apps/contacts/imp.html<#if ckind??>?ckind=${ckind}</#if>" title="导入联系人" data-toggle="ajax" data-width="950" data-height="600"> <button class="layui-btn layui-btn-small" href="/apps/contacts/imp.html<#if ckind??>?ckind=${ckind}</#if>" title="导入联系人" data-toggle="ajax" data-width="950" data-height="600">
@ -207,51 +200,6 @@
$('#batexp').attr("href", "javascript:void(0)"); $('#batexp').attr("href", "javascript:void(0)");
} }
}); });
// TODO 群发消息时重构
// $("#mass").click(function () {
// var ids = "";
// var num = 0;
// $('.ids').each(function() {
// if ($(this).prop("checked")) {
// if (ids != "") {
// ids += "&";
// }
// ids += "ids=" + $(this).val();
// num++;
// }
// });
// if (num > 0) {
// $('#mass').attr('href', '/apps/contacts/startmass.html?' + ids);
// console.log(ids);
// } else {
// layer.msg('请先选择需要群发的联系人信息',{icon: 2, offset: 'rb', time: 3000})
// $('#mass').attr("href", "javascript:void(0)");
// }
// })
// TODO 群发消息时重构
// function getQueryVariable(variable) {
// var query = window.location.search.substring(1);
// var vars = query.split("&");
// for (var i=0;i<vars.length;i++) {
// var pair = vars[i].split("=");
// if(pair[0] == variable){return pair[1];}
// }
// return(false);
// }
// TODO 群发消息时重构
// if ( getQueryVariable('massStatus') ){
// var multiMediaDialogWin = parent.$('#multiMediaDialogWin');
//
// if(multiMediaDialogWin.length>0){
// parent.$('#multiMediaDialogWin').load(function{parent.layui.element().tabChange('ukefutab','multiMediaDialogWin');}).attr('src' , '/agent/index.html');
// } else {
// parent.$('#agentdesktop').attr('data-href' , '/agent/index.html').click();
// }
// }
}); });
layui.use(['laypage', 'layer'], function() { layui.use(['laypage', 'layer'], function() {
var laypage = layui.laypage, var laypage = layui.laypage,

View File

@ -313,9 +313,9 @@
drawCanvasImage(data.attachmentid) ; drawCanvasImage(data.attachmentid) ;
} }
if(data.calltype == "呼入"){ if(data.calltype == "呼入"){
output('<div class="chat-right"> <img class="user-img" src="/im/img/user.png" alt=""><div class="chat-message"><label class="time">'+data.createtime+'</label><label class="user">'+data.nickName+'</label> </div><div class="chatting-right"><i class="arrow arrow${inviteData.consult_dialog_color!''}"></i><div class="chat-content theme${inviteData.consult_dialog_color!''}">'+chat+'</div></div>' , "chat-block"); output('<div class="chat-right"> <img class="user-img" src="/im/img/user.png" alt=""><div class="chat-message"><label class="time">'+data.createtime+'</label><label class="user">'+data.username+'</label> </div><div class="chatting-right"><i class="arrow arrow${inviteData.consult_dialog_color!''}"></i><div class="chat-content theme${inviteData.consult_dialog_color!''}">'+chat+'</div></div>' , "chat-block");
}else if(data.calltype == "呼出"){ }else if(data.calltype == "呼出"){
output('<div class="chat-left"> <img class="user-img" src="<#if inviteData?? && inviteData.consult_dialog_headimg??>/res/image.html?id=${inviteData.consult_dialog_headimg?url}<#else>/images/agent.png</#if>" alt=""><div class="chat-message"><label class="user">'+data.nickName+'</label><label class="time">'+data.createtime+'</label> </div><div class="chatting-left"><i class="arrow"></i><div class="chat-content">'+chat+'</div></div>' , "chat-block"); output('<div class="chat-left"> <img class="user-img" src="<#if inviteData?? && inviteData.consult_dialog_headimg??>/res/image.html?id=${inviteData.consult_dialog_headimg?url}<#else>/images/agent.png</#if>" alt=""><div class="chat-message"><label class="user">'+data.username+'</label><label class="time">'+data.createtime+'</label> </div><div class="chatting-left"><i class="arrow"></i><div class="chat-content">'+chat+'</div></div>' , "chat-block");
} }
}); });

View File

@ -25,7 +25,7 @@
<#elseif msg?? && msg == '1'> <#elseif msg?? && msg == '1'>
layer.msg('用户注册成功,请通过用户名和密码登陆',{icon: 2, time: 3000}) layer.msg('用户注册成功,请通过用户名和密码登陆',{icon: 2, time: 3000})
<#elseif msg?? && msg == '2'> <#elseif msg?? && msg == '2'>
layer.msg('您的账号已经在其它浏览器登录,请确认是您本人的操作,保证安全。',{icon: 2, offset: 't', time: 0}) layer.msg('您的账号已经在其它浏览器登录,请确认是您本人的操作,保证安全。',{icon: 4, offset: 't', time: 0})
</#if> </#if>
}); });
layui.use('form', function(){ layui.use('form', function(){