diff --git a/contact-center/app/src/main/java/com/chatopera/cc/core/UKDataContext.java b/contact-center/app/src/main/java/com/chatopera/cc/core/UKDataContext.java index 55ee0066..5394f5f5 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/core/UKDataContext.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/core/UKDataContext.java @@ -705,7 +705,7 @@ public class UKDataContext { IM("/im/user"), AGENT("/im/agent"), ENTIM("/im/ent"), - AIIM("/im/ai"), + CHATBOT("/im/chatbot"), CALLCENTER("/callcenter/event"), CALLOUT("/callout/event"); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java index 831ff212..9e7a7d87 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/service/repository/ChatbotRepository.java @@ -15,6 +15,8 @@ public abstract interface ChatbotRepository extends JpaRepository findByIdAndOrgi(String id, String orgi); + @Query(value = "select c from Chatbot c where " + "(:myorgans is null or c.organ IN :myorgans)") public Page findByOrgans(@Param("myorgans") List myorgans, Pageable pageRequest); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/ServerRunner.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/ServerRunner.java index 00aa4519..34269faa 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/ServerRunner.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/ServerRunner.java @@ -19,7 +19,7 @@ package com.chatopera.cc.webim.util.server; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import com.chatopera.cc.webim.util.server.handler.AiIMEventHandler; +import com.chatopera.cc.webim.util.server.handler.ChatbotEventHandler; import com.chatopera.cc.webim.util.server.handler.EntIMEventHandler; import com.chatopera.cc.webim.util.server.handler.IMEventHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +38,7 @@ public class ServerRunner implements CommandLineRunner { private final SocketIONamespace imSocketNameSpace ; private final SocketIONamespace agentSocketIONameSpace ; private final SocketIONamespace entIMSocketIONameSpace ; - private final SocketIONamespace aiIMSocketIONameSpace ; + private final SocketIONamespace chatbotSocketIONameSpace ; private final SocketIONamespace callCenterSocketIONameSpace ; private final SocketIONamespace calloutSocketIONameSpace ; @@ -48,7 +48,7 @@ public class ServerRunner implements CommandLineRunner { imSocketNameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.IM.getNamespace()) ; agentSocketIONameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.AGENT.getNamespace()) ; entIMSocketIONameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.ENTIM.getNamespace()) ; - aiIMSocketIONameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.AIIM.getNamespace()) ; + chatbotSocketIONameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.CHATBOT.getNamespace()) ; if(UKDataContext.model.get("sales") != null && UKDataContext.model.get("sales") == true){ calloutSocketIONameSpace = server.addNamespace(UKDataContext.NameSpaceEnum.CALLOUT.getNamespace()); @@ -81,10 +81,10 @@ public class ServerRunner implements CommandLineRunner { return entIMSocketIONameSpace; } - @Bean(name="aiimNamespace") - public SocketIONamespace getAiIMSocketIONameSpace(SocketIOServer server){ - aiIMSocketIONameSpace.addListeners(new AiIMEventHandler(server)); - return aiIMSocketIONameSpace; + @Bean(name="chatbotNamespace") + public SocketIONamespace getChatbotSocketIONameSpace(SocketIOServer server){ + chatbotSocketIONameSpace.addListeners(new ChatbotEventHandler(server)); + return chatbotSocketIONameSpace; } @Bean(name="callCenterNamespace") diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/handler/ChatbotEventHandler.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/handler/ChatbotEventHandler.java new file mode 100644 index 00000000..b6347f0a --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/util/server/handler/ChatbotEventHandler.java @@ -0,0 +1,198 @@ +/* + * 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.webim.util.server.handler; + +import com.chatopera.cc.core.UKDataContext; +import com.chatopera.cc.util.IPTools; +import com.chatopera.cc.util.UKTools; +import com.chatopera.cc.util.client.NettyClients; +import com.chatopera.cc.webim.service.acd.ServiceQuene; +import com.chatopera.cc.webim.service.cache.CacheHelper; +import com.chatopera.cc.webim.service.repository.ConsultInviteRepository; +import com.chatopera.cc.webim.util.MessageUtils; +import com.chatopera.cc.webim.util.OnlineUserUtils; +import com.chatopera.cc.webim.util.router.OutMessageRouter; +import com.chatopera.cc.webim.util.server.message.AgentStatusMessage; +import com.chatopera.cc.webim.util.server.message.ChatMessage; +import com.chatopera.cc.webim.util.server.message.NewRequestMessage; +import com.chatopera.cc.webim.web.model.AgentService; +import com.chatopera.cc.webim.web.model.AiUser; +import com.chatopera.cc.webim.web.model.CousultInvite; +import com.chatopera.cc.webim.web.model.MessageOutContent; +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 org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import java.net.InetSocketAddress; +import java.util.Date; + +public class ChatbotEventHandler +{ + protected SocketIOServer server; + + @Autowired + public ChatbotEventHandler(SocketIOServer server) + { + this.server = server ; + } + + @OnConnect + public void onConnect(SocketIOClient client) + { + try { + String user = client.getHandshakeData().getSingleUrlParam("userid") ; + String orgi = client.getHandshakeData().getSingleUrlParam("orgi") ; +// String session = client.getHandshakeData().getSingleUrlParam("session") ; + String appid = client.getHandshakeData().getSingleUrlParam("appid") ; + String aiid = client.getHandshakeData().getSingleUrlParam("aiid") ; +// String agent = client.getHandshakeData().getSingleUrlParam("agent") ; +// String skill = client.getHandshakeData().getSingleUrlParam("skill") ; + + if(!StringUtils.isBlank(user)){ +// /** +// * 加入到 缓存列表 +// */ + NettyClients.getInstance().putIMEventClient(user, client); + MessageOutContent outMessage = new MessageOutContent() ; + CousultInvite invite = OnlineUserUtils.cousult(appid , orgi, UKDataContext.getContext().getBean(ConsultInviteRepository.class)); + if(invite!=null && !StringUtils.isBlank(invite.getAisuccesstip())) { + outMessage.setMessage(invite.getAisuccesstip()); + }else{ + outMessage.setMessage("欢迎使用优客服小E,我来帮您解答问题"); + } + + outMessage.setMessageType(UKDataContext.MessageTypeEnum.MESSAGE.toString()); + outMessage.setCalltype(UKDataContext.CallTypeEnum.IN.toString()); + outMessage.setNickName("AI"); + outMessage.setCreatetime(UKTools.dateFormate.format(new Date())); + + client.sendEvent(UKDataContext.MessageTypeEnum.STATUS.toString(), outMessage); + + InetSocketAddress address = (InetSocketAddress) client.getRemoteAddress() ; + String ip = UKTools.getIpAddr(client.getHandshakeData().getHttpHeaders(), address.getHostString()) ; + AiUser aiUser = new AiUser(user, user, System.currentTimeMillis() , orgi,IPTools.getInstance().findGeography(ip)) ; + aiUser.setSessionid(UKTools.getContextID(client.getSessionId().toString())); + aiUser.setAppid(appid); + aiUser.setAiid(aiid); + aiUser.setUsername(UKDataContext.GUEST_USER+"_"+UKTools.genIDByKey(aiUser.getId())); + aiUser.setChannel(UKDataContext.ChannelTypeEnum.WEBIM.toString()); + + AgentService agentService = ServiceQuene.processAiService(aiUser, orgi) ; + aiUser.setAgentserviceid(agentService.getId()); + + CacheHelper.getOnlineUserCacheBean().put(user, aiUser, UKDataContext.SYSTEM_ORGI); + + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + //添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息 + @OnDisconnect + public void onDisconnect(SocketIOClient client) throws Exception + { + String user = client.getHandshakeData().getSingleUrlParam("userid") ; + String orgi = client.getHandshakeData().getSingleUrlParam("orgi") ; + if(!StringUtils.isBlank(user)){ + NettyClients.getInstance().removeIMEventClient(user , UKTools.getContextID(client.getSessionId().toString())); + AiUser aiUser = (AiUser) CacheHelper.getOnlineUserCacheBean().getCacheObject(user, orgi) ; + if(aiUser!=null) { + ServiceQuene.processAiService(aiUser, orgi) ; + CacheHelper.getOnlineUserCacheBean().delete(user,UKDataContext.SYSTEM_ORGI) ; + } + } + client.disconnect(); + } + + //消息接收入口,网站有新用户接入对话 + @OnEvent(value = "new") + public void onEvent(SocketIOClient client, AckRequest request, NewRequestMessage data) + { + + } + + //消息接收入口,坐席状态更新 + @OnEvent(value = "agentstatus") + public void onEvent(SocketIOClient client, AckRequest request, AgentStatusMessage data) + { + System.out.println(data.getMessage()); + } + + //消息接收入口,收发消息,用户向坐席发送消息和 坐席向用户发送消息 + @OnEvent(value = "message") + public void onEvent(SocketIOClient client, AckRequest request, ChatMessage data) + { + String orgi = client.getHandshakeData().getSingleUrlParam("orgi") ; + String aiid = client.getHandshakeData().getSingleUrlParam("aiid") ; + String user = client.getHandshakeData().getSingleUrlParam("userid") ; + if(data.getType() == null){ + data.setType("message"); + } + /** + * 以下代码主要用于检查 访客端的字数限制 + */ + CousultInvite invite = OnlineUserUtils.cousult(data.getAppid(),data.getOrgi(), UKDataContext.getContext().getBean(ConsultInviteRepository.class)); + if(invite!=null && invite.getMaxwordsnum() > 0) { + if(!StringUtils.isBlank(data.getMessage()) && data.getMessage().length() > invite.getMaxwordsnum()){ + data.setMessage(data.getMessage().substring(0 , invite.getMaxwordsnum())); + } + }else if(!StringUtils.isBlank(data.getMessage()) && data.getMessage().length() > 300){ + data.setMessage(data.getMessage().substring(0 , 300)); + } + data.setSessionid(UKTools.getContextID(client.getSessionId().toString())); + /** + * 处理表情 + */ + data.setMessage(UKTools.processEmoti(data.getMessage())); + data.setTousername(UKDataContext.ChannelTypeEnum.AI.toString()); + + data.setAiid(aiid); + + Object cacheData = (AiUser) CacheHelper.getOnlineUserCacheBean().getCacheObject(user,orgi) ; + if(cacheData!=null && cacheData instanceof AiUser){ + AiUser aiUser = (AiUser)cacheData ; + data.setAgentserviceid(aiUser.getAgentserviceid()); + data.setChannel(aiUser.getChannel()); + /** + * 一定要设置 ContextID + */ + data.setContextid(aiUser.getAgentserviceid()); + } + MessageOutContent outMessage = MessageUtils.createAiMessage(data , data.getAppid() , data.getChannel() , UKDataContext.CallTypeEnum.IN.toString() , UKDataContext.AiItemType.USERINPUT.toString() , UKDataContext.MediaTypeEnum.TEXT.toString(), data.getUserid()) ; + if(!StringUtils.isBlank(data.getUserid()) && UKDataContext.MessageTypeEnum.MESSAGE.toString().equals(data.getType())){ + if(!StringUtils.isBlank(data.getTouser())){ + OutMessageRouter router = null ; + router = (OutMessageRouter) UKDataContext.getContext().getBean(data.getChannel()) ; + if(router!=null){ + router.handler(data.getTouser(), UKDataContext.MessageTypeEnum.MESSAGE.toString(), data.getAppid(), outMessage); + } + } + if(cacheData!=null && cacheData instanceof AiUser){ + AiUser aiUser = (AiUser)cacheData ; + aiUser.setTime(System.currentTimeMillis()); + CacheHelper.getOnlineUserCacheBean().put(user, aiUser, UKDataContext.SYSTEM_ORGI); + } + } + UKTools.ai(data); + } +} \ No newline at end of file diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/apps/internet/IMController.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/apps/internet/IMController.java index c4546a07..96eac4d5 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/apps/internet/IMController.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/handler/apps/internet/IMController.java @@ -56,6 +56,8 @@ import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; @Controller @@ -474,32 +476,19 @@ public class IMController extends Handler { if (UKDataContext.model.get("chatbot") != null && StringUtils.isNotBlank(invite.getAiid()) && invite.isAi() && - (StringUtils.equals(ai, "true") || (invite.isAifirst() && ai == null))) { //启用 AI , 并且 AI优先 接待 - DataExchangeInterface dataInterface = (DataExchangeInterface) UKDataContext.getContext().getBean("aiconfig"); - AiConfig aiConfig = (AiConfig) dataInterface.getDataByIdAndOrgi(aiid, invite.getOrgi()); - if (aiConfig != null) { - map.addAttribute("aiConfig", aiConfig); + invite.isAifirst()) { //启用 AI , 并且 AI优先 接待 + HashMap chatbotConfig = new HashMap(); + chatbotConfig.put("botname", invite.getAiname()); + chatbotConfig.put("botid", invite.getAiid()); + chatbotConfig.put("botwelcome", invite.getAimsg()); + chatbotConfig.put("botfirst", Boolean.toString(invite.isAifirst())); + chatbotConfig.put("isai", Boolean.toString(invite.isAi())); + if (chatbotConfig != null) { + map.addAttribute("chatbotConfig", chatbotConfig); } - view = request(super.createRequestPageTempletResponse("/apps/im/ai/index")); + view = request(super.createRequestPageTempletResponse("/apps/im/chatbot/index")); if (CheckMobile.check(request.getHeader("User-Agent")) || !StringUtils.isBlank(mobile)) { - view = request(super.createRequestPageTempletResponse("/apps/im/ai/mobile")); //智能机器人 移动端 - } - if (UKDataContext.model.get("xiaoe") != null) { - List topicList = OnlineUserUtils.cacheHotTopic((DataExchangeInterface) UKDataContext.getContext().getBean("topic"), super.getUser(request), orgi, aiid); - - /** - * 初步按照地区匹配分类筛选 - */ - List topicTypeList = OnlineUserUtils.topicType(orgi, ipdata, OnlineUserUtils.cacheHotTopicType((DataExchangeInterface) UKDataContext.getContext().getBean("topictype"), super.getUser(request), orgi, aiid)); - - /** - * 第二步按照 有 热点主题的 分类做筛选 - */ - map.addAttribute("topicList", OnlineUserUtils.topic(orgi, topicTypeList, topicList)); - /** - * 第三步筛选 分类,如果无热点知识,则不显示分类 - */ - map.addAttribute("topicTypeList", OnlineUserUtils.filterTopicType(topicTypeList, topicList)); + view = request(super.createRequestPageTempletResponse("/apps/im/chatbot/mobile")); //智能机器人 移动端 } } else { if (CheckMobile.check(request.getHeader("User-Agent")) || !StringUtils.isBlank(mobile)) { diff --git a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java index 38a681e0..b642b084 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/webim/web/model/Chatbot.java @@ -20,6 +20,7 @@ import com.chatopera.chatbot.ChatbotAPIRuntimeException; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; +import java.io.Serializable; import java.net.MalformedURLException; import java.util.Date; diff --git a/contact-center/app/src/main/resources/templates/apps/im/chatbot/index.html b/contact-center/app/src/main/resources/templates/apps/im/chatbot/index.html new file mode 100644 index 00000000..22566be4 --- /dev/null +++ b/contact-center/app/src/main/resources/templates/apps/im/chatbot/index.html @@ -0,0 +1,581 @@ + + + + + + + + + 在线咨询 + + + + + + + + + + + + + + + +
+
+ +
+
+ <#if welcomeAd> +
+ + <#if welcomeAd.adtype =="image"> + + <#else> + + + +
+ +
+ ${(inviteData.dialog_message!'欢迎您来咨询!欢迎使用春松客服!如需帮助请联系 info@chatopera.com')?no_esc}
+ <#if chatMessageList?? && chatMessageList.content??> + <#list chatMessageList.content?reverse as chatMessage> + <#if chatMessage.userid?? && userid?? && chatMessage.calltype?? && chatMessage.calltype = "呼入"> +
+
+ +
+ + +
+
+ +
<#include "/apps/im/media/message.html">
+
+
+
+ <#else> +
+
+ +
+ + +
+
+ +
<#include "/apps/im/media/message.html">
+
+
+
+ + + +
+
+ +
+
0/200
+ +
+
+
+
+
+
+

信息提示

+
+
    + <#if inviteData.dialog_name?? && inviteData.dialog_name != ""> +
  • +

    名称:${inviteData.dialog_name!''}

    +
  • + + <#if inviteData.dialog_address?? && inviteData.dialog_address != ""> +
  • +

    地址:${inviteData.dialog_address!''}

    +
  • + + <#if inviteData.dialog_phone?? && inviteData.dialog_phone != ""> +
  • +

    电话:${inviteData.dialog_phone!''}

    +
  • + +<#if inviteData.dialog_mail?? && inviteData.dialog_mail != ""> +
  • +

    邮件:${inviteData.dialog_mail!''}

    +
  • + +<#if inviteData.dialog_mail?? && inviteData.dialog_introduction != ""> +
  • +

    ${(inviteData.dialog_introduction!'')?no_esc}

    +
  • + +
+
+
+ <#if imageAd> + <#if imageAd.adtype =="image"> + + <#else> + + +<#elseif inviteData.dialog_ad??> + + +
+
+
+ +
+<#if sessionConfig?? && sessionConfig.satisfaction?? && sessionConfig.satisfaction> + + + + + + + diff --git a/contact-center/app/src/main/resources/templates/apps/im/chatbot/mobile.html b/contact-center/app/src/main/resources/templates/apps/im/chatbot/mobile.html new file mode 100644 index 00000000..9f749aab --- /dev/null +++ b/contact-center/app/src/main/resources/templates/apps/im/chatbot/mobile.html @@ -0,0 +1,431 @@ + + + + + + + + 智能客服 + + + + + + + + + + + + + + + + + + + + + + +
+
+ ${(inviteData.dialog_message!'欢迎您来咨询!欢迎使用春松客服!如需帮助请联系 info@chatopera.com')?no_esc} +
+ <#if chatMessageList?? && chatMessageList.content??> + <#list chatMessageList.content?reverse as chatMessage> + <#if chatMessage.userid?? && userid?? && chatMessage.calltype?? && chatMessage.calltype = "呼入"> +
+
+ +
+ +
+
+ +
<#include + "/apps/im/media/message.html">
+
+
+
+ <#else> +
+
+ +
+ +
+
+ +
<#include "/apps/im/media/message.html">
+
+
+
+ +
+ +
+
+ + + + +
+ +
+ + +
+ + +
+ + + + +