package com.cn.service; import com.alibaba.fastjson.JSONObject; import com.cn.model.ChatGroup; import com.cn.model.ServiceAccountRoleDepartmentMiddle; import nl.basjes.shaded.org.springframework.util.CollectionUtils; import org.jim.server.enums.*; import org.apache.commons.lang3.StringUtils; import org.jim.common.ImConst; import org.jim.common.ImStatus; import org.jim.common.cache.redis.JedisTemplate; import org.jim.common.packets.*; import org.jim.server.command.handler.processor.login.LoginCmdProcessor; import org.jim.server.util.SnowflakeIdUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.ChannelContext; import java.util.*; import java.util.stream.Collectors; /** * @author Darren * @date 2020/2/2 11:03 */ public class IMChatLoginServiceProcessor implements LoginCmdProcessor { private static Logger logger = LoggerFactory.getLogger(IMChatLoginServiceProcessor.class); private static JedisTemplate jedisTemplate = null; private static final String GROUP_PROPERTY = " group_id,name,avatar,service_account_role_department_middle_id,consumerId,group_type,company_id,department_id "; static { try { jedisTemplate = JedisTemplate.me(); } catch (Exception e) { logger.info("JedisTemplate初始化失败!"); e.printStackTrace(); } } /** * doLogin方法注意:J-IM登陆命令是根据user是否为空判断是否登陆成功, * 当登陆失败时设置user属性需要为空,相反登陆成功user属性是必须非空的; * @param loginReqBody * @param channelContext * @return */ @Override public LoginRespBody doLogin(LoginReqBody loginReqBody, ChannelContext channelContext) { //获取token String token = loginReqBody.getToken(); LoginRespBody loginRespBody; User user = null; if(StringUtils.isNotBlank(token)){ user = this.getUser(token); } if(user == null){ loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10008); }else{ Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService"); if(noOnlineService != null && noOnlineService){ loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10022,user); }else{ loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10007,user); } } return loginRespBody; } /** * 通过token 获取用户信息 * @param token * @return {@link {@link User}} * @author Darren * @date 2020/2/5 15:34 */ public User getUser(String token) { User user = null; String[] tokenArray = token.split(ImConst.SEPARATOR); if(tokenArray.length == 2){ //判断访问类型 String visitType = tokenArray[0]; Map map = jedisTemplate.hashGetAll(token); if( !CollectionUtils.isEmpty(map) ){ if( VisitTypeEnum.SERVICEACCOUNT.getKey().equals(visitType) ){ user = this.createServiceAccountUser(map); this.disposeServiceAccountData(user); } if( VisitTypeEnum.CUSTOMER.getKey().equals(visitType) ){ //当前访问人为客户 user = this.createCustomerData(map); } if( VisitTypeEnum.VISITOR.getKey().equals(visitType) ){ //当前访问人为游客 user = this.createVisitorData(map); } } } return user; } /** ============================================= <游客> 处理 start================================================== */ /** * 创建 游客 user 数据 * @param map * @return {@link {@link User}} * @author Darren * @date 2020/2/5 15:30 */ private User createVisitorData(Map map) { User user = new User(); user.setId(map.get("visitorDepartmentId")); String headImg = map.get("headImg"); user.setAvatar(headImg); String nickname = map.get("nickname"); user.setNick(nickname); Map visitorMap = new HashMap<>(); visitorMap.put("visitorId",map.get("id")); visitorMap.put("companyId",map.get("companyId")); visitorMap.put("departmentId",map.get("departmentId")); visitorMap.put("phone",map.get("phone")); user.setExtras(new JSONObject(visitorMap)); //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客) user.setVisitType(VisitTypeEnum.VISITOR.getKey()); user.setGroups(this.initVisitorGroups(user)); return user; } /** * 初始化游客 群组数据 * @param user * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/5 15:26 */ private List initVisitorGroups(User user) { return disposeVisitorGroupData(user); } /** * 处理游客群组数据 * @param user * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/5 15:25 */ private List disposeVisitorGroupData(User user){ //获取 当前平台下 部门下 在线的 客服 String serviceAccountContainerKey = user.getExtras().getString("companyId") + ImConst.SEPARATOR + user.getExtras().getString("departmentId") + ImConst.SEPARATOR + VisitTypeEnum.SERVICEACCOUNT.getKey() ; List groups = new ArrayList<>(); //从当前在线的客服中 获取 连接客户数最少的 客服 //value组成 : Map Map serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey); Map.Entry minReceptionNumServiceAccount = this.getServiceAccountMinReceptionNum(serviceAccountContainer); if( null != minReceptionNumServiceAccount ){ //当前平台下部门下 存在客服 user.getExtras().put("serviceAccountRoleDepartmentId",minReceptionNumServiceAccount.getKey()); //创建临时在线群组 当客户 断开连接时 删除群组 Group group = this.createGroup(user); //把 Group 转成 Map 用于 jfianl框架的 添加使用 Map attrs = groupToMap(group); attrs.put("service_account_type", ServiceAccountOfflineTypeEnum.NO.getKey()); //jfianl框架的 保存 ChatGroup.dao._setAttrs(attrs).save(); groups.add(group); //更改 客服正在 服务的 客户/游客 数量 int num = Integer.valueOf(minReceptionNumServiceAccount.getValue()); jedisTemplate.hashSet(serviceAccountContainerKey,minReceptionNumServiceAccount.getKey(),String.valueOf(++num)); }else{ //当前平台下部门下 没有在线客服 user.getExtras().put("noOnlineService",true); } return groups; } /** ============================================= <游客> 处理 end================================================== */ /** ============================================= <客户> 处理 start================================================== */ /** * 创建 客户 user 数据 * @param map * @return {@link {@link User}} * @author Darren * @date 2020/2/4 10:07 */ private User createCustomerData(Map map) { User user = new User(); user.setId(map.get("customerDepartmentId")); String headImg = map.get("headImg"); user.setAvatar(headImg); String nickname = map.get("nickname"); user.setNick(nickname); Map customerMap = new HashMap<>(); customerMap.put("isBlack",map.get("isBlack")); customerMap.put("isClaim",map.get("isClaim")); customerMap.put("customerId",map.get("id")); customerMap.put("companyId",map.get("companyId")); customerMap.put("departmentId",map.get("departmentId")); customerMap.put("serviceAccountId",map.get("serviceAccountId")); customerMap.put("phone",map.get("phone")); customerMap.put("serviceAccountRoleDepartmentId",map.get("serviceAccountRoleDepartmentId")); user.setExtras(new JSONObject(customerMap)); //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客) user.setVisitType(VisitTypeEnum.CUSTOMER.getKey()); user.setGroups(this.initCustomerGroups(user)); return user; } /** * 绑定客户群组 一个客户在一个平台下在一个部门下 只会有一个群组 * @param user * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/4 11:05 */ private List initCustomerGroups(User user) { //获取 当前平台下 部门下 在线的 客服 String serviceAccountContainerKey = user.getExtras().getString("companyId") + ImConst.SEPARATOR + user.getExtras().getString("departmentId") + ImConst.SEPARATOR + VisitTypeEnum.SERVICEACCOUNT.getKey() ; List groups = new ArrayList<>(); //是否认领 1:是 0:否 String isClaim = user.getExtras().getString("isClaim"); //是否加入黑名单 1:是 0:否 String isBlack = user.getExtras().getString("isBlack"); if( CustomerClaimTypeEnum.YES.getKey().equals(isClaim) && BlackListStateEnum.NO.getKey().equals(isBlack) ){ //被认领 并且 没有加入黑名单 String receptionNum = jedisTemplate.hashGet( serviceAccountContainerKey , user.getExtras().getString("serviceAccountRoleDepartmentId") ); //判断当前 认领这个 客户 的客服 是否在线 if( StringUtils.isNotBlank(receptionNum) ){ //在线 //创建临时在线群组 当客户 断开连接时 删除群组 Group group = this.createGroup(user); //把 Group 转成 Map 用于 jfianl框架的 添加使用 Map attrs = groupToMap(group); //客服是否是离线状态 1: 是 0: 否 attrs.put("service_account_type", ServiceAccountOfflineTypeEnum.NO.getKey()); //jfianl框架的 保存 ChatGroup.dao._setAttrs(attrs).save(); groups.add(group); Integer num = Integer.valueOf(receptionNum); //更改客服的服务数量 jedisTemplate.hashSet(serviceAccountContainerKey,user.getExtras().getString("serviceAccountRoleDepartmentId"),String.valueOf(++num)); }else{ //离线 //查询客户 是否有 上一次 留言的离线群组 String sql = createSelectGroupSql(user); ChatGroup chatGroup = ChatGroup.dao.findFirst(sql); if( null != chatGroup ) { // 客户 上一次 留言的离线群组 Group group = this.disposeChatGroupToGroup(chatGroup); groups.add(group); }else{ //创建临时离线群组 当客户 断开连接时 删除群组 Group group = this.createGroup(user); //把 Group 转成 Map 用于 jfianl框架的 添加使用 Map attrs = groupToMap(group); //客服是否是离线状态 1: 是 0: 否 attrs.put("service_account_type", ServiceAccountOfflineTypeEnum.YES.getKey()); //jfianl框架的 保存 ChatGroup.dao._setAttrs(attrs).save(); groups.add(group); } } }else{ //按照游客 方式 处理 找到 在线客服 连接数 最少的 groups.addAll(this.disposeVisitorGroupData(user)); } return groups; } /** * 创建 sw_conversation 数据 * @param group * @param user * @return {@link } * @author Darren * @date 2020/2/6 10:16 */ private void createConversation(Group group,User user) { ServiceAccountRoleDepartmentMiddle dao = ServiceAccountRoleDepartmentMiddle.dao; Map attrs = new HashMap<>(); attrs.put("id", Long.valueOf(group.getGroup_id())); //会话状态 0:游客会话 1:客户会话 群组类型 0:游客-客服 类型 1:客户-客服 类型 attrs.put("state", group.getGroupType()); if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){ attrs.put("user_id", Long.valueOf(user.getExtras().getString("customerId"))); } if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){ attrs.put("user_id", Long.valueOf(user.getExtras().getString("visitorId"))); } attrs.put("user_nickname", user.getNick()); attrs.put("user_phone", user.getExtras().getString("phone")); attrs.put("company_id", Long.valueOf(group.getCompanyId())); attrs.put("department_id", Long.valueOf(group.getDepartmentId())); ServiceAccountRoleDepartmentMiddle pojo = dao.findById(group.getServiceAccountRoleDepartmentId()); attrs.put("service_account_id", Long.valueOf(pojo.get("service_account_id"))); dao._setAttrs(attrs).save(); } /** * 构建查询分组sql * @param user * @return {@link {@link String}} * @author Darren * @date 2020/2/4 20:45 */ public String createSelectGroupSql(User user){ StringBuilder sql = new StringBuilder(); sql.append(" select ") .append(GROUP_PROPERTY) .append(" from sw_group where service_account_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.getExtras().getString("serviceAccountRoleDepartmentId") ) .append(" and consumer_id = " + user.getId()) .append(" order by create_time desc"); return sql.toString(); } /** * 获取容器中 接待数量最少的 客服 * @param serviceAccountContainer * @return {@link {@link Map.Entry< String, String>}} * @author Darren * @date 2020/2/5 10:34 */ private Map.Entry getServiceAccountMinReceptionNum( Map serviceAccountContainer ){ if(!CollectionUtils.isEmpty(serviceAccountContainer)){ List> entries = serviceAccountContainer.entrySet().stream().collect(Collectors.toList()); entries.sort( (o1,o2) -> (Integer.valueOf(o1.getValue()) - Integer.valueOf(o2.getValue())) ); return entries.get(0); } return null; } /** * 把 Group 转成 Map 用于 jfianl框架的 添加使用 * @param group * @return {@link {@link Map< String, Object>}} * @author Darren * @date 2020/2/4 20:30 */ private Map groupToMap(Group group){ Map attrs = new HashMap<>(); attrs.put("group_id", group.getGroup_id()); attrs.put("name", group.getName()); attrs.put("avatar", group.getAvatar()); attrs.put("service_account_role_department_middle_id", group.getServiceAccountRoleDepartmentId()); attrs.put("consumer_id", group.getConsumerId()); attrs.put("group_type", group.getGroupType()); attrs.put("company_id", group.getCompanyId()); attrs.put("department_id", group.getDepartmentId()); return attrs; } /** * 创建群组 * @param user * @return {@link {@link Group}} * @author Darren * @date 2020/2/4 20:28 */ private Group createGroup(User user){ //创建新的群组 Group group = new Group(); group.setGroup_id(String.valueOf(SnowflakeIdUtils.getInstance().nextId())); //群组名称 采用客户名称 group.setName(user.getNick()); group.setAvatar(user.getAvatar()); group.setServiceAccountRoleDepartmentId(user.getExtras().getString("serviceAccountRoleDepartmentId")); group.setConsumerId(user.getId()); //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客) if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){ group.setGroupType(GroupTypeEnum.CUSTOMER.getKey()); } if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){ group.setGroupType(GroupTypeEnum.VISITOR.getKey()); } group.setCompanyId(user.getExtras().getString("companyId")); group.setDepartmentId(user.getExtras().getString("departmentId")); //创建会话 this.createConversation(group,user); return group; } /** ============================================= <客户> 处理 end================================================== */ /** ============================================= 客服处理 start================================================== */ /** * 创建 客服 user 数据 * @param map * @return {@link {@link User}} * @author Darren * @date 2020/2/3 19:59 */ private User createServiceAccountUser(Map map){ User user = new User(); //当前访问人为客服 user.setId(map.get("serviceAccountRoleDepartmentId")); //用户头像 String headImg = map.get("headImg"); user.setAvatar(headImg); //user nick String nickname = map.get("nickname"); user.setNick(nickname); //扩展预留字段; Map serviceAccountMap = new HashMap<>(); serviceAccountMap.put("companyId", map.get("companyId")); serviceAccountMap.put("isDefault", map.get("isDefault")); serviceAccountMap.put("departmentId", map.get("departmentId")); serviceAccountMap.put("hasViewAllServiceAccount", map.get("hasViewAllServiceAccount")); serviceAccountMap.put("serviceAccountId", map.get("id")); user.setExtras(new JSONObject(serviceAccountMap)); //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客) user.setVisitType(VisitTypeEnum.SERVICEACCOUNT.getKey()); user.setGroups(this.initServiceAccountGroups(user)); user.setViewAllGroups(this.initViewAllServiceAccountGroup(user)); return user; } /** * 初始化 查看全部客服权限 才能看的群组数据 * @param user * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/3 20:29 */ private List initViewAllServiceAccountGroup(User user) { List groups = null; //1.判断当前客服是否具有查看当前部门下 全部客服通话的权限 String hasViewAllServiceAccount = user.getExtras().getString("hasViewAllServiceAccount"); if( Boolean.valueOf(hasViewAllServiceAccount) ){ boolean flag = false; //获取 具有查看全部客服通话权限 的客服容器 并且 加入到该容器中 key数据格式: companyId : departmentId : ADMIN String adminContainerKey = user.getExtras().getString("companyId") + ImConst.SEPARATOR + user.getExtras().getString("departmentId") + ImConst.SEPARATOR + ImConst.ADMIN ; List adminContainer = jedisTemplate.listGetAll(adminContainerKey); if( CollectionUtils.isEmpty(adminContainer) ){ //adminContainer 不存在 jedisTemplate.listPushTail(adminContainerKey,user.getId()); flag = true; }else{ if( !adminContainer.contains(user.getId()) ) { //如果容器中 不 包含这个 id 则添加 jedisTemplate.listPushTail(adminContainerKey,user.getId()); flag = true; } } //... 然后把当前 客服 加入到 该平台下 部门下 所有群组中 if(flag){ //1.从redis中获取数据 //获取平台该部门下 所有 群组 key数据格式: companyId : departmentId : GROUP /*String groupContainerKey = user.getExtras().getString("companyId") + SEPARATOR + user.getExtras().getString("departmentId") + SEPARATOR + GROUP ; Map serviceAccountGroupContainer = jedisTemplate.hashGetAll(groupContainerKey); if( !CollectionUtils.isEmpty(serviceAccountGroupContainer) ){ //如果 客服群组容器中 有值 绑定群组 groups = serviceAccountGroupContainer .values() .stream() .map(x -> JsonKit.toBean(x, Group.class)) //过滤掉 属于该客服的群组 .filter(x -> !x.getServiceAccountRoleDepartmentId().equals(user.getId())) .collect(Collectors.toList()); }else{ }*/ //2.从数据库中获取数据 StringBuilder sql = new StringBuilder(); sql .append(" select ") .append(GROUP_PROPERTY) .append(" from sw_group where 1 = 1 ") .append(" and company_id = "+ user.getExtras().getString("companyId") + " ") .append(" and department_id = " + user.getExtras().getString("departmentId") + " "); List chatGroups = ChatGroup.dao.find(sql.toString()); if( !CollectionUtils.isEmpty(chatGroups) ){ chatGroups = chatGroups.stream() .filter( x -> !x.get("service_account_role_department_middle_id").equals(user.getId()) ) .collect(Collectors.toList()); groups = this.disposeChatGroupsToGroups(chatGroups); } } } return groups; } /*** * 初始化 客服对应的 群组数据 * @param user * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/3 19:55 */ private List initServiceAccountGroups(User user) { //1.从redis中获取数据 // 查看当前是否有相关该客服的群组 key数据格式: companyId : departmentId : userId : GROUP /* String serviceAccountGroupContainerKey = user.getExtras().getString("companyId") + SEPARATOR + user.getExtras().getString("departmentId") + SEPARATOR + user.getId() + GROUP ; // value 数据结构 (field 域 : value) : consumerId 游客或者客户的 userID : {group 群组对象json串 } Map serviceAccountGroupContainer = jedisTemplate.hashGetAll(serviceAccountGroupContainerKey); if( !CollectionUtils.isEmpty(serviceAccountGroupContainer) ){ //如果 客服群组容器中 有值 绑定群组 groups = serviceAccountGroupContainer .values() .stream() .map(x -> JsonKit.toBean(x, Group.class)) .collect(Collectors.toList()); }else{ }*/ //2.从数据库中获取数据 StringBuilder sql = new StringBuilder(); sql .append(" select ") .append(GROUP_PROPERTY) // service_account_type = 1 判断 当前的 是否是离线状态下的 //有可能 客服 下线 客户没下线 所以 这个地方的过滤 不用 过滤 service_account_type = 1 //群组 的删除 是依 客户/游客的下线 为准(并且 是在线群组 才会删除,离线群组不会删除) .append(" from sw_group where 1 = 1 and 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() ); List chatGroups = ChatGroup.dao.find(sql.toString()); List groups = this.disposeChatGroupsToGroups(chatGroups); //修改 sw_group 字段service_account_type 状态 if( !CollectionUtils.isEmpty(chatGroups) ){ for (ChatGroup chatGroup : chatGroups) { ChatGroup.dao.findById(chatGroup.getStr("group_id")) .set("service_account_type", ServiceAccountOfflineTypeEnum.NO.getKey()) .update(); } } return groups; } /*** * 处理 访问类型 为客服 的数据 * @param * @return {@link } * @author Darren * @date 2020/2/3 14:01 */ private void disposeServiceAccountData(User user){ //1.向客服容器中 添加客服 客服容器中过滤掉超级管理员 // key组成 : companyId : departmentId : SERVICEACCOUNT // value组成 : Map String serviceAccountContainerKey = user.getExtras().getString("companyId") + ImConst.SEPARATOR + user.getExtras().getString("departmentId") + ImConst.SEPARATOR + VisitTypeEnum.SERVICEACCOUNT.getKey() ; //判断该客服是否是超级管理员 String isDefault = user.getExtras().getString("isDefault"); if(DefaultStateEnum.NO.getKey().equals(isDefault)){ List groups = user.getGroups(); int num = 0; if(!CollectionUtils.isEmpty(groups)){ num = groups.size(); } Map serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey); //判断 客服容器 是否存在 if( CollectionUtils.isEmpty(serviceAccountContainer) ){ //当前平台下 部门下 客服容器为null serviceAccountContainer = new HashMap<>(); serviceAccountContainer.put(user.getId(),String.valueOf(num)); jedisTemplate.hashMultipleSet(serviceAccountContainerKey,serviceAccountContainer); }else{ //客服容器 存在 //判断容器中是否存在这个客服 if(serviceAccountContainer.containsKey(user.getId())){ //获取 当前客服 服务的 客户/游客 数量 Integer userNum = Integer.valueOf(serviceAccountContainer.get(user.getId())) + num; jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf( userNum )); }else{ //不存在这个客服数据 jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf(num)); } } } } /** ============================================= 客服处理 end =================================================== */ /** * 转换数据 List >> List * @param chatGroups * @return {@link {@link List< Group>}} * @author Darren * @date 2020/2/4 14:27 */ private List disposeChatGroupsToGroups(List chatGroups){ if( !CollectionUtils.isEmpty(chatGroups) ){ List groups = new ArrayList<>(); for (ChatGroup chatGroup : chatGroups) { Group group = this.disposeChatGroupToGroup(chatGroup); groups.add(group); } return groups; } return null; } /** * 转换数据 ChatGroup >> Group * @param chatGroup * @author Darren * @date 2020/2/4 14:27 */ private Group disposeChatGroupToGroup(ChatGroup chatGroup){ if( null != chatGroup ){ Group group = new Group(); group.setGroup_id(chatGroup.getStr("group_id")); group.setName(chatGroup.getStr("name")); group.setAvatar(chatGroup.getStr("avatar")); group.setServiceAccountRoleDepartmentId(chatGroup.getStr("service_account_role_department_middle_id")); group.setConsumerId(chatGroup.getStr("consumer_id")); group.setGroupType(chatGroup.getInt("group_type")); group.setCompanyId(chatGroup.getStr("company_id")); group.setDepartmentId(chatGroup.getStr("department_id")); return group; } return null; } @Override public void onSuccess(ChannelContext channelContext) { logger.info("登录成功回调方法..."); } @Override public boolean isProtocol(ChannelContext channelContext) { return true; } /** * 处理器的名称 * @return */ @Override public String name() { return ImConst.IM_CHAT_LOGIN_SERVICE_PROCESSOR; } }