IMChatLoginServiceProcessor.java 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. package com.cn.service;
  2. import com.alibaba.fastjson.JSONObject;
  3. import org.jim.server.model.*;
  4. import nl.basjes.shaded.org.springframework.util.CollectionUtils;
  5. import org.jim.server.enums.*;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.jim.common.ImConst;
  8. import org.jim.common.ImStatus;
  9. import org.jim.common.cache.redis.JedisTemplate;
  10. import org.jim.common.packets.*;
  11. import org.jim.server.command.handler.processor.login.LoginCmdProcessor;
  12. import org.jim.server.util.SnowflakeIdUtils;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. import org.tio.core.ChannelContext;
  16. import java.util.*;
  17. import java.util.stream.Collectors;
  18. /**
  19. * @author Darren
  20. * @date 2020/2/2 11:03
  21. */
  22. public class IMChatLoginServiceProcessor implements LoginCmdProcessor {
  23. private static Logger logger = LoggerFactory.getLogger(IMChatLoginServiceProcessor.class);
  24. private static JedisTemplate jedisTemplate = null;
  25. private static final String GROUP_PROPERTY = " group_id,name,avatar,service_account_role_department_middle_id,consumer_id,group_type,company_id,department_id ";
  26. static {
  27. try {
  28. jedisTemplate = JedisTemplate.me();
  29. } catch (Exception e) {
  30. logger.info("JedisTemplate初始化失败!");
  31. e.printStackTrace();
  32. }
  33. }
  34. /**
  35. * doLogin方法注意:J-IM登陆命令是根据user是否为空判断是否登陆成功,
  36. * 当登陆失败时设置user属性需要为空,相反登陆成功user属性是必须非空的;
  37. * @param loginReqBody
  38. * @param channelContext
  39. * @return
  40. */
  41. @Override
  42. public LoginRespBody doLogin(LoginReqBody loginReqBody, ChannelContext channelContext) {
  43. logger.info("执行登录处理器doLogin()方法...");
  44. //获取token
  45. String token = loginReqBody.getToken();
  46. LoginRespBody loginRespBody;
  47. User user = null;
  48. if(StringUtils.isNotBlank(token)){
  49. user = this.getUser(token);
  50. }
  51. if(user == null){
  52. loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10008);
  53. }else{
  54. Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
  55. if(noOnlineService != null && noOnlineService){
  56. loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10022,user);
  57. }else{
  58. loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10007,user);
  59. }
  60. }
  61. return loginRespBody;
  62. }
  63. /**
  64. * 通过token 获取用户信息
  65. * @param token
  66. * @return {@link {@link User}}
  67. * @author Darren
  68. * @date 2020/2/5 15:34
  69. */
  70. public User getUser(String token) {
  71. User user = null;
  72. String[] tokenArray = token.split(ImConst.SEPARATOR);
  73. if(tokenArray.length == 2){
  74. //判断访问类型
  75. String visitType = tokenArray[0];
  76. Map<String, String> map = jedisTemplate.hashGetAll(token);
  77. if( !CollectionUtils.isEmpty(map) ){
  78. if( VisitTypeEnum.SERVICEACCOUNT.getKey().equals(visitType) ){
  79. user = this.createServiceAccountUser(map);
  80. this.disposeServiceAccountData(user);
  81. }
  82. if( VisitTypeEnum.CUSTOMER.getKey().equals(visitType) ){
  83. //当前访问人为客户
  84. user = this.createCustomerData(map);
  85. }
  86. if( VisitTypeEnum.VISITOR.getKey().equals(visitType) ){
  87. //当前访问人为游客
  88. user = this.createVisitorData(map);
  89. //修改游客在线状态
  90. Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
  91. if(!noOnlineService){
  92. VisitorDepartmentMiddle.dao.findById(user.getId()).set("online",0).update();
  93. }
  94. }
  95. }
  96. }
  97. return user;
  98. }
  99. /** ============================================= <游客> 处理 start================================================== */
  100. /**
  101. * 创建 游客 user 数据
  102. * @param map
  103. * @return {@link {@link User}}
  104. * @author Darren
  105. * @date 2020/2/5 15:30
  106. */
  107. private User createVisitorData(Map<String, String> map) {
  108. User user = new User();
  109. user.setId(map.get("visitorDepartmentId"));
  110. String headImg = map.get("headImg");
  111. user.setAvatar(headImg);
  112. String nickname = map.get("nickname");
  113. user.setNick(nickname);
  114. Map<String, Object> visitorMap = new HashMap<>();
  115. visitorMap.put("visitorId",map.get("id"));
  116. visitorMap.put("companyId",map.get("companyId"));
  117. visitorMap.put("departmentId",map.get("departmentId"));
  118. visitorMap.put("phone",map.get("phone"));
  119. user.setExtras(new JSONObject(visitorMap));
  120. //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
  121. user.setVisitType(VisitTypeEnum.VISITOR.getKey());
  122. user.setGroups(this.initVisitorGroups(user));
  123. return user;
  124. }
  125. /**
  126. * 初始化游客 群组数据
  127. * @param user
  128. * @return {@link {@link List< Group>}}
  129. * @author Darren
  130. * @date 2020/2/5 15:26
  131. */
  132. private List<Group> initVisitorGroups(User user) {
  133. List<Group> groups = disposeVisitorGroupData(user);
  134. Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
  135. if(!noOnlineService){
  136. this.createConversation(groups.get(0),user);
  137. }
  138. return groups;
  139. }
  140. /**
  141. * 处理游客群组数据
  142. * 自动分配原则,根据上次服务状态,优先分配上次服务客服,
  143. * 若上次服务客服不在线或不存在上次服务客服状况,
  144. * 则优先根据当前在线客服服务人数最少的客服进行分配,和服务上限无关;
  145. * @param user
  146. * @return {@link {@link List< Group>}}
  147. * @author Darren
  148. * @date 2020/2/5 15:25
  149. */
  150. private List<Group> disposeVisitorGroupData(User user){
  151. List<Group> groups = new ArrayList<>();
  152. //获取 当前平台下 部门下 所有的在线的客服
  153. String serviceAccountContainerKey
  154. = user.getExtras().getString("companyId") + ImConst.SEPARATOR
  155. + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
  156. + VisitTypeEnum.SERVICEACCOUNT.getKey() ;
  157. //value组成 : Map<String ( serviceAccountRoleDepartmentId ), String (当前连接客户人数)>
  158. Map<String, String> serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey);
  159. StringBuilder sql1 = new StringBuilder();
  160. sql1.append(" select ")
  161. .append(GROUP_PROPERTY)
  162. .append(" from sw_group where 1 = 1 ")
  163. .append(" and company_id = " + user.getExtras().getString("companyId"))
  164. .append(" and department_id = " + user.getExtras().getString("departmentId"))
  165. .append(" and consumer_id = " + user.getId())
  166. .append(" order by create_time desc");
  167. //判断 游客在平台下部门下 是否有群组 且判断 游客对应的客服 是否在线
  168. ChatGroup chatGroup1 = ChatGroup.dao.findFirst(sql1.toString());
  169. boolean isOnline = false;
  170. if( null != chatGroup1 ){
  171. String accountRoleDepartmentId = chatGroup1.getStr("service_account_role_department_middle_id");
  172. //游客对应客服存在 且 在线; isOnline=true;
  173. isOnline = serviceAccountContainer.containsKey(accountRoleDepartmentId);
  174. }
  175. user.getExtras().put("noOnlineService",false);
  176. if(!isOnline){
  177. // 从当前在线的客服中 获取 连接客户数最少的 客服
  178. Map.Entry<String, String> minReceptionNumServiceAccount = this.getServiceAccountMinReceptionNum(serviceAccountContainer);
  179. if( null != minReceptionNumServiceAccount ){
  180. //当前平台下部门下 存在客服
  181. user.getExtras().put("serviceAccountRoleDepartmentId",minReceptionNumServiceAccount.getKey());
  182. String sql2 = this.createSelectGroupSql(user);
  183. ChatGroup chatGroup2 = ChatGroup.dao.findFirst(sql2);
  184. if( null != chatGroup2){
  185. chatGroup2
  186. // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
  187. .set("group_state",GroupStateEnum.ON_LINE.getKey())
  188. // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
  189. .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
  190. // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
  191. .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
  192. // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
  193. .set("message_state",MessageStateEnum.NO.getKey())
  194. .update();
  195. //之前 客服 和 客户/游客 之间 有群组
  196. Group group = disposeChatGroupToGroup(chatGroup2);
  197. groups.add(group);
  198. }else{
  199. //创建群组
  200. Group group = this.createGroup(user);
  201. //把 Group 转成 Map 用于 jfianl框架的 添加使用
  202. Map<String, Object> groupAttrs = groupToMap(group);
  203. groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
  204. groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
  205. //客服是否是离线状态 1: 是 0: 否
  206. groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
  207. //群组留言状态 1:留言 0:非留言
  208. groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
  209. //jfianl框架的 保存
  210. ChatGroup.dao._setAttrs(groupAttrs).save();
  211. groups.add(group);
  212. }
  213. //更改 客服正在 服务的 客户/游客 数量
  214. int num = Integer.valueOf(minReceptionNumServiceAccount.getValue());
  215. jedisTemplate.hashSet(serviceAccountContainerKey,minReceptionNumServiceAccount.getKey(),String.valueOf(++num));
  216. }else{
  217. //当前平台下部门下 没有在线客服
  218. user.getExtras().put("noOnlineService",true);
  219. }
  220. }else{
  221. Group group = disposeChatGroupToGroup(chatGroup1);
  222. groups.add(group);
  223. chatGroup1
  224. // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
  225. .set("group_state",GroupStateEnum.ON_LINE.getKey())
  226. // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
  227. .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
  228. // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
  229. .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
  230. // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
  231. .set("message_state",MessageStateEnum.NO.getKey())
  232. .update();
  233. }
  234. return groups;
  235. }
  236. /** ============================================= <游客> 处理 end================================================== */
  237. /** ============================================= <客户> 处理 start================================================== */
  238. /**
  239. * 创建 客户 user 数据
  240. * @param map
  241. * @return {@link {@link User}}
  242. * @author Darren
  243. * @date 2020/2/4 10:07
  244. */
  245. private User createCustomerData(Map<String, String> map) {
  246. User user = new User();
  247. user.setId(map.get("customerDepartmentId"));
  248. String headImg = map.get("headImg");
  249. user.setAvatar(headImg);
  250. String nickname = map.get("nickname");
  251. user.setNick(nickname);
  252. Map<String, Object> customerMap = new HashMap<>();
  253. customerMap.put("isBlack",map.get("isBlack"));
  254. customerMap.put("isClaim",map.get("isClaim"));
  255. customerMap.put("customerId",map.get("id"));
  256. customerMap.put("companyId",map.get("companyId"));
  257. customerMap.put("departmentId",map.get("departmentId"));
  258. customerMap.put("serviceAccountId",map.get("serviceAccountId"));
  259. customerMap.put("phone",map.get("phone"));
  260. customerMap.put("serviceAccountRoleDepartmentId",map.get("serviceAccountRoleDepartmentId"));
  261. user.setExtras(new JSONObject(customerMap));
  262. //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
  263. user.setVisitType(VisitTypeEnum.CUSTOMER.getKey());
  264. user.setGroups(this.initCustomerGroups(user));
  265. return user;
  266. }
  267. /**
  268. * 绑定客户群组 一个客户在一个平台下在一个部门下 只会有一个群组
  269. * @param user
  270. * @return {@link {@link List< Group>}}
  271. * @author Darren
  272. * @date 2020/2/4 11:05
  273. */
  274. private List<Group> initCustomerGroups(User user) {
  275. //获取 当前平台下 部门下 在线的 客服
  276. String serviceAccountContainerKey
  277. = user.getExtras().getString("companyId") + ImConst.SEPARATOR
  278. + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
  279. + VisitTypeEnum.SERVICEACCOUNT.getKey() ;
  280. //是否创建 会话
  281. user.getExtras().put("isCreateConversation",true);
  282. List<Group> groups = new ArrayList<>();
  283. // isClaim: 是否认领 1:是 0:否; isBlack: 是否加入黑名单 1:是 0:否;
  284. if(
  285. CustomerClaimTypeEnum.YES.getKey().equals(user.getExtras().getString("isClaim")) &&
  286. BlackListStateEnum.NO.getKey().equals(user.getExtras().getString("isBlack"))
  287. ){
  288. //被认领 并且 没有加入黑名单
  289. String receptionNum
  290. = jedisTemplate.hashGet(
  291. serviceAccountContainerKey
  292. , user.getExtras().getString("serviceAccountRoleDepartmentId")
  293. );
  294. String sql = this.createSelectGroupSql(user);
  295. ChatGroup chatGroup = ChatGroup.dao.findFirst(sql);
  296. if( null != chatGroup){
  297. //判断 (之前群组 是否是留言群组 且 客服是离线状态)
  298. if(
  299. MessageStateEnum.YES.getKey() == chatGroup.getInt("message_state")
  300. && ServiceAccountOfflineTypeEnum.OFF_LINE.getKey() == chatGroup.getInt("service_account_type")
  301. ){
  302. //如果 之前群组 客户是否 在线 且 群组是否 在线 或 之前群组 是留言群组 则不重新创建会话
  303. user.getExtras().put("isCreateConversation",false);
  304. }
  305. chatGroup
  306. // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
  307. .set("group_state",GroupStateEnum.ON_LINE.getKey())
  308. // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
  309. .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
  310. .update();
  311. //判断当前 认领这个 客户 的客服 是否在线
  312. if( StringUtils.isNotBlank(receptionNum) ){
  313. //在线
  314. chatGroup
  315. // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
  316. .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
  317. // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
  318. .set("message_state",MessageStateEnum.NO.getKey())
  319. .update();
  320. //更改客服的服务数量
  321. Integer num = Integer.valueOf(receptionNum);
  322. jedisTemplate.hashSet(serviceAccountContainerKey,user.getExtras().getString("serviceAccountRoleDepartmentId"),String.valueOf(++num));
  323. }else{
  324. //离线
  325. chatGroup
  326. // 更改 sw_group 表中 service_account_type 字段为 离线 (客服离线状态 1: 离线 0: 在线)
  327. .set("service_account_type",ServiceAccountOfflineTypeEnum.OFF_LINE.getKey())
  328. // 更改 sw_group 表中 message_state 字段为 留言 (群组留言状态 1:留言 0:非留言)
  329. .set("message_state",MessageStateEnum.YES.getKey())
  330. .update();
  331. }
  332. //之前 客服 和 客户 之间 有群组
  333. Group group = disposeChatGroupToGroup(chatGroup);
  334. groups.add(group);
  335. }else{
  336. // 创建新的群组
  337. Group group = this.createGroup(user);
  338. //把 Group 转成 Map 用于 jfianl框架的 添加使用
  339. Map<String, Object> groupAttrs = groupToMap(group);
  340. groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
  341. groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
  342. //判断当前 认领这个 客户 的客服 是否在线
  343. if( StringUtils.isNotBlank(receptionNum) ){
  344. //在线
  345. //更改客服的服务数量
  346. Integer num = Integer.valueOf(receptionNum);
  347. jedisTemplate.hashSet(serviceAccountContainerKey,user.getExtras().getString("serviceAccountRoleDepartmentId"),String.valueOf(++num));
  348. //客服是否是离线状态 1: 是 0: 否
  349. groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
  350. //群组留言状态 1:留言 0:非留言
  351. groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
  352. }else{
  353. //离线
  354. //客服是否是离线状态 1: 是 0: 否
  355. groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.OFF_LINE.getKey());
  356. //群组留言状态 1:留言 0:非留言
  357. groupAttrs.put("message_state", MessageStateEnum.YES.getKey());
  358. }
  359. //jfianl框架的 保存
  360. ChatGroup.dao._setAttrs(groupAttrs).save();
  361. groups.add(group);
  362. }
  363. }else{
  364. //按照游客 方式 处理 找到 在线客服 连接数 最少的
  365. groups.addAll(this.disposeVisitorGroupData(user));
  366. }
  367. if( user.getExtras().getBoolean("isCreateConversation")){
  368. Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
  369. if(!noOnlineService){
  370. this.createConversation(groups.get(0),user);
  371. }
  372. }
  373. return groups;
  374. }
  375. /**
  376. * 创建 sw_conversation 数据
  377. * @param group
  378. * @param user
  379. * @return {@link }
  380. * @author Darren
  381. * @date 2020/2/6 10:16
  382. */
  383. private void createConversation(Group group,User user) {
  384. Map<String, Object> attrs = new HashMap<>();
  385. Long conversationId = SnowflakeIdUtils.getInstance().nextId();
  386. attrs.put("id", conversationId );
  387. //会话状态 0:游客会话 1:客户会话 群组类型 0:游客-客服 类型 1:客户-客服 类型
  388. attrs.put("state", group.getGroupType());
  389. if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){
  390. attrs.put("user_id", Long.valueOf(user.getExtras().getString("customerId")));
  391. }
  392. if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){
  393. attrs.put("user_id", Long.valueOf(user.getExtras().getString("visitorId")));
  394. }
  395. attrs.put("user_nickname", user.getNick());
  396. attrs.put("user_phone", user.getExtras().getString("phone"));
  397. attrs.put("company_id", Long.valueOf(group.getCompanyId()));
  398. attrs.put("department_id", Long.valueOf(group.getDepartmentId()));
  399. ServiceAccountRoleDepartmentMiddle pojo
  400. = ServiceAccountRoleDepartmentMiddle.dao.findById(group.getServiceAccountRoleDepartmentId());
  401. attrs.put("service_account_id", pojo.getLong("service_account_id"));
  402. Conversation.dao._setAttrs(attrs).save();
  403. // 添加 sw_group_conversation_middle 数据
  404. GroupConversationMiddle.dao
  405. .set("id",SnowflakeIdUtils.getInstance().nextId())
  406. .set("group_id",group.getGroup_id())
  407. .set("conversation_id",conversationId)
  408. .save();
  409. }
  410. /**
  411. * 构建查询分组sql
  412. * @param user
  413. * @return {@link {@link String}}
  414. * @author Darren
  415. * @date 2020/2/4 20:45
  416. */
  417. private String createSelectGroupSql(User user){
  418. StringBuilder sql = new StringBuilder();
  419. sql.append(" select ")
  420. .append(GROUP_PROPERTY)
  421. .append(" from sw_group where 1 = 1 ")
  422. .append(" and company_id = " + user.getExtras().getString("companyId"))
  423. .append(" and department_id = " + user.getExtras().getString("departmentId"))
  424. .append(
  425. " and service_account_role_department_middle_id = "
  426. + user.getExtras().getString("serviceAccountRoleDepartmentId")
  427. )
  428. .append(" and consumer_id = " + user.getId())
  429. .append(" order by create_time desc");
  430. return sql.toString();
  431. }
  432. /**
  433. * 获取容器中 接待数量最少的 客服
  434. * @param serviceAccountContainer
  435. * @return {@link {@link Map.Entry< String, String>}}
  436. * @author Darren
  437. * @date 2020/2/5 10:34
  438. */
  439. private Map.Entry<String,String> getServiceAccountMinReceptionNum( Map<String, String> serviceAccountContainer ){
  440. if(!CollectionUtils.isEmpty(serviceAccountContainer)){
  441. List<Map.Entry<String, String>> entries = serviceAccountContainer.entrySet().stream().collect(Collectors.toList());
  442. entries.sort( (o1,o2) -> (Integer.valueOf(o1.getValue()) - Integer.valueOf(o2.getValue())) );
  443. return entries.get(0);
  444. }
  445. return null;
  446. }
  447. /**
  448. * 把 Group 转成 Map 用于 jfianl框架的 添加使用
  449. * @param group
  450. * @return {@link {@link Map< String, Object>}}
  451. * @author Darren
  452. * @date 2020/2/4 20:30
  453. */
  454. private Map<String, Object> groupToMap(Group group){
  455. Map<String, Object> attrs = new HashMap<>();
  456. attrs.put("group_id", group.getGroup_id());
  457. attrs.put("name", group.getName());
  458. attrs.put("avatar", group.getAvatar());
  459. attrs.put("service_account_role_department_middle_id", group.getServiceAccountRoleDepartmentId());
  460. attrs.put("consumer_id", group.getConsumerId());
  461. attrs.put("group_type", group.getGroupType());
  462. attrs.put("company_id", group.getCompanyId());
  463. attrs.put("department_id", group.getDepartmentId());
  464. return attrs;
  465. }
  466. /**
  467. * 创建群组
  468. * @param user
  469. * @return {@link {@link Group}}
  470. * @author Darren
  471. * @date 2020/2/4 20:28
  472. */
  473. private Group createGroup(User user){
  474. //创建新的群组
  475. Group group = new Group();
  476. group.setGroup_id(String.valueOf(SnowflakeIdUtils.getInstance().nextId()));
  477. //群组名称 采用客户名称
  478. group.setName(user.getNick());
  479. group.setAvatar(user.getAvatar());
  480. group.setServiceAccountRoleDepartmentId(user.getExtras().getString("serviceAccountRoleDepartmentId"));
  481. group.setConsumerId(user.getId());
  482. //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
  483. if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){
  484. group.setGroupType(GroupTypeEnum.CUSTOMER.getKey());
  485. }
  486. if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){
  487. group.setGroupType(GroupTypeEnum.VISITOR.getKey());
  488. }
  489. group.setCompanyId(user.getExtras().getString("companyId"));
  490. group.setDepartmentId(user.getExtras().getString("departmentId"));
  491. return group;
  492. }
  493. /** ============================================= <客户> 处理 end================================================== */
  494. /** ============================================= 客服处理 start================================================== */
  495. /**
  496. * 创建 客服 user 数据
  497. * @param map
  498. * @return {@link {@link User}}
  499. * @author Darren
  500. * @date 2020/2/3 19:59
  501. */
  502. private User createServiceAccountUser(Map<String, String> map){
  503. User user = new User();
  504. //当前访问人为客服
  505. user.setId(map.get("serviceAccountRoleDepartmentId"));
  506. //用户头像
  507. user.setAvatar(map.get("headImg"));
  508. //user nick
  509. user.setNick( map.get("nickname"));
  510. //扩展预留字段;
  511. Map<String, Object> serviceAccountMap = new HashMap<>();
  512. serviceAccountMap.put("companyId", map.get("companyId"));
  513. serviceAccountMap.put("isDefault", map.get("isDefault"));
  514. serviceAccountMap.put("departmentId", map.get("departmentId"));
  515. serviceAccountMap.put("hasViewAllServiceAccount", map.get("hasViewAllServiceAccount"));
  516. serviceAccountMap.put("serviceAccountId", map.get("id"));
  517. user.setExtras(new JSONObject(serviceAccountMap));
  518. //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
  519. user.setVisitType(VisitTypeEnum.SERVICEACCOUNT.getKey());
  520. user.setGroups(this.initServiceAccountGroups(user));
  521. user.setViewAllGroups(this.initViewAllServiceAccountGroup(user));
  522. return user;
  523. }
  524. /**
  525. * 初始化 查看全部客服权限 才能看的群组数据
  526. * @param user
  527. * @return {@link {@link List< Group>}}
  528. * @author Darren
  529. * @date 2020/2/3 20:29
  530. */
  531. private List<Group> initViewAllServiceAccountGroup(User user) {
  532. List<Group> groups = new ArrayList<>();
  533. //1.判断当前客服是否具有查看当前部门下 全部客服通话的权限
  534. if( Boolean.valueOf(user.getExtras().getString("hasViewAllServiceAccount")) ){
  535. boolean flag = false;
  536. //获取 具有查看全部客服通话权限 的客服容器 并且 加入到该容器中 key数据格式: companyId : departmentId : ADMIN
  537. String adminContainerKey = user.getExtras().getString("companyId")
  538. + ImConst.SEPARATOR + user.getExtras().getString("departmentId")
  539. + ImConst.SEPARATOR + ImConst.ADMIN ;
  540. List<String> adminContainer = jedisTemplate.listGetAll(adminContainerKey);
  541. if( CollectionUtils.isEmpty(adminContainer) ){
  542. //adminContainer 不存在
  543. jedisTemplate.listPushTail(adminContainerKey,user.getId());
  544. flag = true;
  545. }else{
  546. if( !adminContainer.contains(user.getId()) ) {
  547. //如果容器中 不 包含这个 id 则添加
  548. jedisTemplate.listPushTail(adminContainerKey,user.getId());
  549. flag = true;
  550. }
  551. }
  552. //... 然后把当前 客服 加入到 该平台下 部门下 所有群组中
  553. if(flag){
  554. //从数据库中获取数据
  555. //1.查询所有留言群组
  556. StringBuilder sql1 = new StringBuilder();
  557. sql1
  558. .append(" select ")
  559. .append(GROUP_PROPERTY)
  560. //message_state: 群组留言状态 1:留言 0:非留言
  561. //group_type: 群组类型 0:游客-客服 类型 1:客户-客服 类型
  562. .append(" from sw_group where group_type = 1 and message_state = 1 ")
  563. .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
  564. .append(" and department_id = " + user.getExtras().getString("departmentId") + " ");
  565. List<ChatGroup> messageChatGroups = ChatGroup.dao.find(sql1.toString());
  566. if( !CollectionUtils.isEmpty(messageChatGroups) ){
  567. messageChatGroups = messageChatGroups.stream()
  568. .filter(
  569. x -> !x.get("service_account_role_department_middle_id").equals(user.getId())
  570. )
  571. .collect(Collectors.toList());
  572. List<Group> messageGroups = this.disposeChatGroupsToGroups(messageChatGroups);
  573. if( !CollectionUtils.isEmpty(messageGroups) ){
  574. groups.addAll(messageGroups);
  575. }
  576. }
  577. //2.查询所有在线群组
  578. StringBuilder sql2 = new StringBuilder();
  579. sql2
  580. .append(" select ")
  581. .append(GROUP_PROPERTY)
  582. //查看当前平台下 部门下 的所有在线群组
  583. //group_state: 群组离线状态 1: 离线 0: 在线
  584. //consumer_type: 客户/游客 离线状态 1:离线 0:在线
  585. //message_state: 群组留言状态 1:留言 0:非留言
  586. .append(" from sw_group where group_state = 0 and consumer_type = 0 and message_state = 0 ")
  587. .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
  588. .append(" and department_id = " + user.getExtras().getString("departmentId") + " ");
  589. List<ChatGroup> onlineChatGroups = ChatGroup.dao.find(sql2.toString());
  590. if( !CollectionUtils.isEmpty(onlineChatGroups) ){
  591. onlineChatGroups = onlineChatGroups.stream()
  592. .filter(
  593. x -> !x.get("service_account_role_department_middle_id").equals(user.getId())
  594. )
  595. .collect(Collectors.toList());
  596. List<Group> onlineGroups = this.disposeChatGroupsToGroups(onlineChatGroups);
  597. if( !CollectionUtils.isEmpty(onlineGroups) ){
  598. groups.addAll(onlineGroups);
  599. }
  600. }
  601. }
  602. }
  603. return groups;
  604. }
  605. /***
  606. * 初始化 客服对应的 群组数据
  607. * @param user
  608. * @return {@link {@link List< Group>}}
  609. * @author Darren
  610. * @date 2020/2/3 19:55
  611. */
  612. private List<Group> initServiceAccountGroups(User user) {
  613. List<ChatGroup> chatGroups = new ArrayList<>();
  614. //从数据库中获取数据
  615. //2.查询 客户/游客 对应平台下 对应部门下 在线群组状态 和 客户/游客在线状态 和 群组非留言状态
  616. StringBuilder sql2 = new StringBuilder();
  617. sql2
  618. .append(" select ")
  619. .append(GROUP_PROPERTY)
  620. //message_state: 群组留言状态 1:留言 0:非留言
  621. //consumer_type: 客户/游客 离线状态 1:离线 0:在线
  622. //group_state: 群组离线状态 1: 离线 0: 在线
  623. .append(" from sw_group where group_state = 0 and consumer_type = 0 and message_state = 0")
  624. .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
  625. .append(" and department_id = " + user.getExtras().getString("departmentId") + " ")
  626. .append(
  627. " and service_account_role_department_middle_id = " + user.getId()
  628. );
  629. List<ChatGroup> onlineChatGroups = ChatGroup.dao.find(sql2.toString());
  630. if( !CollectionUtils.isEmpty(onlineChatGroups) ){
  631. chatGroups.addAll(onlineChatGroups);
  632. //修改客服离线状态 为 在线
  633. for (ChatGroup chatGroup : onlineChatGroups) {
  634. chatGroup.set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
  635. .update();
  636. }
  637. }
  638. //1.查询 对应平台下 对应部门下 留言群组
  639. StringBuilder sql1 = new StringBuilder();
  640. sql1
  641. .append(" select ")
  642. .append(GROUP_PROPERTY)
  643. //message_state: 群组留言状态 1:留言 0:非留言
  644. //group_type: 群组类型 0:游客-客服 类型 1:客户-客服 类型
  645. .append(" from sw_group where group_type = 1 and message_state = 1 ")
  646. .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
  647. .append(" and department_id = " + user.getExtras().getString("departmentId") + " ")
  648. .append(
  649. " and service_account_role_department_middle_id = " + user.getId()
  650. );
  651. List<ChatGroup> messageChatGroups = ChatGroup.dao.find(sql1.toString());
  652. if( !CollectionUtils.isEmpty(messageChatGroups) ){
  653. chatGroups.addAll(messageChatGroups);
  654. //修改 sw_group 表中的 message_state 状态; 群组留言状态 1:留言 0:非留言
  655. //修改 sw_group 表中的 service_account_type 状态; 客服离线状态 1: 离线 0: 在线
  656. for (ChatGroup chatGroup : messageChatGroups) {
  657. chatGroup .set("message_state", MessageStateEnum.NO.getKey())
  658. .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
  659. .update();
  660. }
  661. }
  662. return this.disposeChatGroupsToGroups(chatGroups);
  663. }
  664. /***
  665. * 处理 访问类型 为客服 的数据
  666. * @param
  667. * @return {@link }
  668. * @author Darren
  669. * @date 2020/2/3 14:01
  670. */
  671. private void disposeServiceAccountData(User user){
  672. //1.向客服容器中 添加客服 客服容器中过滤掉超级管理员
  673. // key组成 : companyId : departmentId : SERVICEACCOUNT
  674. // value组成 : Map<String ( serviceAccountRoleDepartmentId ), String (当前连接客户人数)>
  675. String serviceAccountContainerKey
  676. = user.getExtras().getString("companyId") + ImConst.SEPARATOR
  677. + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
  678. + VisitTypeEnum.SERVICEACCOUNT.getKey() ;
  679. //判断该客服是否是超级管理员
  680. String isDefault = user.getExtras().getString("isDefault");
  681. if(DefaultStateEnum.NO.getKey().equals(isDefault)){
  682. List<Group> groups = user.getGroups();
  683. int num = 0;
  684. if(!CollectionUtils.isEmpty(groups)){
  685. num = groups.size();
  686. }
  687. Map<String, String> serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey);
  688. //判断 客服容器 是否存在
  689. if( CollectionUtils.isEmpty(serviceAccountContainer) ){
  690. //当前平台下 部门下 客服容器为null
  691. serviceAccountContainer = new HashMap<>();
  692. serviceAccountContainer.put(user.getId(),String.valueOf(num));
  693. jedisTemplate.hashMultipleSet(serviceAccountContainerKey,serviceAccountContainer);
  694. }else{
  695. //客服容器 存在
  696. //判断容器中是否存在这个客服
  697. if(serviceAccountContainer.containsKey(user.getId())){
  698. //获取 当前客服 服务的 客户/游客 数量
  699. Integer userNum = Integer.valueOf(serviceAccountContainer.get(user.getId())) + num;
  700. jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf( userNum ));
  701. }else{
  702. //不存在这个客服数据
  703. jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf(num));
  704. }
  705. }
  706. }
  707. }
  708. /** ============================================= 客服处理 end =================================================== */
  709. /**
  710. * 转换数据 List<ChatGroup> >> List<Group>
  711. * @param chatGroups
  712. * @return {@link {@link List< Group>}}
  713. * @author Darren
  714. * @date 2020/2/4 14:27
  715. */
  716. private List<Group> disposeChatGroupsToGroups(List<ChatGroup> chatGroups){
  717. if( !CollectionUtils.isEmpty(chatGroups) ){
  718. List<Group> groups = new ArrayList<>();
  719. for (ChatGroup chatGroup : chatGroups) {
  720. Group group = this.disposeChatGroupToGroup(chatGroup);
  721. groups.add(group);
  722. }
  723. return groups;
  724. }
  725. return null;
  726. }
  727. /**
  728. * 转换数据 ChatGroup >> Group
  729. * @param chatGroup
  730. * @author Darren
  731. * @date 2020/2/4 14:27
  732. */
  733. private Group disposeChatGroupToGroup(ChatGroup chatGroup){
  734. if( null != chatGroup ){
  735. Group group = new Group();
  736. group.setGroup_id(chatGroup.getStr("group_id"));
  737. group.setName(chatGroup.getStr("name"));
  738. group.setAvatar(chatGroup.getStr("avatar"));
  739. group.setServiceAccountRoleDepartmentId(chatGroup.getStr("service_account_role_department_middle_id"));
  740. group.setConsumerId(chatGroup.getStr("consumer_id"));
  741. group.setGroupType(chatGroup.getInt("group_type"));
  742. group.setCompanyId(chatGroup.getStr("company_id"));
  743. group.setDepartmentId(chatGroup.getStr("department_id"));
  744. return group;
  745. }
  746. return null;
  747. }
  748. @Override
  749. public void onSuccess(ChannelContext channelContext) {
  750. logger.info("登录成功回调方法...");
  751. }
  752. @Override
  753. public boolean isProtocol(ChannelContext channelContext) {
  754. return true;
  755. }
  756. /**
  757. * 处理器的名称
  758. * @return
  759. */
  760. @Override
  761. public String name() {
  762. return ImConst.IM_CHAT_LOGIN_SERVICE_PROCESSOR;
  763. }
  764. }