From 29ebd3125b87a3519f003f1e5901eb4c48919ea6 Mon Sep 17 00:00:00 2001 From: Hai Liang Wang Date: Tue, 23 Oct 2018 17:17:02 +0800 Subject: [PATCH] =?UTF-8?q?Closed=20#111=20=E6=94=AF=E6=8C=81WebIM?= =?UTF-8?q?=E7=9A=84=E9=9B=86=E7=BE=A4=E5=9C=BA=E6=99=AF=E4=B8=8B=E4=BD=BF?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/algorithm/AutomaticServiceDist.java | 10 +- .../cc/app/config/RedisConfigure.java | 37 +- .../handler/apps/agent/AgentController.java | 9 +- .../apps/service/ChatServiceController.java | 760 +++++++++--------- .../cc/app/im/client/NettyClients.java | 283 ++++--- .../cc/app/im/handler/AgentEventHandler.java | 398 +++++---- .../app/im/handler/CalloutEventHandler.java | 2 +- .../cc/app/im/router/MessageRouter.java | 2 +- .../app/im/router/WebIMOutMessageRouter.java | 2 +- .../chatopera/cc/app/im/util/HumanUtils.java | 10 +- .../cc/app/im/util/IMServiceUtils.java | 29 + .../cc/app/model/MessageInContent.java | 315 ++++---- .../cc/app/schedule/CallOutWireTask.java | 2 - .../cc/app/schedule/WebIMAgentDispatcher.java | 104 +++ .../schedule/WebIMOnlineUserDispatcher.java | 102 +++ .../chatopera/cc/app/schedule/WebIMTask.java | 51 +- .../java/com/chatopera/cc/util/Constants.java | 12 + .../src/main/resources/application.properties | 4 +- 18 files changed, 1237 insertions(+), 895 deletions(-) create mode 100644 contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMAgentDispatcher.java create mode 100644 contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMOnlineUserDispatcher.java diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/algorithm/AutomaticServiceDist.java b/contact-center/app/src/main/java/com/chatopera/cc/app/algorithm/AutomaticServiceDist.java index cb69525e..0f66550b 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/algorithm/AutomaticServiceDist.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/algorithm/AutomaticServiceDist.java @@ -214,8 +214,8 @@ public class AutomaticServiceDist { router.handler(agentUser.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), agentUser.getAppid(), outMessage); } } - - NettyClients.getInstance().sendAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser); + // TODO #111 为坐席分配访客 + NettyClients.getInstance().publishAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser); } catch (Exception ex) { ex.printStackTrace(); } @@ -311,7 +311,8 @@ public class AutomaticServiceDist { NettyClients.getInstance().sendCalloutEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.END.toString(), agentUser); } else { if (agentStatus != null) // WebIM 查看用户状态 - NettyClients.getInstance().sendAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.END.toString(), agentUser); + // TODO #111 结束会话 + NettyClients.getInstance().publishAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.END.toString(), agentUser); OutMessageRouter router = null; router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel()); if (router != null) { @@ -500,7 +501,8 @@ public class AutomaticServiceDist { if (agentStatus != null) { agentService = processAgentService(agentStatus, agentUser, orgi); publishMessage(orgi, "invite", "success", agentno); - NettyClients.getInstance().sendAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser); + // TODO #111 为坐席分配邀请的访客 + NettyClients.getInstance().publishAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), agentUser); } else { agentService = allotAgent(agentUser, orgi); } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/config/RedisConfigure.java b/contact-center/app/src/main/java/com/chatopera/cc/app/config/RedisConfigure.java index 4539c90c..599d55ea 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/config/RedisConfigure.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/config/RedisConfigure.java @@ -15,8 +15,10 @@ */ package com.chatopera.cc.app.config; -import com.chatopera.cc.util.Constants; import com.chatopera.cc.app.schedule.CallOutWireTask; +import com.chatopera.cc.app.schedule.WebIMAgentDispatcher; +import com.chatopera.cc.app.schedule.WebIMOnlineUserDispatcher; +import com.chatopera.cc.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,17 +40,25 @@ public class RedisConfigure { @Autowired CallOutWireTask callOutWireTask; + @Autowired + WebIMAgentDispatcher webIMAgentDispatcher; + + @Autowired + WebIMOnlineUserDispatcher webIMOnlineUserDispatcher; + @Bean RedisMessageListenerContainer redisContainer() { final RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(jedisConnectionFactory); - container.addMessageListener(messageListener(), pbxEvents()); + container.addMessageListener(pbxMessageListener(), pbxEvents()); + container.addMessageListener(imAgentDispatchListener(), imAgentEvents()); + container.addMessageListener(imOnlineUserDispatchListener(), imOnlineUserEvents()); container.setTaskExecutor(Executors.newFixedThreadPool(50)); return container; } @Bean - MessageListenerAdapter messageListener() { + MessageListenerAdapter pbxMessageListener() { return new MessageListenerAdapter(callOutWireTask); } @@ -57,4 +67,25 @@ public class RedisConfigure { return new PatternTopic(Constants.FS_CHANNEL_FS_TO_CC); } + @Bean + MessageListenerAdapter imAgentDispatchListener() { + return new MessageListenerAdapter(webIMAgentDispatcher); + } + + @Bean + PatternTopic imAgentEvents() { + return new PatternTopic(Constants.INSTANT_MESSAGING_WEBIM_AGENT_CHANNEL); + } + + @Bean + MessageListenerAdapter imOnlineUserDispatchListener(){ + return new MessageListenerAdapter(webIMOnlineUserDispatcher); + } + + @Bean + PatternTopic imOnlineUserEvents() { + return new PatternTopic(Constants.INSTANT_MESSAGING_WEBIM_ONLINE_USER_CHANNEL); + } + + } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/agent/AgentController.java b/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/agent/AgentController.java index 081a4023..041611d5 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/agent/AgentController.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/agent/AgentController.java @@ -768,8 +768,8 @@ public class AgentController extends Handler { data.setUsername(super.getUser(request).getUsername()); chatMessageRepository.save(data); - - NettyClients.getInstance().sendAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); + // TODO #111 通知文件上传消息 + NettyClients.getInstance().publishAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); } } else { @@ -1068,7 +1068,8 @@ public class AgentController extends Handler { agentService.setAgentno(agentno); agentService.setAgentusername(transAgentStatus.getUsername()); } - NettyClients.getInstance().sendAgentEventMessage(agentno, MainContext.MessageTypeEnum.NEW.toString(), agentUser); + // TODO #111 通知转接消息 + NettyClients.getInstance().publishAgentEventMessage(agentno, MainContext.MessageTypeEnum.NEW.toString(), agentUser); } } else { agentUser = agentUserRepository.findByIdAndOrgi(agentuserid, super.getOrgi(request)); @@ -1317,4 +1318,4 @@ public class AgentController extends Handler { return request(super.createRequestPageTempletResponse("redirect:/agent/index.html")); } -} \ No newline at end of file +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/service/ChatServiceController.java b/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/service/ChatServiceController.java index f641c839..edb6947c 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/service/ChatServiceController.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/handler/apps/service/ChatServiceController.java @@ -16,27 +16,17 @@ */ package com.chatopera.cc.app.handler.apps.service; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.servlet.http.HttpServletRequest; -import javax.validation.Valid; - -import com.chatopera.cc.app.basic.MainContext; -import com.chatopera.cc.util.IP; -import com.chatopera.cc.app.basic.MainUtils; -import com.chatopera.cc.util.Menu; -import com.chatopera.cc.app.im.client.NettyClients; import com.chatopera.cc.app.algorithm.AutomaticServiceDist; +import com.chatopera.cc.app.basic.MainContext; +import com.chatopera.cc.app.basic.MainUtils; import com.chatopera.cc.app.cache.CacheHelper; -import com.chatopera.cc.util.OnlineUserUtils; import com.chatopera.cc.app.handler.Handler; +import com.chatopera.cc.app.im.client.NettyClients; +import com.chatopera.cc.app.model.*; +import com.chatopera.cc.app.persistence.repository.*; +import com.chatopera.cc.util.IP; +import com.chatopera.cc.util.Menu; +import com.chatopera.cc.util.OnlineUserUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -48,400 +38,402 @@ import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; -import com.chatopera.cc.app.persistence.repository.AgentServiceRepository; -import com.chatopera.cc.app.persistence.repository.AgentStatusRepository; -import com.chatopera.cc.app.persistence.repository.AgentUserRepository; -import com.chatopera.cc.app.persistence.repository.LeaveMsgRepository; -import com.chatopera.cc.app.persistence.repository.OrganRepository; -import com.chatopera.cc.app.persistence.repository.OrgiSkillRelRepository; -import com.chatopera.cc.app.persistence.repository.UserRepository; -import com.chatopera.cc.app.model.AgentService; -import com.chatopera.cc.app.model.AgentStatus; -import com.chatopera.cc.app.model.AgentUser; -import com.chatopera.cc.app.model.AiUser; -import com.chatopera.cc.app.model.LeaveMsg; -import com.chatopera.cc.app.model.Organ; -import com.chatopera.cc.app.model.OrgiSkillRel; -import com.chatopera.cc.app.model.User; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; @Controller @RequestMapping("/service") public class ChatServiceController extends Handler { - - @Autowired - private AgentServiceRepository agentServiceRes ; - - @Autowired - private AgentUserRepository agentUserRes ; - - @Autowired - private AgentStatusRepository agentStatusRepository ; - - @Autowired - private AgentUserRepository agentUserRepository ; - - @Autowired - private LeaveMsgRepository leaveMsgRes ; - - @Autowired - private OrganRepository organRes ; - - @Autowired - private OrganRepository organ ; - - @Autowired - private UserRepository user ; - - @Autowired - private UserRepository userRes ; - @Autowired - private OrgiSkillRelRepository orgiSkillRelService; - - @RequestMapping("/history/index") - @Menu(type = "service" , subtype = "history" , admin= true) - public ModelAndView index(ModelMap map , HttpServletRequest request ,final String username,final String channel ,final String servicetype,final String allocation,final String servicetimetype,final String begin,final String end) { - Page page = agentServiceRes.findAll(new Specification(){ - @Override - public Predicate toPredicate(Root root, CriteriaQuery query,CriteriaBuilder cb) { - List list = new ArrayList(); - if(!StringUtils.isBlank(username)) { - list.add(cb.equal(root.get("username").as(String.class), username)) ; - } - if(!StringUtils.isBlank(channel)) { - list.add(cb.equal(root.get("channel").as(String.class), channel)) ; - } - if(!StringUtils.isBlank(servicetype)&&!StringUtils.isBlank(allocation)) { - list.add(cb.equal(root.get(servicetype).as(String.class), allocation)); - } - if(!StringUtils.isBlank(servicetimetype)) { - try { - if(!StringUtils.isBlank(begin) && begin.matches("[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:[\\d]{2}:[\\d]{2}")){ - list.add(cb.greaterThanOrEqualTo(root.get(servicetimetype).as(Date.class), MainUtils.dateFormate.parse(begin))) ; - } - if(!StringUtils.isBlank(end) && end.matches("[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:[\\d]{2}:[\\d]{2}")){ - list.add(cb.lessThanOrEqualTo(root.get(servicetimetype).as(Date.class), MainUtils.dateFormate.parse(end))) ; - } - } catch (ParseException e) { - e.printStackTrace(); - } - - } - Predicate[] p = new Predicate[list.size()]; - return cb.and(list.toArray(p)); - } - },new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")) ; - map.put("agentServiceList", page) ; - map.put("username", username) ; - map.put("channel", channel) ; - map.put("servicetype", servicetype) ; - map.put("servicetimetype", servicetimetype) ; - map.put("allocation", allocation); - map.put("begin", begin) ; - map.put("end", end) ; - map.put("deptlist",organ.findByOrgi(super.getOrgi(request))); - map.put("userlist",user.findByOrgiAndDatastatus(super.getOrgi(request), false)); - + + @Autowired + private AgentServiceRepository agentServiceRes; + + @Autowired + private AgentUserRepository agentUserRes; + + @Autowired + private AgentStatusRepository agentStatusRepository; + + @Autowired + private AgentUserRepository agentUserRepository; + + @Autowired + private LeaveMsgRepository leaveMsgRes; + + @Autowired + private OrganRepository organRes; + + @Autowired + private OrganRepository organ; + + @Autowired + private UserRepository user; + + @Autowired + private UserRepository userRes; + @Autowired + private OrgiSkillRelRepository orgiSkillRelService; + + @RequestMapping("/history/index") + @Menu(type = "service", subtype = "history", admin = true) + public ModelAndView index(ModelMap map, HttpServletRequest request, final String username, final String channel, final String servicetype, final String allocation, final String servicetimetype, final String begin, final String end) { + Page page = agentServiceRes.findAll(new Specification() { + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + List list = new ArrayList(); + if (!StringUtils.isBlank(username)) { + list.add(cb.equal(root.get("username").as(String.class), username)); + } + if (!StringUtils.isBlank(channel)) { + list.add(cb.equal(root.get("channel").as(String.class), channel)); + } + if (!StringUtils.isBlank(servicetype) && !StringUtils.isBlank(allocation)) { + list.add(cb.equal(root.get(servicetype).as(String.class), allocation)); + } + if (!StringUtils.isBlank(servicetimetype)) { + try { + if (!StringUtils.isBlank(begin) && begin.matches("[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:[\\d]{2}:[\\d]{2}")) { + list.add(cb.greaterThanOrEqualTo(root.get(servicetimetype).as(Date.class), MainUtils.dateFormate.parse(begin))); + } + if (!StringUtils.isBlank(end) && end.matches("[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:[\\d]{2}:[\\d]{2}")) { + list.add(cb.lessThanOrEqualTo(root.get(servicetimetype).as(Date.class), MainUtils.dateFormate.parse(end))); + } + } catch (ParseException e) { + e.printStackTrace(); + } + + } + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + }, new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + map.put("agentServiceList", page); + map.put("username", username); + map.put("channel", channel); + map.put("servicetype", servicetype); + map.put("servicetimetype", servicetimetype); + map.put("allocation", allocation); + map.put("begin", begin); + map.put("end", end); + map.put("deptlist", organ.findByOrgi(super.getOrgi(request))); + map.put("userlist", user.findByOrgiAndDatastatus(super.getOrgi(request), false)); + return request(super.createAppsTempletResponse("/apps/service/history/index")); } - - @RequestMapping("/current/index") - @Menu(type = "service" , subtype = "current" , admin= true) - public ModelAndView current(ModelMap map , HttpServletRequest request) { - map.put("agentServiceList", agentServiceRes.findByOrgiAndStatus(super.getOrgi(request), MainContext.AgentUserStatusEnum.INSERVICE.toString() ,new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime"))) ; + + @RequestMapping("/current/index") + @Menu(type = "service", subtype = "current", admin = true) + public ModelAndView current(ModelMap map, HttpServletRequest request) { + map.put("agentServiceList", agentServiceRes.findByOrgiAndStatus(super.getOrgi(request), MainContext.AgentUserStatusEnum.INSERVICE.toString(), new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime"))); return request(super.createAppsTempletResponse("/apps/service/current/index")); } - - @RequestMapping("/current/trans") - @Menu(type = "service" , subtype = "current" , admin= true) - public ModelAndView trans(ModelMap map , HttpServletRequest request , @Valid String id) { - if(!StringUtils.isBlank(id)){ - AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - List skillList = OnlineUserUtils.organ(super.getOrgi(request),true) ; - String currentOrgan = super.getUser(request).getOrgan(); - if(StringUtils.isBlank(currentOrgan)) { - if(!skillList.isEmpty()) { - currentOrgan = skillList.get(0).getId(); - } - } - List agentStatusList = AutomaticServiceDist.getAgentStatus(null , super.getOrgi(request)); - List usersids = new ArrayList(); - if(!agentStatusList.isEmpty()) { - for(AgentStatus agentStatus:agentStatusList) { - if(agentStatus!=null){ - usersids.add(agentStatus.getAgentno()) ; - } - } - - } - List userList = userRes.findAll(usersids); - for(User user : userList){ - user.setAgentStatus((AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request))); - } - map.addAttribute("userList", userList) ; - map.addAttribute("userid", agentService.getUserid()) ; - map.addAttribute("agentserviceid", agentService.getId()) ; - map.addAttribute("agentuserid", agentService.getAgentuserid()) ; - map.addAttribute("agentservice", agentService) ; - map.addAttribute("skillList", skillList) ; - map.addAttribute("currentorgan", currentOrgan) ; - } - - return request(super.createRequestPageTempletResponse("/apps/service/current/transfer")); + + @RequestMapping("/current/trans") + @Menu(type = "service", subtype = "current", admin = true) + public ModelAndView trans(ModelMap map, HttpServletRequest request, @Valid String id) { + if (!StringUtils.isBlank(id)) { + AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)); + List skillList = OnlineUserUtils.organ(super.getOrgi(request), true); + String currentOrgan = super.getUser(request).getOrgan(); + if (StringUtils.isBlank(currentOrgan)) { + if (!skillList.isEmpty()) { + currentOrgan = skillList.get(0).getId(); + } + } + List agentStatusList = AutomaticServiceDist.getAgentStatus(null, super.getOrgi(request)); + List usersids = new ArrayList(); + if (!agentStatusList.isEmpty()) { + for (AgentStatus agentStatus : agentStatusList) { + if (agentStatus != null) { + usersids.add(agentStatus.getAgentno()); + } + } + + } + List userList = userRes.findAll(usersids); + for (User user : userList) { + user.setAgentStatus((AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request))); + } + map.addAttribute("userList", userList); + map.addAttribute("userid", agentService.getUserid()); + map.addAttribute("agentserviceid", agentService.getId()); + map.addAttribute("agentuserid", agentService.getAgentuserid()); + map.addAttribute("agentservice", agentService); + map.addAttribute("skillList", skillList); + map.addAttribute("currentorgan", currentOrgan); + } + + return request(super.createRequestPageTempletResponse("/apps/service/current/transfer")); } - - @RequestMapping(value="/transfer/save") - @Menu(type = "apps", subtype = "transfersave") - public ModelAndView transfersave(ModelMap map , HttpServletRequest request , @Valid String id , @Valid String agentno , @Valid String memo){ - if(!StringUtils.isBlank(id)){ - AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(agentService.getUserid(), super.getOrgi(request)) ; - if(agentUser != null){ - agentUser.setAgentno(agentno); - CacheHelper.getAgentUserCacheBean().put(agentService.getUserid() , agentUser , super.getOrgi(request)) ; - agentUserRepository.save(agentUser) ; - if(MainContext.AgentUserStatusEnum.INSERVICE.toString().equals(agentUser.getStatus())){ //转接 , 发送消息给 目标坐席 - AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(super.getUser(request).getId(), super.getOrgi(request)) ; - - if(agentStatus!=null){ - AutomaticServiceDist.updateAgentStatus(agentStatus, agentUser, super.getOrgi(request), false); - } - - AgentStatus transAgentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentno, super.getOrgi(request)) ; - if(transAgentStatus!=null){ - AutomaticServiceDist.updateAgentStatus(transAgentStatus, agentUser, super.getOrgi(request), true); - agentService.setAgentno(agentno); - agentService.setAgentusername(transAgentStatus.getUsername()); - } - NettyClients.getInstance().sendAgentEventMessage(agentno, MainContext.MessageTypeEnum.NEW.toString(), agentUser); - } - }else{ - agentUser = agentUserRepository.findByIdAndOrgi(agentService.getAgentuserid(), super.getOrgi(request)); - if(agentUser!=null){ - agentUser.setAgentno(agentno); - agentUserRepository.save(agentUser) ; - } - } - - if(agentService!=null){ - agentService.setAgentno(agentno); - if(!StringUtils.isBlank(memo)){ - agentService.setTransmemo(memo); - } - agentService.setTrans(true); - agentService.setTranstime(new Date()); - agentServiceRes.save(agentService) ; - } - } - - return request(super.createRequestPageTempletResponse("redirect:/service/current/index.html")) ; - } - - @RequestMapping("/current/end") - @Menu(type = "service" , subtype = "current" , admin= true) - public ModelAndView end(ModelMap map , HttpServletRequest request , @Valid String id) throws Exception { - if(!StringUtils.isBlank(id)){ - AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - if(agentService!=null){ - User user = super.getUser(request); - AgentUser agentUser = agentUserRepository.findByIdAndOrgi(agentService.getAgentuserid(), super.getOrgi(request)); - if(agentUser!=null){ - AutomaticServiceDist.deleteAgentUser(agentUser, user.getOrgi()); - } - agentService.setStatus(MainContext.AgentUserStatusEnum.END.toString()); - agentServiceRes.save(agentService) ; - } - } + + @RequestMapping(value = "/transfer/save") + @Menu(type = "apps", subtype = "transfersave") + public ModelAndView transfersave(ModelMap map, HttpServletRequest request, @Valid String id, @Valid String agentno, @Valid String memo) { + if (!StringUtils.isBlank(id)) { + AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)); + AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(agentService.getUserid(), super.getOrgi(request)); + if (agentUser != null) { + agentUser.setAgentno(agentno); + CacheHelper.getAgentUserCacheBean().put(agentService.getUserid(), agentUser, super.getOrgi(request)); + agentUserRepository.save(agentUser); + if (MainContext.AgentUserStatusEnum.INSERVICE.toString().equals(agentUser.getStatus())) { //转接 , 发送消息给 目标坐席 + AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(super.getUser(request).getId(), super.getOrgi(request)); + + if (agentStatus != null) { + AutomaticServiceDist.updateAgentStatus(agentStatus, agentUser, super.getOrgi(request), false); + } + + AgentStatus transAgentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentno, super.getOrgi(request)); + if (transAgentStatus != null) { + AutomaticServiceDist.updateAgentStatus(transAgentStatus, agentUser, super.getOrgi(request), true); + agentService.setAgentno(agentno); + agentService.setAgentusername(transAgentStatus.getUsername()); + } + // TODO #111 通知转接消息 + NettyClients.getInstance().publishAgentEventMessage(agentno, MainContext.MessageTypeEnum.NEW.toString(), agentUser); + } + } else { + agentUser = agentUserRepository.findByIdAndOrgi(agentService.getAgentuserid(), super.getOrgi(request)); + if (agentUser != null) { + agentUser.setAgentno(agentno); + agentUserRepository.save(agentUser); + } + } + + if (agentService != null) { + agentService.setAgentno(agentno); + if (!StringUtils.isBlank(memo)) { + agentService.setTransmemo(memo); + } + agentService.setTrans(true); + agentService.setTranstime(new Date()); + agentServiceRes.save(agentService); + } + } + return request(super.createRequestPageTempletResponse("redirect:/service/current/index.html")); } - - @RequestMapping("/current/invite") - @Menu(type = "service" , subtype = "current" , admin= true) - public ModelAndView currentinvite(ModelMap map , HttpServletRequest request , @Valid String id) throws Exception { - if(!StringUtils.isBlank(id)){ - AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - if(agentService!=null){ - User user = super.getUser(request); - if(StringUtils.isBlank(agentService.getAgentno())) { - AiUser aiUser = (AiUser) CacheHelper.getOnlineUserCacheBean().getCacheObject(agentService.getSessionid(), agentService.getOrgi()) ; - IP ipdata = null ; - if(aiUser != null ) { - ipdata = aiUser.getIpdata() ; - OnlineUserUtils.newRequestMessage(aiUser.getUserid() , aiUser.getUsername(), user.getOrgi(), agentService.getSessionid(), agentService.getAppid() , agentService.getIpaddr(), agentService.getOsname() , agentService.getBrowser() , "" , ipdata!=null ? ipdata : null , agentService.getChannel() , user.getOrgan(), user.getId() , null ,null, agentService.getContactsid(), MainContext.ChatInitiatorType.AGENT.toString() , aiUser.getContextid()) ; - } - } - } - } + + @RequestMapping("/current/end") + @Menu(type = "service", subtype = "current", admin = true) + public ModelAndView end(ModelMap map, HttpServletRequest request, @Valid String id) throws Exception { + if (!StringUtils.isBlank(id)) { + AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)); + if (agentService != null) { + User user = super.getUser(request); + AgentUser agentUser = agentUserRepository.findByIdAndOrgi(agentService.getAgentuserid(), super.getOrgi(request)); + if (agentUser != null) { + AutomaticServiceDist.deleteAgentUser(agentUser, user.getOrgi()); + } + agentService.setStatus(MainContext.AgentUserStatusEnum.END.toString()); + agentServiceRes.save(agentService); + } + } return request(super.createRequestPageTempletResponse("redirect:/service/current/index.html")); } - - - @RequestMapping("/quene/index") - @Menu(type = "service" , subtype = "filter" , admin= true) - public ModelAndView quene(ModelMap map , HttpServletRequest request) { - Page agentUserList = agentUserRes.findByOrgiAndStatus(super.getOrgi(request), MainContext.AgentUserStatusEnum.INQUENE.toString() ,new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")) ; - List skillList = new ArrayList(); - for(AgentUser agentUser : agentUserList.getContent()){ - agentUser.setWaittingtime((int) (System.currentTimeMillis() - agentUser.getCreatetime().getTime())); - if(!StringUtils.isBlank(agentUser.getSkill())){ - skillList.add(agentUser.getSkill()) ; - } - } - if(skillList.size() > 0){ - List organList = organRes.findAll(skillList) ; - for(AgentUser agentUser : agentUserList.getContent()){ - if(!StringUtils.isBlank(agentUser.getSkill())){ - for(Organ organ : organList){ - if(agentUser.getSkill().equals(organ.getId())){ - agentUser.setSkillname(organ.getName()); - break ; - } - } - } - } - } - map.put("agentUserList", agentUserList) ; + + @RequestMapping("/current/invite") + @Menu(type = "service", subtype = "current", admin = true) + public ModelAndView currentinvite(ModelMap map, HttpServletRequest request, @Valid String id) throws Exception { + if (!StringUtils.isBlank(id)) { + AgentService agentService = agentServiceRes.findByIdAndOrgi(id, super.getOrgi(request)); + if (agentService != null) { + User user = super.getUser(request); + if (StringUtils.isBlank(agentService.getAgentno())) { + AiUser aiUser = (AiUser) CacheHelper.getOnlineUserCacheBean().getCacheObject(agentService.getSessionid(), agentService.getOrgi()); + IP ipdata = null; + if (aiUser != null) { + ipdata = aiUser.getIpdata(); + OnlineUserUtils.newRequestMessage(aiUser.getUserid(), aiUser.getUsername(), user.getOrgi(), agentService.getSessionid(), agentService.getAppid(), agentService.getIpaddr(), agentService.getOsname(), agentService.getBrowser(), "", ipdata != null ? ipdata : null, agentService.getChannel(), user.getOrgan(), user.getId(), null, null, agentService.getContactsid(), MainContext.ChatInitiatorType.AGENT.toString(), aiUser.getContextid()); + } + } + } + } + return request(super.createRequestPageTempletResponse("redirect:/service/current/index.html")); + } + + + @RequestMapping("/quene/index") + @Menu(type = "service", subtype = "filter", admin = true) + public ModelAndView quene(ModelMap map, HttpServletRequest request) { + Page agentUserList = agentUserRes.findByOrgiAndStatus(super.getOrgi(request), MainContext.AgentUserStatusEnum.INQUENE.toString(), new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + List skillList = new ArrayList(); + for (AgentUser agentUser : agentUserList.getContent()) { + agentUser.setWaittingtime((int) (System.currentTimeMillis() - agentUser.getCreatetime().getTime())); + if (!StringUtils.isBlank(agentUser.getSkill())) { + skillList.add(agentUser.getSkill()); + } + } + if (skillList.size() > 0) { + List organList = organRes.findAll(skillList); + for (AgentUser agentUser : agentUserList.getContent()) { + if (!StringUtils.isBlank(agentUser.getSkill())) { + for (Organ organ : organList) { + if (agentUser.getSkill().equals(organ.getId())) { + agentUser.setSkillname(organ.getName()); + break; + } + } + } + } + } + map.put("agentUserList", agentUserList); return request(super.createAppsTempletResponse("/apps/service/quene/index")); } - - @RequestMapping("/quene/clean") - @Menu(type = "service" , subtype = "queneclean" , admin= true) - public ModelAndView clean(ModelMap map , HttpServletRequest request ,@Valid String id) { - AgentUser agentUser = agentUserRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - if(agentUser!=null && agentUser.getStatus().equals(MainContext.AgentUserStatusEnum.INQUENE.toString())){ - agentUser.setAgent(null); - agentUser.setSkill(null); - agentUserRes.save(agentUser) ; - CacheHelper.getAgentUserCacheBean().put(agentUser.getUserid(), agentUser, super.getOrgi(request)); - AutomaticServiceDist.allotAgent(agentUser, super.getOrgi(request)) ; - } + + @RequestMapping("/quene/clean") + @Menu(type = "service", subtype = "queneclean", admin = true) + public ModelAndView clean(ModelMap map, HttpServletRequest request, @Valid String id) { + AgentUser agentUser = agentUserRes.findByIdAndOrgi(id, super.getOrgi(request)); + if (agentUser != null && agentUser.getStatus().equals(MainContext.AgentUserStatusEnum.INQUENE.toString())) { + agentUser.setAgent(null); + agentUser.setSkill(null); + agentUserRes.save(agentUser); + CacheHelper.getAgentUserCacheBean().put(agentUser.getUserid(), agentUser, super.getOrgi(request)); + AutomaticServiceDist.allotAgent(agentUser, super.getOrgi(request)); + } return request(super.createRequestPageTempletResponse("redirect:/service/quene/index.html")); } - - @RequestMapping("/quene/invite") - @Menu(type = "service" , subtype = "invite" , admin= true) - public ModelAndView invite(ModelMap map , HttpServletRequest request ,@Valid String id) throws Exception { - AgentUser agentUser = agentUserRes.findByIdAndOrgi(id, super.getOrgi(request)) ; - if(agentUser!=null && agentUser.getStatus().equals(MainContext.AgentUserStatusEnum.INQUENE.toString())){ - AutomaticServiceDist.allotAgentForInvite(super.getUser(request).getId() , agentUser, super.getOrgi(request)) ; - } + + @RequestMapping("/quene/invite") + @Menu(type = "service", subtype = "invite", admin = true) + public ModelAndView invite(ModelMap map, HttpServletRequest request, @Valid String id) throws Exception { + AgentUser agentUser = agentUserRes.findByIdAndOrgi(id, super.getOrgi(request)); + if (agentUser != null && agentUser.getStatus().equals(MainContext.AgentUserStatusEnum.INQUENE.toString())) { + AutomaticServiceDist.allotAgentForInvite(super.getUser(request).getId(), agentUser, super.getOrgi(request)); + } return request(super.createRequestPageTempletResponse("redirect:/service/quene/index.html")); } - - @RequestMapping("/agent/index") - @Menu(type = "service" , subtype = "onlineagent" , admin= true) - public ModelAndView agent(ModelMap map , HttpServletRequest request) { - List agentStatusList = agentStatusRepository.findByOrgi(super.getOrgi(request)) ; - for(int i=0 ; i skillList = new ArrayList(); - for(AgentStatus agentStatus : agentStatusList){ - if(!StringUtils.isBlank(agentStatus.getSkill())){ - skillList.add(agentStatus.getSkill()) ; - } - } - if(skillList.size() > 0){ - List organList = organRes.findAll(skillList) ; - for(AgentStatus agentStatus : agentStatusList){ - if(!StringUtils.isBlank(agentStatus.getSkill())){ - for(Organ organ : organList){ - if(agentStatus.getSkill().equals(organ.getId())){ - agentStatus.setSkillname(organ.getName()); - break ; - } - } - } - } - } - map.put("agentStatusList", agentStatusList) ; + + @RequestMapping("/agent/index") + @Menu(type = "service", subtype = "onlineagent", admin = true) + public ModelAndView agent(ModelMap map, HttpServletRequest request) { + List agentStatusList = agentStatusRepository.findByOrgi(super.getOrgi(request)); + for (int i = 0; i < agentStatusList.size(); ) { + AgentStatus agentStatus = agentStatusList.get(i); + if (CacheHelper.getAgentStatusCacheBean().getCacheObject(agentStatus.getAgentno(), super.getOrgi(request)) == null) { + agentStatusRepository.delete(agentStatus); + agentStatusList.remove(i); + continue; + } else { + AgentStatus temp = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(agentStatus.getAgentno(), super.getOrgi(request)); + agentStatusList.set(i, temp); + } + i++; + } + List skillList = new ArrayList(); + for (AgentStatus agentStatus : agentStatusList) { + if (!StringUtils.isBlank(agentStatus.getSkill())) { + skillList.add(agentStatus.getSkill()); + } + } + if (skillList.size() > 0) { + List organList = organRes.findAll(skillList); + for (AgentStatus agentStatus : agentStatusList) { + if (!StringUtils.isBlank(agentStatus.getSkill())) { + for (Organ organ : organList) { + if (agentStatus.getSkill().equals(organ.getId())) { + agentStatus.setSkillname(organ.getName()); + break; + } + } + } + } + } + map.put("agentStatusList", agentStatusList); return request(super.createAppsTempletResponse("/apps/service/agent/index")); } - - @RequestMapping("/agent/offline") - @Menu(type = "service" , subtype = "offline" , admin= true) - public ModelAndView offline(ModelMap map , HttpServletRequest request , @Valid String id) { - - AgentStatus agentStatus = agentStatusRepository.findByIdAndOrgi(id, super.getOrgi(request)); - if(agentStatus!=null){ - agentStatusRepository.delete(agentStatus); - } - CacheHelper.getAgentStatusCacheBean().delete(agentStatus.getAgentno(), super.getOrgi(request));; - AutomaticServiceDist.publishMessage(super.getOrgi(request) , "agent" , "offline" , super.getUser(request).getId()); - - + + @RequestMapping("/agent/offline") + @Menu(type = "service", subtype = "offline", admin = true) + public ModelAndView offline(ModelMap map, HttpServletRequest request, @Valid String id) { + + AgentStatus agentStatus = agentStatusRepository.findByIdAndOrgi(id, super.getOrgi(request)); + if (agentStatus != null) { + agentStatusRepository.delete(agentStatus); + } + CacheHelper.getAgentStatusCacheBean().delete(agentStatus.getAgentno(), super.getOrgi(request)); + ; + AutomaticServiceDist.publishMessage(super.getOrgi(request), "agent", "offline", super.getUser(request).getId()); + + return request(super.createRequestPageTempletResponse("redirect:/service/agent/index.html")); } - /** - * 非管理员坐席 - * @param map - * @param request - * @return - */ - @RequestMapping("/user/index") - @Menu(type = "service" , subtype = "userlist" , admin= true) - public ModelAndView user(ModelMap map , HttpServletRequest request) { - Page userList = null; - if(super.isTenantshare()) { - List organIdList = new ArrayList<>(); - List orgiSkillRelList = orgiSkillRelService.findByOrgi(super.getOrgi(request)) ; - if(!orgiSkillRelList.isEmpty()) { - for(OrgiSkillRel rel:orgiSkillRelList) { - organIdList.add(rel.getSkillid()); - } - } - userList=userRes.findByOrganInAndAgentAndDatastatus(organIdList,true,false,new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")); - }else { - userList=userRes.findByOrgiAndAgentAndDatastatus(super.getOrgi(request), true,false, new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")) ; - } - for(User user : userList.getContent()){ - if(CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request))!=null){ - user.setOnline(true); - } - } - map.put("userList", userList) ; + + /** + * 非管理员坐席 + * + * @param map + * @param request + * @return + */ + @RequestMapping("/user/index") + @Menu(type = "service", subtype = "userlist", admin = true) + public ModelAndView user(ModelMap map, HttpServletRequest request) { + Page userList = null; + if (super.isTenantshare()) { + List organIdList = new ArrayList<>(); + List orgiSkillRelList = orgiSkillRelService.findByOrgi(super.getOrgi(request)); + if (!orgiSkillRelList.isEmpty()) { + for (OrgiSkillRel rel : orgiSkillRelList) { + organIdList.add(rel.getSkillid()); + } + } + userList = userRes.findByOrganInAndAgentAndDatastatus(organIdList, true, false, new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + } else { + userList = userRes.findByOrgiAndAgentAndDatastatus(super.getOrgi(request), true, false, new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + } + for (User user : userList.getContent()) { + if (CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request)) != null) { + user.setOnline(true); + } + } + map.put("userList", userList); return request(super.createAppsTempletResponse("/apps/service/user/index")); } - /** - * 管理员坐席 - * @param map - * @param request - * @return - */ - @RequestMapping("/adminagent/index") - @Menu(type = "service" , subtype = "adminagentlist" , admin= true) - public ModelAndView adminagent(ModelMap map , HttpServletRequest request) { - Page userList = userRes.findByOrgidAndAgentAndDatastatusAndUsertype(super.getOrgid(request), true,false,"0", new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")) ; - for(User user : userList.getContent()){ - if(CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request))!=null){ - user.setOnline(true); - } - } - map.put("userList", userList) ; + + /** + * 管理员坐席 + * + * @param map + * @param request + * @return + */ + @RequestMapping("/adminagent/index") + @Menu(type = "service", subtype = "adminagentlist", admin = true) + public ModelAndView adminagent(ModelMap map, HttpServletRequest request) { + Page userList = userRes.findByOrgidAndAgentAndDatastatusAndUsertype(super.getOrgid(request), true, false, "0", new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + for (User user : userList.getContent()) { + if (CacheHelper.getAgentStatusCacheBean().getCacheObject(user.getId(), super.getOrgi(request)) != null) { + user.setOnline(true); + } + } + map.put("userList", userList); return request(super.createAppsTempletResponse("/apps/service/adminagent/index")); } - @RequestMapping("/leavemsg/index") - @Menu(type = "service" , subtype = "leavemsg" , admin= true) - public ModelAndView leavemsg(ModelMap map , HttpServletRequest request) { - Page leaveMsgList = leaveMsgRes.findByOrgi(super.getOrgi(request),new PageRequest(super.getP(request), super.getPs(request), Direction.DESC , "createtime")) ; - map.put("leaveMsgList", leaveMsgList) ; + + @RequestMapping("/leavemsg/index") + @Menu(type = "service", subtype = "leavemsg", admin = true) + public ModelAndView leavemsg(ModelMap map, HttpServletRequest request) { + Page leaveMsgList = leaveMsgRes.findByOrgi(super.getOrgi(request), new PageRequest(super.getP(request), super.getPs(request), Direction.DESC, "createtime")); + map.put("leaveMsgList", leaveMsgList); return request(super.createAppsTempletResponse("/apps/service/leavemsg/index")); } - - @RequestMapping("/leavemsg/delete") - @Menu(type = "service" , subtype = "leavemsg" , admin= true) - public ModelAndView leavemsg(ModelMap map , HttpServletRequest request , @Valid String id) { - if(!StringUtils.isBlank(id)){ - leaveMsgRes.delete(id); - } - return request(super.createRequestPageTempletResponse("redirect:/service/leavemsg/index.html")); + + @RequestMapping("/leavemsg/delete") + @Menu(type = "service", subtype = "leavemsg", admin = true) + public ModelAndView leavemsg(ModelMap map, HttpServletRequest request, @Valid String id) { + if (!StringUtils.isBlank(id)) { + leaveMsgRes.delete(id); + } + return request(super.createRequestPageTempletResponse("redirect:/service/leavemsg/index.html")); } } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/client/NettyClients.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/client/NettyClients.java index b45af141..ca9a78e2 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/client/NettyClients.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/client/NettyClients.java @@ -16,110 +16,190 @@ */ package com.chatopera.cc.app.im.client; -import java.util.List; - -import com.corundumstudio.socketio.SocketIOClient; +import com.chatopera.cc.app.basic.MainContext; import com.chatopera.cc.app.basic.MainUtils; +import com.chatopera.cc.app.im.util.IMServiceUtils; +import com.chatopera.cc.app.schedule.WebIMAgentDispatcher; +import com.chatopera.cc.app.schedule.WebIMOnlineUserDispatcher; +import com.corundumstudio.socketio.SocketIOClient; +import com.google.gson.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.Serializable; +import java.util.List; + public class NettyClients { private final Logger logger = LoggerFactory.getLogger(NettyClient.class); - private static NettyClients clients = new NettyClients(); - - private NettyIMClient imClients = new NettyIMClient(); - private NettyAgentClient agentClients = new NettyAgentClient(); - private NettyIMClient entIMClients = new NettyIMClient(); - private NettyCallCenterClient callCenterClients = new NettyCallCenterClient(); - private NettyCalloutClient calloutClients = new NettyCalloutClient(); - private NettyChatbotClient chatbotClients = new NettyChatbotClient(); - - public int size(){ - return imClients.size(); - } - - public static NettyClients getInstance(){ - return clients ; - } - - public NettyCallCenterClient getCallCenterClients(){ - return this.callCenterClients ; - } + private static NettyClients clients = new NettyClients(); - public void setImClients(NettyIMClient imClients) { - this.imClients = imClients; - } - public void putIMEventClient(String id , SocketIOClient userClient){ - imClients.putClient(id, userClient); - } - - public void closeIMEventClient(String id , String sessionid, String orgi){ - List userClients = imClients.getClients(id) ; - for(SocketIOClient userClient : userClients){ - if(MainUtils.getContextID(userClient.getSessionId().toString()).equals(sessionid)){ - userClient.disconnect(); - } - } - } - public void removeIMEventClient(String id , String sessionid){ - imClients.removeClient(id, sessionid); - } - public void sendIMEventMessage(String id , String event , Object data){ - List userClients = imClients.getClients(id) ; - for(SocketIOClient userClient : userClients){ - userClient.sendEvent(event, data); - } - } - - public void setAgentClients(NettyAgentClient agentClients) { - this.agentClients = agentClients; - } - public void putAgentEventClient(String id , SocketIOClient agentClient){ - agentClients.putClient(id, agentClient); - } - public void removeAgentEventClient(String id , String sessionid){ - agentClients.removeClient(id, sessionid); - } - public void sendAgentEventMessage(String id , String event , Object data){ - List agents = agentClients.getClients(id) ; - for(SocketIOClient agentClient : agents){ - agentClient.sendEvent(event, data); - } - } - - public void setEntImClients(NettyIMClient entIMClients) { - this.entIMClients = entIMClients; - } - public void putEntIMEventClient(String id , SocketIOClient userClient){ - entIMClients.putClient(id, userClient); - } - public void removeEntIMEventClient(String id , String sessionid){ - entIMClients.removeClient(id, sessionid); - } - public void sendEntIMEventMessage(String id , String event , Object data){ - List entims = entIMClients.getClients(id) ; - for(SocketIOClient userClient : entims){ - userClient.sendEvent(event, data); - } - } - public int getEntIMClientsNum(String user){ - return entIMClients.getClients(user)!=null ? entIMClients.getClients(user).size() : 0; - } - - public void sendCallCenterMessage(String id , String event , Object data){ - List ccClients = callCenterClients.getClients(id) ; - for(SocketIOClient ccClient : ccClients){ - ccClient.sendEvent(event, data); - } - } + private NettyIMClient imClients = new NettyIMClient(); + private NettyAgentClient agentClients = new NettyAgentClient(); + private NettyIMClient entIMClients = new NettyIMClient(); + private NettyCallCenterClient callCenterClients = new NettyCallCenterClient(); + private NettyCalloutClient calloutClients = new NettyCalloutClient(); + private NettyChatbotClient chatbotClients = new NettyChatbotClient(); + + public int size() { + return imClients.size(); + } + + public static NettyClients getInstance() { + return clients; + } + + public NettyCallCenterClient getCallCenterClients() { + return this.callCenterClients; + } + + /** + * 访客连接 + */ + public void setImClients(NettyIMClient imClients) { + this.imClients = imClients; + } + + public void putIMEventClient(String id, SocketIOClient userClient) { + imClients.putClient(id, userClient); + } + + public void closeIMEventClient(String id, String sessionid, String orgi) { + List userClients = imClients.getClients(id); + for (SocketIOClient userClient : userClients) { + if (MainUtils.getContextID(userClient.getSessionId().toString()).equals(sessionid)) { + userClient.disconnect(); + } + } + } + + public void removeIMEventClient(String id, String sessionid) { + imClients.removeClient(id, sessionid); + } + + public void publishIMEventMessage(final String id, final String event, Serializable data) { + // 检测client是否在这台机器上 + if (!sendIMEventMessage(id, event, data)) { + try { + JsonObject payload = new JsonObject(); + payload.addProperty("event", event); + payload.addProperty("id", id); + payload.addProperty("data", IMServiceUtils.serialize(data)); + MainContext.getContext().getBean(WebIMOnlineUserDispatcher.class).publish(payload); + } catch (IOException e) { + logger.error("publishIMEventMessage", e); + } + } + } + + public boolean sendIMEventMessage(final String id, final String event, Object data) { + List userClients = imClients.getClients(id); + for (SocketIOClient userClient : userClients) { + userClient.sendEvent(event, data); + } + return userClients.size() > 0; + } + + /** + * 坐席连接 + */ + public void setAgentClients(NettyAgentClient agentClients) { + this.agentClients = agentClients; + } + + public void putAgentEventClient(String id, SocketIOClient agentClient) { + agentClients.putClient(id, agentClient); + } + + public void removeAgentEventClient(String id, String sessionid) { + agentClients.removeClient(id, sessionid); + } + + // publish to Redis + public void publishAgentEventMessage(String id, String event, Serializable data) { + // 检测client是否在这台机器上 + if (!sendAgentEventMessage(id, event, data)) { + try { + JsonObject payload = new JsonObject(); + payload.addProperty("event", event); + payload.addProperty("id", id); + payload.addProperty("data", IMServiceUtils.serialize(data)); + MainContext.getContext().getBean(WebIMAgentDispatcher.class).publish(payload); + } catch (IOException e) { + logger.error("publishAgentEventMessage", e); + } + } + } + + // 向坐席发送消息 + public boolean sendAgentEventMessage(String id, String event, Object data) { + List agents = agentClients.getClients(id); + for (SocketIOClient agentClient : agents) { + agentClient.sendEvent(event, data); + } + + return agents.size() > 0; + } + + /** + * 企业聊天 + */ + public void setEntImClients(NettyIMClient entIMClients) { + this.entIMClients = entIMClients; + } + + public void putEntIMEventClient(String id, SocketIOClient userClient) { + entIMClients.putClient(id, userClient); + } + + public void removeEntIMEventClient(String id, String sessionid) { + entIMClients.removeClient(id, sessionid); + } + + public void sendEntIMEventMessage(String id, String event, Object data) { + List entims = entIMClients.getClients(id); + for (SocketIOClient userClient : entims) { + userClient.sendEvent(event, data); + } + } + + public int getEntIMClientsNum(String user) { + return entIMClients.getClients(user) != null ? entIMClients.getClients(user).size() : 0; + } + + public void sendCallCenterMessage(String id, String event, Object data) { + List ccClients = callCenterClients.getClients(id); + for (SocketIOClient ccClient : ccClients) { + ccClient.sendEvent(event, data); + } + } + + /** + * Callout Event Server Methods. + */ + public void putCalloutEventClient(String id, SocketIOClient client) { + calloutClients.putClient(id, client); + } + + public void removeCalloutEventClient(String id, String sessionId) { + calloutClients.removeClient(id, sessionId); + } + + public void sendCalloutEventMessage(String id, String event, Object data) { + List _clients = calloutClients.getClients(id); + logger.info("sendCalloutEventMessage get clients size {}", _clients.size()); + for (SocketIOClient c : _clients) { + c.sendEvent(event, data); + } + } /** * Chatbot Event Server Methods. */ - public void putChatbotEventClient(String id, SocketIOClient client){ + public void putChatbotEventClient(String id, SocketIOClient client) { chatbotClients.putClient(id, client); } @@ -127,29 +207,10 @@ public class NettyClients { chatbotClients.removeClient(id, sessionId); } - public void sendChatbotEventMessage(String id, String event, Object data){ + public void sendChatbotEventMessage(String id, String event, Object data) { List _clients = chatbotClients.getClients(id); logger.info("sendChatbotEventMessage get clients size {}", _clients.size()); - for(SocketIOClient c: _clients){ - c.sendEvent(event, data); - } - } - - /** - * Callout Event Server Methods. - */ - public void putCalloutEventClient(String id, SocketIOClient client){ - calloutClients.putClient(id, client); - } - - public void removeCalloutEventClient(String id, String sessionId) { - calloutClients.removeClient(id, sessionId); - } - - public void sendCalloutEventMessage(String id, String event, Object data){ - List _clients = calloutClients.getClients(id); - logger.info("sendCalloutEventMessage get clients size {}", _clients.size()); - for(SocketIOClient c: _clients){ + for (SocketIOClient c : _clients) { c.sendEvent(event, data); } } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/AgentEventHandler.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/AgentEventHandler.java index f18ed5c5..903f65c3 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/AgentEventHandler.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/AgentEventHandler.java @@ -16,228 +16,212 @@ */ package com.chatopera.cc.app.im.handler; -import java.net.InetSocketAddress; -import java.util.Date; -import java.util.List; - import com.chatopera.cc.app.algorithm.AutomaticServiceDist; import com.chatopera.cc.app.basic.MainContext; import com.chatopera.cc.app.basic.MainUtils; -import com.chatopera.cc.app.im.client.NettyClients; import com.chatopera.cc.app.cache.CacheHelper; -import com.chatopera.cc.app.im.router.OutMessageRouter; +import com.chatopera.cc.app.im.client.NettyClients; import com.chatopera.cc.app.im.message.AgentServiceMessage; import com.chatopera.cc.app.im.message.AgentStatusMessage; import com.chatopera.cc.app.im.message.ChatMessage; -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; - +import com.chatopera.cc.app.im.router.OutMessageRouter; +import com.chatopera.cc.app.model.*; +import com.chatopera.cc.app.persistence.repository.*; import com.corundumstudio.socketio.AckRequest; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.annotation.OnConnect; import com.corundumstudio.socketio.annotation.OnDisconnect; import com.corundumstudio.socketio.annotation.OnEvent; -import com.chatopera.cc.app.persistence.repository.AgentStatusRepository; -import com.chatopera.cc.app.persistence.repository.AgentUserRepository; -import com.chatopera.cc.app.persistence.repository.AgentUserTaskRepository; -import com.chatopera.cc.app.persistence.repository.ChatMessageRepository; -import com.chatopera.cc.app.persistence.repository.WorkSessionRepository; -import com.chatopera.cc.app.model.AgentStatus; -import com.chatopera.cc.app.model.AgentUser; -import com.chatopera.cc.app.model.AgentUserTask; -import com.chatopera.cc.app.model.MessageOutContent; -import com.chatopera.cc.app.model.WorkSession; - -public class AgentEventHandler -{ - protected SocketIOServer server; - - @Autowired - public AgentEventHandler(SocketIOServer server) - { - this.server = server ; - } - - @OnConnect - public void onConnect(SocketIOClient client) - { - String user = client.getHandshakeData().getSingleUrlParam("userid") ; - String orgi = client.getHandshakeData().getSingleUrlParam("orgi") ; - String session = client.getHandshakeData().getSingleUrlParam("session") ; - String admin = client.getHandshakeData().getSingleUrlParam("admin") ; - if(!StringUtils.isBlank(user) && !StringUtils.isBlank(user)){ - client.set("agentno", user); - AgentStatusRepository agentStatusRepository = MainContext.getContext().getBean(AgentStatusRepository.class) ; - List agentStatusList = agentStatusRepository.findByAgentnoAndOrgi(user , orgi); - if(agentStatusList.size() > 0){ - AgentStatus agentStatus = agentStatusList.get(0) ; - agentStatus.setUpdatetime(new Date()); - agentStatusRepository.save(agentStatus); - if(CacheHelper.getAgentStatusCacheBean().getCacheObject(user, orgi)!=null) { - CacheHelper.getAgentStatusCacheBean().put(user, agentStatus , orgi); - } - } - InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress() ; - String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString()) ; - - - WorkSessionRepository workSessionRepository = MainContext.getContext().getBean(WorkSessionRepository.class) ; - int count = workSessionRepository.countByAgentAndDatestrAndOrgi(user, MainUtils.simpleDateFormat.format(new Date()), orgi) ; - - workSessionRepository.save(MainUtils.createWorkSession(user, MainUtils.getContextID(client.getSessionId().toString()), session, orgi, ip, address.getHostName() , admin , count == 0)) ; - - NettyClients.getInstance().putAgentEventClient(user, client); - } - } - - //添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息 - @OnDisconnect - public void onDisconnect(SocketIOClient client) - { - String user = client.getHandshakeData().getSingleUrlParam("userid") ; - String orgi = client.getHandshakeData().getSingleUrlParam("orgi") ; - String admin = client.getHandshakeData().getSingleUrlParam("admin") ; - if(!StringUtils.isBlank(user)){ - AutomaticServiceDist.deleteAgentStatus(user, orgi, !StringUtils.isBlank(admin) && admin.equals("true")); - NettyClients.getInstance().removeAgentEventClient(user , MainUtils.getContextID(client.getSessionId().toString())); - - WorkSessionRepository workSessionRepository = MainContext.getContext().getBean(WorkSessionRepository.class) ; - List workSessionList = workSessionRepository.findByOrgiAndClientid(orgi, MainUtils.getContextID(client.getSessionId().toString())) ; - if(workSessionList.size() > 0) { - WorkSession workSession = workSessionList.get(0) ; - workSession.setEndtime(new Date()); - if(workSession.getBegintime()!=null) { - workSession.setDuration((int) (System.currentTimeMillis() - workSession.getBegintime().getTime())); - }else if(workSession.getCreatetime()!=null) { - workSession.setDuration((int) (System.currentTimeMillis() - workSession.getCreatetime().getTime())); - } - if(workSession.isFirsttime()) { - workSession.setFirsttimes(workSession.getDuration()); - } - workSessionRepository.save(workSession) ; - } - } - } - - //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 - @OnEvent(value = "service") - public void onEvent(SocketIOClient client, AckRequest request, AgentServiceMessage data) - { - - } - - //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 - @OnEvent(value = "status") - public void onEvent(SocketIOClient client, AckRequest request, AgentStatusMessage data) - { - +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.List; + +public class AgentEventHandler { + protected SocketIOServer server; + + @Autowired + public AgentEventHandler(SocketIOServer server) { + this.server = server; } - - //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 - @OnEvent(value = "message") - public void onEvent(SocketIOClient client, AckRequest request, ChatMessage data) - { - String user = client.getHandshakeData().getSingleUrlParam("userid") ; - AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(data.getTouser(), data.getOrgi()); - MessageOutContent outMessage = new MessageOutContent() ; - outMessage.setMessage(data.getMessage()); - if(MainContext.MediaTypeEnum.COOPERATION.toString().equals(data.getMsgtype())){ - outMessage.setMessageType(MainContext.MediaTypeEnum.COOPERATION.toString()); - }else{ - outMessage.setMessageType(MainContext.MediaTypeEnum.TEXT.toString()); - } - - outMessage.setAttachmentid(data.getAttachmentid()); - - outMessage.setCalltype(MainContext.CallTypeEnum.OUT.toString()); - outMessage.setAgentUser(agentUser); - outMessage.setSnsAccount(null); - AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(data.getUserid(), data.getOrgi()) ; - - if(agentUser == null){ - agentUser = MainContext.getContext().getBean(AgentUserRepository.class).findByIdAndOrgi(data.getTouser() , data.getOrgi()) ; - try { - AutomaticServiceDist.serviceFinish(agentUser, data.getOrgi()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - if(agentUser!=null && user!=null && user.equals(agentUser.getAgentno())){ - data.setId(MainUtils.getUUID()); - data.setContextid(agentUser.getContextid()); - - data.setAgentserviceid(agentUser.getAgentserviceid()); - data.setCreater(agentUser.getAgentno()); - - if(MainContext.MediaTypeEnum.COOPERATION.toString().equals(data.getMsgtype())){ - data.setMsgtype(MainContext.MediaTypeEnum.COOPERATION.toString()); - }else{ - data.setMsgtype(MainContext.MediaTypeEnum.TEXT.toString()); - } - - data.setCalltype(MainContext.CallTypeEnum.OUT.toString()); - if(!StringUtils.isBlank(agentUser.getAgentno())){ - data.setTouser(agentUser.getUserid()); - } - data.setChannel(agentUser.getChannel()); - - data.setUsession(agentUser.getUserid()); - - outMessage.setContextid(agentUser.getContextid()); - outMessage.setFromUser(data.getUserid()); - outMessage.setToUser(data.getTouser()); - outMessage.setChannelMessage(data); - if(agentStatus!=null){ - data.setUsername(agentStatus.getUsername()); - outMessage.setNickName(agentStatus.getUsername()); - }else{ - outMessage.setNickName(data.getUsername()); - } - outMessage.setCreatetime(data.getCreatetime()); - - AgentUserTaskRepository agentUserTaskRes = MainContext.getContext().getBean(AgentUserTaskRepository.class) ; - AgentUserTask agentUserTask = agentUserTaskRes.getOne(agentUser.getId()) ; - - if(agentUserTask!=null){ - if(agentUserTask.getLastgetmessage() != null && agentUserTask.getLastmessage()!=null){ - data.setLastagentmsgtime(agentUserTask.getLastgetmessage()); - data.setLastmsgtime(agentUserTask.getLastmessage()); - data.setAgentreplyinterval((int)((System.currentTimeMillis() - agentUserTask.getLastgetmessage().getTime())/1000)); //坐席上次回复消息的间隔 - data.setAgentreplytime((int)((System.currentTimeMillis() - agentUserTask.getLastmessage().getTime())/1000)); //坐席回复消息花费时间 - } - - agentUserTask.setAgentreplys(agentUserTask.getAgentreplys()+1); //总咨询记录数量 - agentUserTask.setAgentreplyinterval(agentUserTask.getAgentreplyinterval() + data.getAgentreplyinterval()); //总时长 - if(agentUserTask.getAgentreplys()>0){ - agentUserTask.setAvgreplyinterval(agentUserTask.getAgentreplyinterval() / agentUserTask.getAgentreplys()); - } - - agentUserTask.setLastgetmessage(new Date()); - + + @OnConnect + public void onConnect(SocketIOClient client) { + String user = client.getHandshakeData().getSingleUrlParam("userid"); + String orgi = client.getHandshakeData().getSingleUrlParam("orgi"); + String session = client.getHandshakeData().getSingleUrlParam("session"); + String admin = client.getHandshakeData().getSingleUrlParam("admin"); + if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(user)) { + client.set("agentno", user); + AgentStatusRepository agentStatusRepository = MainContext.getContext().getBean(AgentStatusRepository.class); + List agentStatusList = agentStatusRepository.findByAgentnoAndOrgi(user, orgi); + if (agentStatusList.size() > 0) { + AgentStatus agentStatus = agentStatusList.get(0); + agentStatus.setUpdatetime(new Date()); + agentStatusRepository.save(agentStatus); + if (CacheHelper.getAgentStatusCacheBean().getCacheObject(user, orgi) != null) { + CacheHelper.getAgentStatusCacheBean().put(user, agentStatus, orgi); + } + } + InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress(); + String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString()); + + + WorkSessionRepository workSessionRepository = MainContext.getContext().getBean(WorkSessionRepository.class); + int count = workSessionRepository.countByAgentAndDatestrAndOrgi(user, MainUtils.simpleDateFormat.format(new Date()), orgi); + + workSessionRepository.save(MainUtils.createWorkSession(user, MainUtils.getContextID(client.getSessionId().toString()), session, orgi, ip, address.getHostName(), admin, count == 0)); + + NettyClients.getInstance().putAgentEventClient(user, client); + } + } + + //添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息 + @OnDisconnect + public void onDisconnect(SocketIOClient client) { + String user = client.getHandshakeData().getSingleUrlParam("userid"); + String orgi = client.getHandshakeData().getSingleUrlParam("orgi"); + String admin = client.getHandshakeData().getSingleUrlParam("admin"); + if (StringUtils.isNotBlank(user)) { + AutomaticServiceDist.deleteAgentStatus(user, orgi, StringUtils.isNotBlank(admin) && admin.equals("true")); + NettyClients.getInstance().removeAgentEventClient(user, MainUtils.getContextID(client.getSessionId().toString())); + + WorkSessionRepository workSessionRepository = MainContext.getContext().getBean(WorkSessionRepository.class); + List workSessionList = workSessionRepository.findByOrgiAndClientid(orgi, MainUtils.getContextID(client.getSessionId().toString())); + if (workSessionList.size() > 0) { + WorkSession workSession = workSessionList.get(0); + workSession.setEndtime(new Date()); + if (workSession.getBegintime() != null) { + workSession.setDuration((int) (System.currentTimeMillis() - workSession.getBegintime().getTime())); + } else if (workSession.getCreatetime() != null) { + workSession.setDuration((int) (System.currentTimeMillis() - workSession.getCreatetime().getTime())); + } + if (workSession.isFirsttime()) { + workSession.setFirsttimes(workSession.getDuration()); + } + workSessionRepository.save(workSession); + } + } + } + + //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 + @OnEvent(value = "service") + public void onEvent(SocketIOClient client, AckRequest request, AgentServiceMessage data) { + + } + + //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 + @OnEvent(value = "status") + public void onEvent(SocketIOClient client, AckRequest request, AgentStatusMessage data) { + + } + + //消息接收入口,当接收到消息后,查找发送目标客户端,并且向该客户端发送消息,且给自己发送消息 + @OnEvent(value = "message") + public void onEvent(SocketIOClient client, AckRequest request, ChatMessage data) { + String user = client.getHandshakeData().getSingleUrlParam("userid"); + AgentUser agentUser = (AgentUser) CacheHelper.getAgentUserCacheBean().getCacheObject(data.getTouser(), data.getOrgi()); + MessageOutContent outMessage = new MessageOutContent(); + outMessage.setMessage(data.getMessage()); + if (MainContext.MediaTypeEnum.COOPERATION.toString().equals(data.getMsgtype())) { + outMessage.setMessageType(MainContext.MediaTypeEnum.COOPERATION.toString()); + } else { + outMessage.setMessageType(MainContext.MediaTypeEnum.TEXT.toString()); + } + + outMessage.setAttachmentid(data.getAttachmentid()); + + outMessage.setCalltype(MainContext.CallTypeEnum.OUT.toString()); + outMessage.setAgentUser(agentUser); + outMessage.setSnsAccount(null); + AgentStatus agentStatus = (AgentStatus) CacheHelper.getAgentStatusCacheBean().getCacheObject(data.getUserid(), data.getOrgi()); + + if (agentUser == null) { + agentUser = MainContext.getContext().getBean(AgentUserRepository.class).findByIdAndOrgi(data.getTouser(), data.getOrgi()); + try { + AutomaticServiceDist.serviceFinish(agentUser, data.getOrgi()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (agentUser != null && user != null && user.equals(agentUser.getAgentno())) { + data.setId(MainUtils.getUUID()); + data.setContextid(agentUser.getContextid()); + + data.setAgentserviceid(agentUser.getAgentserviceid()); + data.setCreater(agentUser.getAgentno()); + + if (MainContext.MediaTypeEnum.COOPERATION.toString().equals(data.getMsgtype())) { + data.setMsgtype(MainContext.MediaTypeEnum.COOPERATION.toString()); + } else { + data.setMsgtype(MainContext.MediaTypeEnum.TEXT.toString()); + } + + data.setCalltype(MainContext.CallTypeEnum.OUT.toString()); + if (StringUtils.isNotBlank(agentUser.getAgentno())) { + data.setTouser(agentUser.getUserid()); + } + data.setChannel(agentUser.getChannel()); + + data.setUsession(agentUser.getUserid()); + + outMessage.setContextid(agentUser.getContextid()); + outMessage.setFromUser(data.getUserid()); + outMessage.setToUser(data.getTouser()); + outMessage.setChannelMessage(data); + if (agentStatus != null) { + data.setUsername(agentStatus.getUsername()); + outMessage.setNickName(agentStatus.getUsername()); + } else { + outMessage.setNickName(data.getUsername()); + } + outMessage.setCreatetime(data.getCreatetime()); + + AgentUserTaskRepository agentUserTaskRes = MainContext.getContext().getBean(AgentUserTaskRepository.class); + AgentUserTask agentUserTask = agentUserTaskRes.getOne(agentUser.getId()); + + if (agentUserTask != null) { + if (agentUserTask.getLastgetmessage() != null && agentUserTask.getLastmessage() != null) { + data.setLastagentmsgtime(agentUserTask.getLastgetmessage()); + data.setLastmsgtime(agentUserTask.getLastmessage()); + data.setAgentreplyinterval((int) ((System.currentTimeMillis() - agentUserTask.getLastgetmessage().getTime()) / 1000)); //坐席上次回复消息的间隔 + data.setAgentreplytime((int) ((System.currentTimeMillis() - agentUserTask.getLastmessage().getTime()) / 1000)); //坐席回复消息花费时间 + } + + agentUserTask.setAgentreplys(agentUserTask.getAgentreplys() + 1); //总咨询记录数量 + agentUserTask.setAgentreplyinterval(agentUserTask.getAgentreplyinterval() + data.getAgentreplyinterval()); //总时长 + if (agentUserTask.getAgentreplys() > 0) { + agentUserTask.setAvgreplyinterval(agentUserTask.getAgentreplyinterval() / agentUserTask.getAgentreplys()); + } + + agentUserTask.setLastgetmessage(new Date()); + // agentUserTask.setReptime(null); // agentUserTask.setReptimes("0"); - - agentUserTaskRes.save(agentUserTask) ; - } - - /** - * 保存消息 - */ - MainContext.getContext().getBean(ChatMessageRepository.class).save(data) ; - client.sendEvent(MainContext.MessageTypeEnum.MESSAGE.toString(), data); - - if(!StringUtils.isBlank(data.getTouser())){ - OutMessageRouter router = null ; - router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel()) ; - if(router!=null){ - router.handler(data.getTouser(), MainContext.MessageTypeEnum.MESSAGE.toString(), agentUser.getAppid(), outMessage); - } - } - }else if(user!=null && agentUser!=null && !user.equals(agentUser.getAgentno())){ - client.sendEvent(MainContext.MessageTypeEnum.END.toString(), agentUser); - } - } + agentUserTaskRes.save(agentUserTask); + } + + /** + * 保存消息 + */ + MainContext.getContext().getBean(ChatMessageRepository.class).save(data); + + client.sendEvent(MainContext.MessageTypeEnum.MESSAGE.toString(), data); + + if (StringUtils.isNotBlank(data.getTouser())) { + OutMessageRouter router = null; + router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel()); + if (router != null) { + router.handler(data.getTouser(), MainContext.MessageTypeEnum.MESSAGE.toString(), agentUser.getAppid(), outMessage); + } + } + } else if (user != null && agentUser != null && !user.equals(agentUser.getAgentno())) { + client.sendEvent(MainContext.MessageTypeEnum.END.toString(), agentUser); + } + } } \ No newline at end of file diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/CalloutEventHandler.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/CalloutEventHandler.java index aed2980a..8f405846 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/CalloutEventHandler.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/handler/CalloutEventHandler.java @@ -51,7 +51,7 @@ public class CalloutEventHandler String admin = client.getHandshakeData().getSingleUrlParam("admin") ; logger.info("onConnect userid {}, orgi {}.", user, orgi); - if(!StringUtils.isBlank(user) && !StringUtils.isBlank(user)){ + if(StringUtils.isNotBlank(user) && StringUtils.isNotBlank(user)){ client.set("agentno", user); InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress() ; String ip = MainUtils.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString()) ; diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/MessageRouter.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/MessageRouter.java index 53655660..647273d1 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/MessageRouter.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/MessageRouter.java @@ -58,7 +58,7 @@ public class MessageRouter extends Router { if (agentService != null && MainContext.AgentUserStatusEnum.INSERVICE.toString().equals(agentService.getStatus())) { outMessage.setMessage(AutomaticServiceDist.getSuccessMessage(agentService, inMessage.getAgentUser().getChannel(), inMessage.getOrgi())); // TODO #111 publish to redis - NettyClients.getInstance().sendAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), inMessage.getAgentUser()); + NettyClients.getInstance().publishAgentEventMessage(agentService.getAgentno(), MainContext.MessageTypeEnum.NEW.toString(), inMessage.getAgentUser()); } else { if (agentService.getQueneindex() > 0) { //当前有坐席 outMessage.setMessage(AutomaticServiceDist.getQueneMessage(agentService.getQueneindex(), inMessage.getAgentUser().getChannel(), inMessage.getOrgi())); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/WebIMOutMessageRouter.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/WebIMOutMessageRouter.java index 8f60b68d..d9d5e790 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/WebIMOutMessageRouter.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/router/WebIMOutMessageRouter.java @@ -32,7 +32,7 @@ public class WebIMOutMessageRouter implements OutMessageRouter{ @Override public void handler(String touser, String msgtype, String appid, MessageOutContent outMessage) { - NettyClients.getInstance().sendIMEventMessage(touser, msgtype, outMessage); + NettyClients.getInstance().publishIMEventMessage(touser, msgtype, outMessage); } } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/HumanUtils.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/HumanUtils.java index da4efde2..0d1ad19a 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/HumanUtils.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/HumanUtils.java @@ -137,14 +137,14 @@ public class HumanUtils { } } if (StringUtils.isNotBlank(data.getUserid()) && MainContext.MessageTypeEnum.MESSAGE.toString().equals(data.getType())) { - NettyClients.getInstance().sendIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), outMessage); + NettyClients.getInstance().publishIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), outMessage); if (statusMessage != null) { - NettyClients.getInstance().sendIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.STATUS.toString(), statusMessage); + NettyClients.getInstance().publishIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.STATUS.toString(), statusMessage); } } if (agentUser != null && StringUtils.isNotBlank(agentUser.getAgentno())) { - //将消息发送给 坐席 - NettyClients.getInstance().sendAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); + // TODO 将消息发送给 坐席 + NettyClients.getInstance().publishAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); } } @@ -176,7 +176,7 @@ public class HumanUtils { // outMessage.setCreatetime(data.getCreatetime()); // // if (!StringUtils.isBlank(data.getUserid()) && MainContext.MessageTypeEnum.MESSAGE.toString().equals(data.getType())) { -// NettyClients.getInstance().sendIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), outMessage); +// NettyClients.getInstance().publishIMEventMessage(data.getUserid(), MainContext.MessageTypeEnum.MESSAGE.toString(), outMessage); // } // } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/IMServiceUtils.java b/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/IMServiceUtils.java index a5e5a7c4..4e5edc31 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/IMServiceUtils.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/im/util/IMServiceUtils.java @@ -8,6 +8,9 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.*; +import java.util.Base64; + public class IMServiceUtils { private final static Logger logger = LoggerFactory.getLogger(IMServiceUtils.class); @@ -36,4 +39,30 @@ public class IMServiceUtils { service.save(agentUser); CacheHelper.getAgentUserCacheBean().put(agentUser.getUserid(), agentUser, orgi); } + + /** + * Write the object to a Base64 string. + */ + public static String serialize(Serializable o) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(o); + oos.close(); + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + + /** + * Read the object from Base64 string. + */ + public static Object deserialize(String s) throws IOException, + ClassNotFoundException { + byte[] data = Base64.getDecoder().decode(s); + ObjectInputStream ois = new ObjectInputStream( + new ByteArrayInputStream(data)); + Object o = ois.readObject(); + ois.close(); + return o; + } + } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/model/MessageInContent.java b/contact-center/app/src/main/java/com/chatopera/cc/app/model/MessageInContent.java index 13e81480..d604dee2 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/model/MessageInContent.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/model/MessageInContent.java @@ -20,143 +20,180 @@ package com.chatopera.cc.app.model; import com.chatopera.cc.app.basic.MainContext; -public class MessageInContent implements MessageDataBean{ - - public String id ; - private String nickName; - private String orgi ; - private String message ; - private String filename ; - private int filesize ; - private String messageType; - private String fromUser; - private String calltype = MainContext.CallTypeEnum.IN.toString() ; - private String toUser; - private SNSAccount snsAccount ; - private AgentUser agentUser ; - private Object channelMessage ; - private String agentserviceid ; - - private String attachmentid ; - - private boolean noagent ; - - private Object user ; - private String contextid ; - private String createtime ; - - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public String getNickName() { - return nickName; - } - public void setNickName(String nickName) { - this.nickName = nickName; - } - public String getOrgi() { - return orgi; - } - public void setOrgi(String orgi) { - this.orgi = orgi; - } - public String getMessage() { - return message; - } - public void setMessage(String message) { - this.message = message; - } - public String getMessageType() { - return messageType; - } - public void setMessageType(String messageType) { - this.messageType = messageType; - } - public String getFromUser() { - return fromUser; - } - public void setFromUser(String fromUser) { - this.fromUser = fromUser; - } - public String getToUser() { - return toUser; - } - public void setToUser(String toUser) { - this.toUser = toUser; - } - public SNSAccount getSnsAccount() { - return snsAccount; - } - public void setSnsAccount(SNSAccount snsAccount) { - this.snsAccount = snsAccount; - } - public AgentUser getAgentUser() { - return agentUser; - } - public void setAgentUser(AgentUser agentUser) { - this.agentUser = agentUser; - } - public Object getChannelMessage() { - return channelMessage; - } - public void setChannelMessage(Object channelMessage) { - this.channelMessage = channelMessage; - } - public Object getUser() { - return user; - } - public void setUser(Object user) { - this.user = user; - } - public String getContextid() { - return contextid; - } - public void setContextid(String contextid) { - this.contextid = contextid; - } - public String getCalltype() { - return calltype; - } - public void setCalltype(String calltype) { - this.calltype = calltype; - } - public String getCreatetime() { - return createtime; - } - public void setCreatetime(String createtime) { - this.createtime = createtime; - } - public String getFilename() { - return filename; - } - public void setFilename(String filename) { - this.filename = filename; - } - public int getFilesize() { - return filesize; - } - public void setFilesize(int filesize) { - this.filesize = filesize; - } - public String getAgentserviceid() { - return agentserviceid; - } - public void setAgentserviceid(String agentserviceid) { - this.agentserviceid = agentserviceid; - } - public String getAttachmentid() { - return attachmentid; - } - public void setAttachmentid(String attachmentid) { - this.attachmentid = attachmentid; - } - public boolean isNoagent() { - return noagent; - } - public void setNoagent(boolean noagent) { - this.noagent = noagent; - } +public class MessageInContent implements MessageDataBean, java.io.Serializable { + + public String id; + private String nickName; + private String orgi; + private String message; + private String filename; + private int filesize; + private String messageType; + private String fromUser; + private String calltype = MainContext.CallTypeEnum.IN.toString(); + private String toUser; + private SNSAccount snsAccount; + private AgentUser agentUser; + private Object channelMessage; + private String agentserviceid; + + private String attachmentid; + + private boolean noagent; + + private Object user; + private String contextid; + private String createtime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getOrgi() { + return orgi; + } + + public void setOrgi(String orgi) { + this.orgi = orgi; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessageType() { + return messageType; + } + + public void setMessageType(String messageType) { + this.messageType = messageType; + } + + public String getFromUser() { + return fromUser; + } + + public void setFromUser(String fromUser) { + this.fromUser = fromUser; + } + + public String getToUser() { + return toUser; + } + + public void setToUser(String toUser) { + this.toUser = toUser; + } + + public SNSAccount getSnsAccount() { + return snsAccount; + } + + public void setSnsAccount(SNSAccount snsAccount) { + this.snsAccount = snsAccount; + } + + public AgentUser getAgentUser() { + return agentUser; + } + + public void setAgentUser(AgentUser agentUser) { + this.agentUser = agentUser; + } + + public Object getChannelMessage() { + return channelMessage; + } + + public void setChannelMessage(Object channelMessage) { + this.channelMessage = channelMessage; + } + + public Object getUser() { + return user; + } + + public void setUser(Object user) { + this.user = user; + } + + public String getContextid() { + return contextid; + } + + public void setContextid(String contextid) { + this.contextid = contextid; + } + + public String getCalltype() { + return calltype; + } + + public void setCalltype(String calltype) { + this.calltype = calltype; + } + + public String getCreatetime() { + return createtime; + } + + public void setCreatetime(String createtime) { + this.createtime = createtime; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public int getFilesize() { + return filesize; + } + + public void setFilesize(int filesize) { + this.filesize = filesize; + } + + public String getAgentserviceid() { + return agentserviceid; + } + + public void setAgentserviceid(String agentserviceid) { + this.agentserviceid = agentserviceid; + } + + public String getAttachmentid() { + return attachmentid; + } + + public void setAttachmentid(String attachmentid) { + this.attachmentid = attachmentid; + } + + public boolean isNoagent() { + return noagent; + } + + public void setNoagent(boolean noagent) { + this.noagent = noagent; + } } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/CallOutWireTask.java b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/CallOutWireTask.java index 2c4afa4a..fd6cde8c 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/CallOutWireTask.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/CallOutWireTask.java @@ -55,8 +55,6 @@ import java.util.List; @Component public class CallOutWireTask implements MessageListener { private static final Logger logger = LoggerFactory.getLogger(CallOutWireTask.class); - private static final RedisSerializer redisStringSerializer = new StringRedisSerializer(); - @Autowired private CallOutDialplanRepository callOutDialplanRes; diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMAgentDispatcher.java b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMAgentDispatcher.java new file mode 100644 index 00000000..4d09db99 --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMAgentDispatcher.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 Chatopera Inc, + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.chatopera.cc.app.schedule; + +import com.chatopera.cc.app.im.client.NettyClients; +import com.chatopera.cc.app.im.util.IMServiceUtils; +import com.chatopera.cc.util.Constants; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.ListOperations; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.*; +import java.util.Base64; + +/** + * 坐席消息分发 + */ +@Component +public class WebIMAgentDispatcher implements MessageListener { + private final static Logger logger = LoggerFactory.getLogger(WebIMAgentDispatcher.class); + + private ListOperations redisListOps; + private HashOperations redisHashOps; + + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private String redisPort; + + @Value("${spring.redis.database}") + private String redisDB; + + @Value("${application.node.id}") + private String appNodeId; + + + /** + * 使用StringRedisTemplate而不是RedisTemplate解决序列化问题 + * https://stackoverflow.com/questions/13215024/weird-redis-key-with-spring-data-jedis + */ + @Autowired + private StringRedisTemplate redis; + + @PostConstruct + private void init() { + redisListOps = redis.opsForList(); + redisHashOps = redis.opsForHash(); + } + + /** + * Publish Message into Channel with redis PubSub + * + * @param j + */ + public void publish(JsonObject j) { + ChannelTopic ct = new ChannelTopic(String.format(Constants.INSTANT_MESSTRING_WEBIM_AGENT_PATTERN, appNodeId)); + j.addProperty("node", appNodeId); + redis.convertAndSend(ct.getTopic(), j.toString()); + } + + @Override + public void onMessage(Message message, byte[] bytes) { + logger.debug("[instant messaging] onMessage {}", message); + String payload = new String(message.getBody()); + JsonParser parser = new JsonParser(); + JsonObject j = parser.parse(payload).getAsJsonObject(); + logger.debug("[instant messaging] message body {}", j.toString()); + try { + NettyClients.getInstance().sendAgentEventMessage(j.get("id").getAsString(), + j.get("event").getAsString(), + IMServiceUtils.deserialize(j.get("data").getAsString())); + } catch (Exception e) { + logger.error("onMessage", e); + } + + } + +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMOnlineUserDispatcher.java b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMOnlineUserDispatcher.java new file mode 100644 index 00000000..3254d099 --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMOnlineUserDispatcher.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 Chatopera Inc, + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.chatopera.cc.app.schedule; + +import com.chatopera.cc.app.im.client.NettyClients; +import com.chatopera.cc.app.im.util.IMServiceUtils; +import com.chatopera.cc.util.Constants; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.ListOperations; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.*; +import java.util.Base64; + +/** + * 访客消息分发 + */ +@Component +public class WebIMOnlineUserDispatcher implements MessageListener { + private final static Logger logger = LoggerFactory.getLogger(WebIMOnlineUserDispatcher.class); + + private ListOperations redisListOps; + private HashOperations redisHashOps; + + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private String redisPort; + + @Value("${spring.redis.database}") + private String redisDB; + + @Value("${application.node.id}") + private String appNodeId; + + + /** + * 使用StringRedisTemplate而不是RedisTemplate解决序列化问题 + * https://stackoverflow.com/questions/13215024/weird-redis-key-with-spring-data-jedis + */ + @Autowired + private StringRedisTemplate redis; + + @PostConstruct + private void init() { + redisListOps = redis.opsForList(); + redisHashOps = redis.opsForHash(); + } + + /** + * Publish Message into Channel with redis PubSub + * + * @param j + */ + public void publish(JsonObject j) { + ChannelTopic ct = new ChannelTopic(String.format(Constants.INSTANT_MESSTRING_WEBIM_ONLINE_USER_PATTERN, appNodeId)); + j.addProperty("node", appNodeId); + redis.convertAndSend(ct.getTopic(), j.toString()); + } + + @Override + public void onMessage(Message message, byte[] bytes) { + logger.debug("[instant messaging] onMessage {}", message); + String payload = new String(message.getBody()); + JsonParser parser = new JsonParser(); + JsonObject j = parser.parse(payload).getAsJsonObject(); + logger.debug("[instant messaging] message body {}", j.toString()); + try { + NettyClients.getInstance().sendIMEventMessage(j.get("id").getAsString(), + j.get("event").getAsString(), + IMServiceUtils.deserialize(j.get("data").getAsString())); + } catch (Exception e) { + logger.error("onMessage", e); + } + } +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMTask.java b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMTask.java index e11dc5b9..433c1268 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMTask.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/app/schedule/WebIMTask.java @@ -15,25 +15,19 @@ */ package com.chatopera.cc.app.schedule; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; - import com.chatopera.cc.app.algorithm.AutomaticServiceDist; import com.chatopera.cc.app.basic.MainContext; -import com.chatopera.cc.app.im.client.NettyClients; import com.chatopera.cc.app.basic.MainUtils; -import com.chatopera.cc.exchange.DataExchangeInterface; -import com.chatopera.cc.util.freeswitch.model.CallCenterAgent; import com.chatopera.cc.app.cache.CacheHelper; -import com.chatopera.cc.app.persistence.impl.CallOutQuene; -import com.chatopera.cc.app.persistence.repository.AgentUserTaskRepository; -import com.chatopera.cc.app.persistence.repository.JobDetailRepository; -import com.chatopera.cc.app.persistence.repository.OnlineUserRepository; -import com.chatopera.cc.util.OnlineUserUtils; -import com.chatopera.cc.app.im.router.OutMessageRouter; +import com.chatopera.cc.app.im.client.NettyClients; import com.chatopera.cc.app.im.message.ChatMessage; +import com.chatopera.cc.app.im.router.OutMessageRouter; +import com.chatopera.cc.app.model.*; +import com.chatopera.cc.app.persistence.impl.CallOutQuene; +import com.chatopera.cc.app.persistence.repository.*; +import com.chatopera.cc.exchange.DataExchangeInterface; +import com.chatopera.cc.util.OnlineUserUtils; +import com.chatopera.cc.util.freeswitch.model.CallCenterAgent; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -43,18 +37,10 @@ import org.springframework.data.domain.PageRequest; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; -import com.chatopera.cc.app.persistence.repository.ChatMessageRepository; -import com.chatopera.cc.app.persistence.repository.ConsultInviteRepository; -import com.chatopera.cc.app.model.AgentStatus; -import com.chatopera.cc.app.model.AgentUser; -import com.chatopera.cc.app.model.AgentUserTask; -import com.chatopera.cc.app.model.AiConfig; -import com.chatopera.cc.app.model.AiUser; -import com.chatopera.cc.app.model.CousultInvite; -import com.chatopera.cc.app.model.JobDetail; -import com.chatopera.cc.app.model.MessageOutContent; -import com.chatopera.cc.app.model.OnlineUser; -import com.chatopera.cc.app.model.SessionConfig; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; @Configuration @EnableScheduling @@ -72,7 +58,7 @@ public class WebIMTask { @Autowired private TaskExecutor webimTaskExecutor; - @Scheduled(fixedDelay = 5000) // 每5秒执行一次 + @Scheduled(fixedDelay = 5000) // 处理超时消息,每5秒执行一次 public void task() { List sessionConfigList = AutomaticServiceDist.initSessionConfigList(); if (sessionConfigList != null && sessionConfigList.size() > 0 && MainContext.getContext() != null) { @@ -255,7 +241,7 @@ public class WebIMTask { private void processMessage(SessionConfig sessionConfig, String message, String servicename, AgentUser agentUser, AgentStatus agentStatus, AgentUserTask task) { MessageOutContent outMessage = new MessageOutContent(); - if (!StringUtils.isBlank(message)) { + if (StringUtils.isNotBlank(message)) { outMessage.setMessage(message); outMessage.setMessageType(MainContext.MediaTypeEnum.TEXT.toString()); outMessage.setCalltype(MainContext.CallTypeEnum.OUT.toString()); @@ -279,7 +265,7 @@ public class WebIMTask { data.setAgentserviceid(agentUser.getAgentserviceid()); data.setCalltype(MainContext.CallTypeEnum.OUT.toString()); - if (!StringUtils.isBlank(agentUser.getAgentno())) { + if (StringUtils.isNotBlank(agentUser.getAgentno())) { data.setTouser(agentUser.getUserid()); } data.setChannel(agentUser.getChannel()); @@ -304,11 +290,12 @@ public class WebIMTask { */ MainContext.getContext().getBean(ChatMessageRepository.class).save(data); - if (agentUser != null && !StringUtils.isBlank(agentUser.getAgentno())) { //同时发送消息给双方 - NettyClients.getInstance().sendAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); + // 同时发送消息给双方 + if (agentUser != null && StringUtils.isNotBlank(agentUser.getAgentno())) { + NettyClients.getInstance().publishAgentEventMessage(agentUser.getAgentno(), MainContext.MessageTypeEnum.MESSAGE.toString(), data); } - if (!StringUtils.isBlank(data.getTouser())) { + if (StringUtils.isNotBlank(data.getTouser())) { OutMessageRouter router = null; router = (OutMessageRouter) MainContext.getContext().getBean(agentUser.getChannel()); if (router != null) { diff --git a/contact-center/app/src/main/java/com/chatopera/cc/util/Constants.java b/contact-center/app/src/main/java/com/chatopera/cc/util/Constants.java index c83644ba..d3ed8ecc 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/util/Constants.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/util/Constants.java @@ -16,6 +16,7 @@ package com.chatopera.cc.util; import com.chatopera.cc.app.basic.MainContext; +import org.apache.commons.lang.StringUtils; import java.text.DecimalFormat; import java.text.SimpleDateFormat; @@ -46,6 +47,17 @@ public class Constants { public final static SimpleDateFormat DISPLAY_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public final static DecimalFormat DURATION_MINS_FORMATTER = new DecimalFormat("0.00"); + + /** + * Instant Messaging Events + */ + public final static String INSTANT_MESSTRING_WEBIM_AGENT_PATTERN = "im:webim:agent:%s:events"; + public final static String INSTANT_MESSAGING_WEBIM_AGENT_CHANNEL = String.format(INSTANT_MESSTRING_WEBIM_AGENT_PATTERN, "*"); + public final static String INSTANT_MESSTRING_WEBIM_ONLINE_USER_PATTERN = "im:webim:onlineuser:%s:events"; + public final static String INSTANT_MESSAGING_WEBIM_ONLINE_USER_CHANNEL = String.format(INSTANT_MESSTRING_WEBIM_ONLINE_USER_PATTERN, "*"); + + + /** * FreeSwitch Communication */ diff --git a/contact-center/app/src/main/resources/application.properties b/contact-center/app/src/main/resources/application.properties index 5af82aae..248feb69 100644 --- a/contact-center/app/src/main/resources/application.properties +++ b/contact-center/app/src/main/resources/application.properties @@ -18,6 +18,8 @@ # 证书相关信息 license.client.id=cskefu application.version=3.9.0 +# 在集群状态下,每个Node都有自己唯一的ID +application.node.id=localhost # security management.security.enabled=false @@ -131,7 +133,7 @@ spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) -spring.redis.pool.max-active=20 +spring.redis.pool.max-active=-1 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接