Просмотр исходного кода

新增 客服邀请客户 进入聊天室功能

779513719 5 лет назад
Родитель
Сommit
8f213f8d23

+ 16 - 10
jim-common/src/main/java/org/jim/common/packets/User.java

@@ -53,6 +53,10 @@ public class User implements Serializable{
 	 */
 	private List<Group> viewAllGroups;
 	/**
+	 * 由客服主动发起聊天的群组
+	 */
+	private List<Group> intoChatGroups;
+	/**
 	 * 扩展预留字段;
 	 */
 	private JSONObject extras;
@@ -61,27 +65,30 @@ public class User implements Serializable{
 	 */
 	private String visitType;
 
+	public User(){}
+	public User(String id , String nick){
+		this.id = id;
+		this.nick = nick;
+	}
+
+	public List<Group> getIntoChatGroups() {
+		return intoChatGroups;
+	}
+	public void setIntoChatGroups(List<Group> intoChatGroups) {
+		this.intoChatGroups = intoChatGroups;
+	}
 	public List<Group> getViewAllGroups() {
 		return viewAllGroups;
 	}
-
 	public void setViewAllGroups(List<Group> viewAllGroups) {
 		this.viewAllGroups = viewAllGroups;
 	}
-
 	public String getVisitType() {
 		return visitType;
 	}
-
 	public void setVisitType(String visitType) {
 		this.visitType = visitType;
 	}
-
-	public User(){}
-	public User(String id , String nick){
-		this.id = id;
-		this.nick = nick;
-	}
 	public String getNick() {
 		return nick;
 	}
@@ -94,7 +101,6 @@ public class User implements Serializable{
 	public void setAvatar(String avatar) {
 		this.avatar = avatar;
 	}
-	
 	public List<Group> getGroups() {
 		return groups;
 	}

+ 75 - 28
jim-server/src/main/java/org/jim/server/command/handler/LoginReqHandler.java

@@ -1,5 +1,6 @@
 package org.jim.server.command.handler;
 
+import com.sun.org.apache.xml.internal.security.Init;
 import org.apache.commons.collections4.CollectionUtils;
 import org.jim.common.ImConst;
 import org.jim.common.ImAio;
@@ -102,8 +103,9 @@ public class LoginReqHandler extends AbstractCmdHandler {
 		bindUnbindGroup(channelContext, user, user.getGroups());
 		//判断当前用户 是否具有 查看所有客服权限 如果有 绑定群组
 		hasViewAllServiceAccountAuthority(channelContext,user);
+		//初始化 由客服主动发起聊天的群组
+		initIntoChatGroups(channelContext,user);
 		loginServiceHandler.onSuccess(channelContext);
-
 		if( !VisitTypeEnum.SERVICEACCOUNT.getKey().equals(user.getVisitType()) ){
 			//自动发送客服设置的消息 (在存在群组时 => 相当于客户或游客登录后)
 			sendServiceAccountAutoMsg(user);
@@ -114,6 +116,48 @@ public class LoginReqHandler extends AbstractCmdHandler {
 	}
 
 	/**
+	 * 初始化 由客服主动发起聊天的群组
+	 * @param channelContext 客服通道
+ 	 * @param user 客服
+	 * @return {@link }
+	 * @author Darren
+	 * @date 2020/2/17 11:05
+	 */
+	private void initIntoChatGroups(ChannelContext channelContext, User user) throws Exception {
+		if(VisitTypeEnum.SERVICEACCOUNT.getKey().equals(user.getVisitType())){
+			if( user.getExtras().getBoolean("intoChat") ){
+				List<Group> intoChatGroups = user.getIntoChatGroups();
+				//把客服绑定到群组中
+				this.bindUnbindGroup(channelContext, user, intoChatGroups);
+				//如果客户在线 >> 把客户通道 绑定到群组
+				String customerDepartmentId = user.getExtras().getString("customerDepartmentId");
+				SetWithLock<ChannelContext> channelContexts = ImAio.getChannelContextsByUserId(customerDepartmentId);
+				if( null != channelContexts && channelContexts.size() > 0 ){
+					User customerUser = ImAio.getUser(customerDepartmentId);
+					if( null != customerUser ){
+						//获取锁
+						ReadLock readLock = channelContexts.getLock().readLock();
+						//加锁
+						readLock.lock();
+						try {
+							Set<ChannelContext> channels = channelContexts.getObj();
+							for (ChannelContext channel : channels) {
+								//为客户 绑定 群组
+								this.bindUnbindGroup(channel, customerUser, intoChatGroups);
+							}
+						} catch (Exception e) {
+							log.error(e.toString(),e);
+						} finally {
+							//解锁
+							readLock.unlock();
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
 	 * 自动发送客服设置的消息 (在存在群组时 => 相当于客户或游客登录后)
 	 * @param user
 	 * @return {@link }
@@ -125,9 +169,11 @@ public class LoginReqHandler extends AbstractCmdHandler {
 		ImServerGroupContext groupContext = (ImServerGroupContext)imConfig.getGroupContext();
 		IMAutoMsgQueueRunnable iMAutoMsgQueueRunnable = new IMAutoMsgQueueRunnable(groupContext.getTimExecutor());
 		List<Group> groups = user.getGroups();
-		for (Group group : groups) {
-			iMAutoMsgQueueRunnable.addMsg(group);
-			iMAutoMsgQueueRunnable.getExecutor().execute(iMAutoMsgQueueRunnable);
+		if( CollectionUtils.isNotEmpty(groups) ){
+			for (Group group : groups) {
+				iMAutoMsgQueueRunnable.addMsg(group);
+				iMAutoMsgQueueRunnable.getExecutor().execute(iMAutoMsgQueueRunnable);
+			}
 		}
 	}
 
@@ -147,33 +193,34 @@ public class LoginReqHandler extends AbstractCmdHandler {
 					+ ImConst.SEPARATOR + ImConst.ADMIN ;
 			// list 中存放的是 具有权限的 客服 userId ( == serviceAccountRoleDepartmentId)
 			List<String> adminContainer = jedisTemplate.listGetAll(adminContainerKey);
-			for(String serviceAccountRoleDepartmentId : adminContainer){
-				//获取 客服 所在 的通道
-				SetWithLock<ChannelContext> channelContexts = ImAio.getChannelContextsByUserId(serviceAccountRoleDepartmentId);
-				if( null != channelContexts && channelContexts.size() > 0 ){
-					//获取读锁
-					ReadLock readLock = channelContexts.getLock().readLock();
-					//加锁
-					readLock.lock();
-					try {
-						Set<ChannelContext> channels = channelContexts.getObj();
-						for (ChannelContext channelContext : channels) {
-							//判断 通道 是否已经加入了 群组
-							if( !Aio.isInGroup(group.getGroup_id(),channelContext) ){
-								//该通道未加入该群组则 加入
-								//获取加入群组 处理器
-								JoinGroupReqHandler joinGroupReqHandler = CommandManager.getCommand(Command.COMMAND_JOIN_GROUP_REQ, JoinGroupReqHandler.class);
-								joinGroupReqHandler.bindGroup(groupPacket, channelContext);
+			if( CollectionUtils.isNotEmpty(adminContainer) ){
+				for(String serviceAccountRoleDepartmentId : adminContainer){
+					//获取 客服 所在 的通道
+					SetWithLock<ChannelContext> channelContexts = ImAio.getChannelContextsByUserId(serviceAccountRoleDepartmentId);
+					if( null != channelContexts && channelContexts.size() > 0 ){
+						//获取读锁
+						ReadLock readLock = channelContexts.getLock().readLock();
+						//加锁
+						readLock.lock();
+						try {
+							Set<ChannelContext> channels = channelContexts.getObj();
+							for (ChannelContext channelContext : channels) {
+								//判断 通道 是否已经加入了 群组
+								if( !Aio.isInGroup(group.getGroup_id(),channelContext) ){
+									//该通道未加入该群组则 加入
+									//获取加入群组 处理器
+									JoinGroupReqHandler joinGroupReqHandler = CommandManager.getCommand(Command.COMMAND_JOIN_GROUP_REQ, JoinGroupReqHandler.class);
+									joinGroupReqHandler.bindGroup(groupPacket, channelContext);
+								}
 							}
+						} catch (Exception e) {
+							log.error(e.toString(),e);
+						}finally {
+							//解锁
+							readLock.unlock();
 						}
-					} catch (Exception e) {
-						log.error(e.toString(),e);
-					}finally {
-						//解锁
-						readLock.unlock();
 					}
 				}
-
 			}
 		}
 	}
@@ -198,7 +245,7 @@ public class LoginReqHandler extends AbstractCmdHandler {
 	 */
 	public void bindUnbindGroup(ChannelContext channelContext ,  User user, List<Group> groups)throws Exception{
 		String userId = user.getId();
-		if( groups != null){
+		if( CollectionUtils.isNotEmpty(groups) ){
 			//判断是否开启持久化
 			boolean isStore = ImConst.ON.equals(imConfig.getIsStore());
 			MessageHelper messageHelper = null;

+ 181 - 6
server-chat/src/main/java/com/cn/service/IMChatLoginServiceProcessor.java

@@ -1,7 +1,11 @@
 package com.cn.service;
 
 import com.alibaba.fastjson.JSONObject;
+import org.jim.common.ImAio;
+import org.jim.common.ImConfig;
+import org.jim.common.message.MessageHelper;
 import org.jim.common.utils.JsonKit;
+import org.jim.server.command.CommandManager;
 import org.jim.server.model.*;
 import nl.basjes.shaded.org.springframework.util.CollectionUtils;
 import org.jim.server.enums.*;
@@ -15,6 +19,7 @@ import org.jim.server.util.SnowflakeIdUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.tio.core.ChannelContext;
+import org.tio.utils.lock.SetWithLock;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -29,15 +34,18 @@ public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
 
     private static JedisTemplate jedisTemplate = null;
 
+    protected static ImConfig imConfig;
+
     private static final String GROUP_PROPERTY = " * ";
 
     private static final String CURRENT_SYSTEM_ALL_USER_ID = "CURRENT_SYSTEM_ALL_USER_ID";
 
     static {
         try {
+            imConfig = CommandManager.getImConfig();
             jedisTemplate = JedisTemplate.me();
         } catch (Exception e) {
-            logger.info("JedisTemplate初始化失败!");
+            logger.info("JedisTemplate 和 imConfig 初始化失败!");
             e.printStackTrace();
         }
     }
@@ -335,12 +343,11 @@ public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
             String sql = this.createSelectGroupSql(user);
             ChatGroup chatGroup = ChatGroup.dao.findFirst(sql);
             if( null != chatGroup){
-                //判断  (之前群组 是否是留言群组 且 客服是离线状态)
+                //判断  (之前群组 是否是留言群组 )  >>> 现在 处理的是 客户登录的处理逻辑
                 if(
                     MessageStateEnum.YES.getKey() == chatGroup.getInt("message_state")
-                            && ServiceAccountOfflineTypeEnum.OFF_LINE.getKey() == chatGroup.getInt("service_account_type")
                 ){
-                    //如果 之前群组 客户是否 在线 且 群组是否 在线 或 之前群组 是留言群组 则不重新创建会话
+                    // 群组 是留言群组 则不重新创建会话
                     user.getExtras().put("isCreateConversation",false);
                 }
                 chatGroup
@@ -371,13 +378,13 @@ public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
                             .update();
                 }
                 //之前 客服 和 客户 之间 有群组
-                Group group = disposeChatGroupToGroup(chatGroup);
+                Group group = this.disposeChatGroupToGroup(chatGroup);
                 groups.add(group);
             }else{
                 // 创建新的群组
                 Group group = this.createGroup(user);
                 //把 Group 转成 Map 用于 jfianl框架的 添加使用
-                Map<String, Object> groupAttrs = groupToMap(group);
+                Map<String, Object> groupAttrs = this.groupToMap(group);
                 groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
                 groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
                 //判断当前 认领这个 客户 的客服 是否在线
@@ -564,15 +571,179 @@ public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
         serviceAccountMap.put("departmentId", map.get("departmentId"));
         serviceAccountMap.put("hasViewAllServiceAccount", map.get("hasViewAllServiceAccount"));
         serviceAccountMap.put("serviceAccountId", map.get("id"));
+        serviceAccountMap.put("intoChat", map.get("intoChat"));
+        if(Boolean.valueOf(map.get("intoChat"))){
+            serviceAccountMap.put("customerDepartmentId", map.get("customerDepartmentId"));
+            serviceAccountMap.put("customerNickname", map.get("customerNickname"));
+            serviceAccountMap.put("customerHeadImg", map.get("customerHeadImg"));
+            serviceAccountMap.put("customerPhone", map.get("customerPhone"));
+            serviceAccountMap.put("customerId", map.get("customerId"));
+        }
         user.setExtras(new JSONObject(serviceAccountMap));
         //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
         user.setVisitType(VisitTypeEnum.SERVICEACCOUNT.getKey());
         user.setGroups(this.initServiceAccountGroups(user));
         user.setViewAllGroups(this.initViewAllServiceAccountGroup(user));
+        user.setIntoChatGroups(this.initIntoChatGroups(user));
         return user;
     }
 
     /**
+     * 初始化 由客服主动发起聊天的群组
+     * @param user
+     * @return {@link {@link List< Group>}}
+     * @author Darren
+     * @date 2020/2/17 9:43
+     */
+    private List<Group> initIntoChatGroups(User user) {
+        List<Group> groups = new ArrayList<>();
+        //是否创建 会话
+        user.getExtras().put("isCreateConversation",true);
+        if( user.getExtras().getBoolean("intoChat") ){
+            String customerDepartmentId = user.getExtras().getString("customerDepartmentId");
+            StringBuilder sql = new StringBuilder();
+            sql.append(" select ")
+                    .append(GROUP_PROPERTY)
+                    .append(" from sw_group where group_type = 1 ")
+                    .append(" and company_id = " + user.getExtras().getString("companyId"))
+                    .append(" and department_id = " + user.getExtras().getString("departmentId"))
+                    .append(
+                            " and service_account_role_department_middle_id = " + user.getId()
+                    )
+                    .append(" and consumer_id = " + customerDepartmentId )
+                    .append(" order by create_time desc");
+            ChatGroup chatGroup = ChatGroup.dao.findFirst(sql.toString());
+            //customer 是否在线 如果不在线就是 留言群组
+            boolean isOnline = isOnline(customerDepartmentId);
+            if(null != chatGroup){
+                //说明 当前存在 群组
+                if(
+                        MessageStateEnum.YES.getKey() == chatGroup.getInt("message_state") ||
+                                GroupStateEnum.ON_LINE.getKey() == chatGroup.getInt("group_state")
+                ){
+                    // 群组 是留言群组 或 群组为在线状态 则不重新创建会话
+                    user.getExtras().put("isCreateConversation",false);
+                }
+                chatGroup
+                        // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
+                        .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
+                        .update();
+                if(isOnline){
+                    chatGroup
+                            // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
+                            .set("group_state",GroupStateEnum.ON_LINE.getKey())
+                            // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
+                            .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
+                            // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
+                            .set("message_state",MessageStateEnum.NO.getKey())
+                            .update();
+                }else{
+                    chatGroup
+                            // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
+                            .set("group_state",GroupStateEnum.OFF_LINE.getKey())
+                            // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
+                            .set("consumer_type",ConsumerTypeEnum.OFF_LINE.getKey())
+                            // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
+                            .set("message_state",MessageStateEnum.YES.getKey())
+                            .update();
+                }
+                Group group = disposeChatGroupToGroup(chatGroup);
+                groups.add(group);
+            }else{
+                //创建新的群组
+                Group group = this.createIntoChatGroup(user);
+                Map<String, Object> groupAttrs = this.groupToMap(group);
+                groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
+                if(isOnline){
+                    groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
+                    groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
+                    groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
+                }else{
+                    groupAttrs.put("group_state", GroupStateEnum.OFF_LINE.getKey());
+                    groupAttrs.put("consumer_type", ConsumerTypeEnum.OFF_LINE.getKey());
+                    groupAttrs.put("message_state", MessageStateEnum.YES.getKey());
+                }
+                //jfianl框架的 保存
+                ChatGroup.dao._setAttrs(groupAttrs).save();
+                groups.add(group);
+            }
+        }
+        if( user.getExtras().getBoolean("isCreateConversation")){
+            this.createIntoChatConversation(groups.get(0),user);
+        }
+        return groups;
+    }
+
+    /**
+     * 创建客服邀请客户进入聊天的会话
+     * @param group
+     * @param user
+     * @return {@link }
+     * @author Darren
+     * @date 2020/2/17 14:55
+     */
+    private void createIntoChatConversation(Group group, User user){
+        Map<String, Object> attrs = new HashMap<>();
+        Long conversationId = SnowflakeIdUtils.getInstance().nextId();
+        attrs.put("id", conversationId );
+        //会话状态 0:游客会话 1:客户会话  群组类型 0:游客-客服 类型 1:客户-客服 类型
+        attrs.put("state", group.getGroupType());
+        attrs.put("user_id", user.getExtras().getString("customerId"));
+        attrs.put("user_nickname", user.getExtras().getString("customerNickname"));
+        attrs.put("user_phone", user.getExtras().getString("customerPhone"));
+        attrs.put("company_id", Long.valueOf(group.getCompanyId()));
+        attrs.put("department_id", Long.valueOf(group.getDepartmentId()));
+        ServiceAccountRoleDepartmentMiddle pojo
+                = ServiceAccountRoleDepartmentMiddle.dao.findById(group.getServiceAccountRoleDepartmentId());
+        attrs.put("service_account_id", pojo.getLong("service_account_id"));
+        Conversation.dao._setAttrs(attrs).save();
+        // 添加 sw_group_conversation_middle 数据
+        GroupConversationMiddle.dao
+                .set("id",SnowflakeIdUtils.getInstance().nextId())
+                .set("group_id",group.getGroup_id())
+                .set("conversation_id",conversationId)
+                .save();
+    }
+
+    /**
+     * 创建客服邀请客户 加入聊天群组
+     * @param user
+     * @return {@link {@link Group}}
+     * @author Darren
+     * @date 2020/2/17 10:43
+     */
+    private Group createIntoChatGroup(User user){
+        Group group = new Group();
+        group.setGroup_id(String.valueOf(SnowflakeIdUtils.getInstance().nextId()));
+        //群组名称 采用客户名称
+        group.setName(user.getExtras().getString("customerNickname"));
+        group.setAvatar(user.getExtras().getString("customerHeadImg"));
+        group.setServiceAccountRoleDepartmentId(user.getId());
+        group.setConsumerId(user.getExtras().getString("customerDepartmentId"));
+        group.setGroupType(GroupTypeEnum.CUSTOMER.getKey());
+        group.setCompanyId(user.getExtras().getString("companyId"));
+        group.setDepartmentId(user.getExtras().getString("departmentId"));
+        return group;
+    }
+
+    /**
+     * 判断用户是否在线
+     * @param customerDepartmentId
+     * @return {@link {@link boolean}}
+     * @author Darren
+     * @date 2020/2/17 10:03
+     */
+    private boolean isOnline(String customerDepartmentId){
+        if(ImConst.ON.equals(imConfig.getIsStore())){
+            MessageHelper messageHelper = imConfig.getMessageHelper();
+            return messageHelper.isOnline(customerDepartmentId);
+        }else{
+            SetWithLock<ChannelContext> channelContexts = ImAio.getChannelContextsByUserId(customerDepartmentId);
+            return (channelContexts != null && channelContexts.size() > 0);
+        }
+    }
+
+    /**
      * 初始化 查看全部客服权限 才能看的群组数据
      * @param user
      * @return {@link {@link List< Group>}}
@@ -744,6 +915,10 @@ public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
             if(!CollectionUtils.isEmpty(groups)){
                 num = groups.size();
             }
+            List<Group> intoChatGroups = user.getIntoChatGroups();
+            if(!CollectionUtils.isEmpty(groups)){
+                num += intoChatGroups.size();
+            }
             Map<String, String> serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey);
             //判断 客服容器 是否存在
             if( CollectionUtils.isEmpty(serviceAccountContainer) ){