diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDAgentService.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDAgentService.java index 4e5847e4..ea3e43f6 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDAgentService.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDAgentService.java @@ -76,7 +76,7 @@ public class ACDAgentService { } try { - agentService = acdAgentAllocatorMw.processAgentService(agentStatus, agentUser, orgi, false, sessionConfig); + agentService = acdAgentAllocatorMw.processAgentService(agentStatus, agentUser, orgi, false); // 处理结果:进入排队队列 if (StringUtils.equals(MainContext.AgentUserStatusEnum.INQUENE.toString(), agentService.getStatus())) { agentService.setQueneindex( diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDPolicyService.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDPolicyService.java index fcc9b83c..b110ceb0 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDPolicyService.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDPolicyService.java @@ -18,6 +18,7 @@ package com.chatopera.cc.acd; import com.chatopera.cc.basic.MainContext; import com.chatopera.cc.cache.Cache; +import com.chatopera.cc.model.AgentStatus; import com.chatopera.cc.model.SessionConfig; import com.chatopera.cc.persistence.repository.SessionConfigRepository; import org.slf4j.Logger; @@ -75,4 +76,44 @@ public class ACDPolicyService { } return sessionConfig; } + + /** + * 确定AgentStatus:空闲坐席优先 + * + * @param agentStatuses + * @return + */ + public AgentStatus decideAgentStatusWithIdleAgent(final List agentStatuses) { + for (final AgentStatus o : agentStatuses) { + if (o.getUsers() == 0) { + logger.info("[decideAgentStatusWithIdleAgent] choose agentno {} by idle status.", o.getAgentno()); + return o; + } + } + return null; + } + + /** + * 确定AgentStatus:坐席平均分配 + * + * @param agentStatuses + * @return + */ + public AgentStatus decideAgentStatusInAverage(final List agentStatuses) { + // 查找最少人数的AgentStatus + AgentStatus x = null; + int min = 0; + for (final AgentStatus o : agentStatuses) { + if (o.getUsers() <= min) { + x = o; + min = o.getUsers(); + } + } + + if (x != null) { + logger.info("[decideAgentStatusWithIdleAgent] choose agentno {} in average.", x.getAgentno()); + } + + return x; + } } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDServiceRouter.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDServiceRouter.java index eae4c57e..8dab33d9 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDServiceRouter.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/ACDServiceRouter.java @@ -255,7 +255,7 @@ public class ACDServiceRouter { // 下面开始处理其加入到服务中的队列 try { AgentService agentService = acdVisAllocatorMw.processAgentService( - agentStatus, agentUser, orgi, false, sessionConfig); + agentStatus, agentUser, orgi, false); // 处理完成得到 agentService Message outMessage = new Message(); @@ -328,7 +328,7 @@ public class ACDServiceRouter { } else if (agentStatus != null) { // 该访客没有和坐席对话,因此没有 AgentService // 当做留言处理,创建一个新的 AgentService - service = acdVisAllocatorMw.processAgentService(agentStatus, agentUser, orgi, true, sessionConfig); + service = acdVisAllocatorMw.processAgentService(agentStatus, agentUser, orgi, true); } if (service != null) { @@ -504,8 +504,7 @@ public class ACDServiceRouter { AgentStatus agentStatus = cache.findOneAgentStatusByAgentnoAndOrig(agentno, orgi); AgentService agentService; if (agentStatus != null) { - SessionConfig sessionConfig = acdPolicyService.initSessionConfig(orgi); - agentService = acdVisAllocatorMw.processAgentService(agentStatus, agentUser, orgi, false, sessionConfig); + agentService = acdVisAllocatorMw.processAgentService(agentStatus, agentUser, orgi, false); agentUserProxy.broadcastAgentsStatus(orgi, "invite", "success", agentno); /** diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisAllocatorMw.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisAllocatorMw.java index 718d0d7f..395f32de 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisAllocatorMw.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisAllocatorMw.java @@ -17,7 +17,7 @@ package com.chatopera.cc.acd.visitor; import com.chatopera.cc.acd.ACDComposeContext; -import com.chatopera.cc.acd.ACDQueueService; +import com.chatopera.cc.acd.ACDPolicyService; import com.chatopera.cc.basic.MainContext; import com.chatopera.cc.basic.MainUtils; import com.chatopera.cc.cache.Cache; @@ -27,6 +27,7 @@ import com.chatopera.cc.persistence.repository.AgentUserRepository; import com.chatopera.cc.persistence.repository.OnlineUserRepository; import com.chatopera.cc.persistence.repository.UserRepository; import com.chatopera.cc.proxy.AgentUserProxy; +import com.chatopera.cc.util.HashMapUtils; import com.chatopera.cc.util.WebIMReport; import com.chatopera.compose4j.Functional; import com.chatopera.compose4j.Middleware; @@ -48,9 +49,6 @@ public class ACDVisAllocatorMw implements Middleware { @Autowired private Cache cache; - @Autowired - private ACDQueueService acdQueueService; - @Autowired private AgentServiceRepository agentServiceRes; @@ -66,54 +64,127 @@ public class ACDVisAllocatorMw implements Middleware { @Autowired private AgentUserProxy agentUserProxy; + @Autowired + private ACDPolicyService acdPolicyService; + @Override public void apply(final ACDComposeContext ctx, final Functional next) { /** * 查询条件,当前在线的 坐席,并且 未达到最大 服务人数的坐席 */ - List agentStatuses = filterOutAvailableAgentStatus( + final List agentStatuses = filterOutAvailableAgentStatus( ctx.getAgentUser(), ctx.getOrgi()); + /** * 处理ACD 的 技能组请求和 坐席请求 */ - AgentStatus agentStatus = null; - AgentService agentService = null; //放入缓存的对象 - - if (agentStatuses.size() > 0) { - agentStatus = agentStatuses.get(0); - if (agentStatus.getUsers() >= ctx.getSessionConfig().getMaxuser()) { - agentStatus = null; - /** - * 判断当前有多少人排队中 , 分三种情况:1、请求技能组的,2、请求坐席的,3,默认请求的 - * - */ - } - } + AgentStatus agentStatus = filterOutAgentStatusWithPolicies( + ctx.getSessionConfig(), agentStatuses, ctx.getOrgi(), ctx.getOnlineUserId(), ctx.isInvite()); + AgentService agentService = null; try { agentService = processAgentService( - agentStatus, ctx.getAgentUser(), ctx.getOrgi(), false, ctx.getSessionConfig()); - // 处理结果:进入排队队列 - if (StringUtils.equals(MainContext.AgentUserStatusEnum.INQUENE.toString(), agentService.getStatus())) { - agentService.setQueneindex( - acdQueueService.getQueueIndex( - ctx.getAgentUser().getAgentno(), ctx.getOrgi(), ctx.getAgentUser().getSkill())); - } + agentStatus, ctx.getAgentUser(), ctx.getOrgi(), false); } catch (Exception ex) { logger.warn("[allotAgent] exception: ", ex); } - agentUserProxy.broadcastAgentsStatus( - ctx.getOrgi(), "user", agentService != null && agentService.getStatus().equals( - MainContext.AgentUserStatusEnum.INSERVICE.toString()) ? "inservice" : "inquene", - ctx.getAgentUser().getId()); ctx.setAgentService(agentService); - } /** - * 过滤在线客服 + * 根据坐席配置的策略输出符合要求的AgentStatus,确定最终的坐席 + * + * @param sessionConfig + * @param agentStatuses + * @return + */ + private AgentStatus filterOutAgentStatusWithPolicies( + final SessionConfig sessionConfig, + final List agentStatuses, + final String orgi, + final String onlineUserId, + final boolean isInvite) { + AgentStatus agentStatus = null; + + // 邀请功能 + if (isInvite) { + logger.info("[filterOutAgentStatusWithPolicies] is invited onlineUser."); + if (agentStatuses.size() == 1) { + agentStatus = agentStatuses.get(0); + // Note: 如何该邀请人离线了,恰巧只有一个其它就绪坐席,也会进入这种条件。 + logger.info( + "[filterOutAgentStatusWithPolicies] resolve agent as the invitee {}.", + agentStatus.getAgentno()); + } + // 邀请功能,但是agentStatuses大小不是1,则进入后续决策 + } + + // 启用历史坐席优先 + if ((agentStatus == null) && sessionConfig.isLastagent()) { + logger.info("[filterOutAgentStatusWithPolicies] check agent against chat history."); + // 启用了历史坐席优先 , 查找 历史服务坐席 + List webIMaggs = MainUtils.getWebIMDataAgg( + onlineUserRes.findByOrgiForDistinctAgent(orgi, onlineUserId)); + for (WebIMReport report : webIMaggs) { + for (final AgentStatus o : agentStatuses) { + if (StringUtils.equals( + o.getAgentno(), report.getData()) && o.getUsers() < sessionConfig.getMaxuser()) { + logger.info( + "[filterOutAgentStatusWithPolicies] choose agentno {} by chat history.", + agentStatus.getAgentno()); + agentStatus = o; + break; + } + } + + if (agentStatus != null) { + break; + } + } + } + + // 新客服接入人工坐席分配策略 + if (agentStatus == null) { + // 设置默认为空闲坐席优先 + if (StringUtils.isBlank(sessionConfig.getDistribution())) { + sessionConfig.setDistribution("0"); + } + + switch (sessionConfig.getDistribution()) { + case "0": + // 空闲坐席优先 + agentStatus = acdPolicyService.decideAgentStatusWithIdleAgent(agentStatuses); + if (agentStatus == null) { + // 如果没有空闲坐席,则按照平均分配 + agentStatus = acdPolicyService.decideAgentStatusInAverage(agentStatuses); + } + break; + case "1": + // 坐席平均分配 + agentStatus = acdPolicyService.decideAgentStatusInAverage(agentStatuses); + break; + default: + logger.warn( + "[filterOutAgentStatusWithPolicies] unexpected Distribution Strategy 【{}】", + sessionConfig.getDistribution()); + } + } + + if (agentStatus != null) { + logger.info( + "[filterOutAgentStatusWithPolicies] final agentStatus {}, agentno {}", agentStatus.getId(), + agentStatus.getAgentno()); + } else { + logger.info("[filterOutAgentStatusWithPolicies] oops, no agent satisfy rules."); + } + + return agentStatus; + } + + /** + * 过滤就绪坐席 * 优先级: 1. 指定坐席;2. 指定技能组; 3. 租户所有的坐席 * * @param agentUser @@ -125,47 +196,99 @@ public class ACDVisAllocatorMw implements Middleware { final String orgi ) { logger.info( - "[filterOutAvailableAgentStatus] agentUser {}, orgi {}, skill {}, onlineUser {}", + "[filterOutAvailableAgentStatus] pre-conditions: agentUser.agentno {}, orgi {}, skill {}, onlineUser {}", agentUser.getAgentno(), orgi, agentUser.getSkill(), agentUser.getUserid() ); List agentStatuses = new ArrayList<>(); Map map = cache.findAllReadyAgentStatusByOrgi(orgi); + // DEBUG + if (map.size() > 0) { + StringBuffer sb = new StringBuffer(); + sb.append("[filterOutAvailableAgentStatus] ready agents online: \n"); + for (final Map.Entry f : map.entrySet()) { + sb.append( + String.format(" name %s, agentno %s, service %d/%d, status %s, busy %s, skills %s \n", + f.getValue().getUsername(), + f.getValue().getAgentno(), f.getValue().getUsers(), f.getValue().getMaxusers(), + f.getValue().getStatus(), f.getValue().isBusy(), + HashMapUtils.concatKeys(f.getValue().getSkills(), "|"))); + } + logger.info(sb.toString()); + } else { + logger.info("[filterOutAvailableAgentStatus] None ready agent found."); + } + if (agentUser != null && StringUtils.isNotBlank(agentUser.getAgentno())) { // 指定坐席 - for (Map.Entry entry : map.entrySet()) { - if ((!entry.getValue().isBusy()) && (StringUtils.equals( - entry.getValue().getAgentno(), agentUser.getAgentno()))) { + for (final Map.Entry entry : map.entrySet()) { + if (StringUtils.equals( + entry.getValue().getAgentno(), agentUser.getAgentno())) { agentStatuses.add(entry.getValue()); + logger.info( + "[filterOutAvailableAgentStatus] find ready agent {}, name {}, status {}, service {}/{}", + entry.getValue().getAgentno(), entry.getValue().getUsername(), entry.getValue().getStatus(), + entry.getValue().getUsers(), + entry.getValue().getMaxusers()); + break; } } } + // 此处size是1或0 + if (agentStatuses.size() == 1) { + logger.info("[filterOutAvailableAgentStatus] agent status list size: {}", agentStatuses.size()); + // 得到指定的坐席 + return agentStatuses; + } + + // Note 如果指定了坐席,但是该坐席却不是就绪的,那么就根据技能组或其它条件查找 + /** * 指定坐席未查询到就绪的 */ - if (agentStatuses.size() == 0) { - if (StringUtils.isNotBlank(agentUser.getSkill())) { - // 指定技能组 - for (Map.Entry entry : map.entrySet()) { - if ((!entry.getValue().isBusy()) && - (entry.getValue().getSkills() != null && - entry.getValue().getSkills().containsKey(agentUser.getSkill()))) { - agentStatuses.add(entry.getValue()); - } + if (StringUtils.isNotBlank(agentUser.getSkill())) { + // 指定技能组 + for (final Map.Entry entry : map.entrySet()) { + if ((!entry.getValue().isBusy()) && + (entry.getValue().getSkills() != null && + entry.getValue().getSkills().containsKey(agentUser.getSkill()))) { + logger.info( + "[filterOutAvailableAgentStatus] find ready agent {}, name {}, status {}, service {}/{}, skills {}", + agentUser.getSkill(), + entry.getValue().getAgentno(), entry.getValue().getUsername(), entry.getValue().getStatus(), + entry.getValue().getUsers(), + entry.getValue().getMaxusers(), + HashMapUtils.concatKeys(entry.getValue().getSkills(), "|")); + agentStatuses.add(entry.getValue()); + } else { + logger.info( + "[filterOutAvailableAgentStatus] skip ready agent {}, name {}, status {}, service {}/{}, skills {}", + agentUser.getSkill(), + entry.getValue().getAgentno(), entry.getValue().getUsername(), entry.getValue().getStatus(), + entry.getValue().getUsers(), + entry.getValue().getMaxusers(), + HashMapUtils.concatKeys(entry.getValue().getSkills(), "|")); } } - } - - /** - * 在指定的坐席和技能组中未查到坐席 - * 接下来进行无差别查询 - */ - if (agentStatuses.size() == 0) { + // 如果绑定了技能组,立即返回该技能组的人 + // 这时候,如果该技能组没有人,也不按照其它条件查找 + logger.info("[filterOutAvailableAgentStatus] agent status list size: {}", agentStatuses.size()); + return agentStatuses; + } else { + /** + * 在指定的坐席和技能组中未查到坐席 + * 接下来进行无差别查询 + */ // 对于该租户的所有客服 - for (Map.Entry entry : map.entrySet()) { + for (final Map.Entry entry : map.entrySet()) { if (!entry.getValue().isBusy()) { agentStatuses.add(entry.getValue()); + logger.info( + "[filterOutAvailableAgentStatus] find ready agent {}, agentname {}, status {}, service {}/{}", + entry.getValue().getAgentno(), entry.getValue().getUsername(), entry.getValue().getStatus(), + entry.getValue().getUsers(), + entry.getValue().getMaxusers()); } } } @@ -174,7 +297,6 @@ public class ACDVisAllocatorMw implements Middleware { return agentStatuses; } - /** * 为agentUser生成对应的AgentService * 使用场景: @@ -192,8 +314,7 @@ public class ACDVisAllocatorMw implements Middleware { AgentStatus agentStatus, final AgentUser agentUser, final String orgi, - final boolean finished, - final SessionConfig sessionConfig) { + final boolean finished) { AgentService agentService = new AgentService(); if (StringUtils.isNotBlank(agentUser.getAgentserviceid())) { agentService.setId(agentUser.getAgentserviceid()); @@ -228,33 +349,9 @@ public class ACDVisAllocatorMw implements Middleware { } else if (agentStatus != null) { agentService.setAgent(agentStatus.getAgentno()); agentService.setSkill(agentUser.getSkill()); - - if (sessionConfig.isLastagent()) { - // 启用了历史坐席优先 , 查找 历史服务坐席 - List webIMaggList = MainUtils.getWebIMDataAgg( - onlineUserRes.findByOrgiForDistinctAgent(orgi, agentUser.getUserid())); - if (webIMaggList.size() > 0) { - for (WebIMReport report : webIMaggList) { - if (report.getData().equals(agentStatus.getAgentno())) { - break; - } else { - AgentStatus hisAgentStatus = cache.findOneAgentStatusByAgentnoAndOrig( - report.getData(), orgi); - if (hisAgentStatus != null && hisAgentStatus.getUsers() < hisAgentStatus.getMaxusers()) { - // 变更为 历史服务坐席 - agentStatus = hisAgentStatus; - break; - } - } - - } - } - } - agentUser.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString()); agentService.setStatus(MainContext.AgentUserStatusEnum.INSERVICE.toString()); agentService.setSessiontype(MainContext.AgentUserStatusEnum.INSERVICE.toString()); - // 设置坐席名字 agentService.setAgentno(agentStatus.getUserid()); agentService.setAgentusername(agentStatus.getUsername()); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisBodyParserMw.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisBodyParserMw.java index 66db451f..3f1dee49 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisBodyParserMw.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisBodyParserMw.java @@ -17,12 +17,16 @@ package com.chatopera.cc.acd.visitor; import com.chatopera.cc.acd.ACDComposeContext; +import com.chatopera.cc.acd.ACDMessageHelper; +import com.chatopera.cc.acd.ACDQueueService; +import com.chatopera.cc.basic.MainContext; import com.chatopera.cc.cache.Cache; import com.chatopera.cc.model.AgentUser; import com.chatopera.cc.model.AgentUserContacts; import com.chatopera.cc.model.Contacts; import com.chatopera.cc.persistence.es.ContactsRepository; import com.chatopera.cc.persistence.repository.AgentUserContactsRepository; +import com.chatopera.cc.proxy.AgentUserProxy; import com.chatopera.compose4j.Functional; import com.chatopera.compose4j.Middleware; import org.apache.commons.lang.StringUtils; @@ -47,6 +51,15 @@ public class ACDVisBodyParserMw implements Middleware { @Autowired private Cache cache; + @Autowired + private AgentUserProxy agentUserProxy; + + @Autowired + private ACDQueueService acdQueueService; + + @Autowired + private ACDMessageHelper acdMessageHelper; + /** * 设置AgentUser基本信息 * @@ -104,13 +117,91 @@ public class ACDVisBodyParserMw implements Middleware { agentUser.setTitle(ctx.getTitle()); agentUser.setUrl(ctx.getUrl()); agentUser.setTraceid(ctx.getTraceid()); - ctx.setAgentUser(agentUser); + next.apply(); + /** + * 发送通知 + */ + if (ctx.getAgentService() != null && StringUtils.isNotBlank(ctx.getAgentService().getStatus())) { + /** + * 找到空闲坐席,如果未找到坐席,则将该用户放入到 排队队列 + */ + switch (MainContext.AgentUserStatusEnum.toValue(ctx.getAgentService().getStatus())) { + case INSERVICE: + ctx.setMessage( + acdMessageHelper.getSuccessMessage( + ctx.getAgentService(), + ctx.getChannel(), + ctx.getOrgi())); + + // TODO 判断 INSERVICE 时,agentService 对应的 agentUser + logger.info( + "[apply] agent service: agentno {}, \n agentuser id {} \n user {} \n channel {} \n status {} \n queue index {}", + ctx.getAgentService().getAgentno(), ctx.getAgentService().getAgentuserid(), + ctx.getAgentService().getUserid(), + ctx.getAgentService().getChannel(), + ctx.getAgentService().getStatus(), + ctx.getAgentService().getQueneindex()); + + if (StringUtils.isNotBlank(ctx.getAgentService().getAgentuserid())) { + agentUserProxy.findOne(ctx.getAgentService().getAgentuserid()).ifPresent(p -> { + ctx.setAgentUser(p); + }); + } + + // TODO 如果是 INSERVICE 那么 agentService.getAgentuserid 就一定不能为空? +// // TODO 此处需要考虑 agentService.getAgentuserid 为空的情况 +// // 那么什么情况下,agentService.getAgentuserid为空? +// if (StringUtils.isNotBlank(agentService.getAgentuserid())) { +// logger.info("[handle] set Agent User with agentUser Id {}", agentService.getAgentuserid()); +// getAgentUserProxy().findOne(agentService.getAgentuserid()).ifPresent(p -> { +// outMessage.setChannelMessage(p); +// }); +// } else { +// logger.info("[handle] agent user id is null."); +// } + + agentUserProxy.broadcastAgentsStatus( + ctx.getOrgi(), "user", MainContext.AgentUserStatusEnum.INSERVICE.toString(), + ctx.getAgentUser().getId()); + break; + case INQUENE: + // 处理结果:进入排队队列 + ctx.getAgentService().setQueneindex( + acdQueueService.getQueueIndex( + ctx.getAgentUser().getAgentno(), ctx.getOrgi(), ctx.getAgentUser().getSkill())); + + if (ctx.getAgentService().getQueneindex() > 0) { + // 当前有坐席,要排队 + ctx.setMessage(acdMessageHelper.getQueneMessage( + ctx.getAgentService().getQueneindex(), + ctx.getAgentUser().getChannel(), + ctx.getOrgi())); + } else { + // TODO 什么是否返回 noAgentMessage, 是否在是 INQUENE 时 getQueneindex == 0 + // 当前没有坐席,要留言 + ctx.setMessage(acdMessageHelper.getNoAgentMessage( + ctx.getAgentService().getQueneindex(), + ctx.getChannel(), + ctx.getOrgi())); + } + + agentUserProxy.broadcastAgentsStatus( + ctx.getOrgi(), "user", MainContext.AgentUserStatusEnum.INQUENE.toString(), + ctx.getAgentUser().getId()); + + break; + case END: + logger.info("[handler] should not happen for new onlineUser service request."); + default: + } + ctx.setChannelMessage(ctx.getAgentUser()); + } + logger.info( "[apply] message text: {}, noagent {}", ctx.getMessage(), ctx.isNoagent()); - } @@ -145,5 +236,4 @@ public class ACDVisBodyParserMw implements Middleware { return nickname; } - } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisServiceMw.java b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisServiceMw.java index 64ad26fd..9b4fbc24 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisServiceMw.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/acd/visitor/ACDVisServiceMw.java @@ -42,9 +42,6 @@ public class ACDVisServiceMw implements Middleware { @Autowired private ACDMessageHelper acdMessageHelper; - @Autowired - private AgentUserProxy agentUserProxy; - @Override public void apply(final ACDComposeContext ctx, final Functional next) { ctx.setMessageType(MainContext.MessageType.STATUS.toString()); @@ -75,88 +72,11 @@ public class ACDVisServiceMw implements Middleware { logger.info("[apply] agent user is null or END"); // 过滤坐席,获得 Agent Service next.apply(); - if (ctx.getAgentService() != null) { - // 没有得到agent service - postResolveAgentService(ctx); - } } } else { // 该AgentUser为新建 // 过滤坐席,获得 Agent Service next.apply(); - if (ctx.getAgentService() != null) { - // 没有得到agent service - postResolveAgentService(ctx); - } } } - - /** - * 根据AgentService,按照逻辑继续执行 - * - * @param ctx - */ - private void postResolveAgentService(final ACDComposeContext ctx) { - /** - * 找到空闲坐席,如果未找到坐席,则将该用户放入到 排队队列 - */ - switch (MainContext.AgentUserStatusEnum.toValue(ctx.getAgentService().getStatus())) { - case INSERVICE: - ctx.setMessage( - acdMessageHelper.getSuccessMessage( - ctx.getAgentService(), - ctx.getChannel(), - ctx.getOrgi())); - - // TODO 判断 INSERVICE 时,agentService 对应的 agentUser - logger.info( - "[apply] agent service: agentno {}, \n agentuser id {} \n user {} \n channel {} \n status {} \n queue index {}", - ctx.getAgentService().getAgentno(), ctx.getAgentService().getAgentuserid(), - ctx.getAgentService().getUserid(), - ctx.getAgentService().getChannel(), - ctx.getAgentService().getStatus(), - ctx.getAgentService().getQueneindex()); - - if (StringUtils.isNotBlank(ctx.getAgentService().getAgentuserid())) { - agentUserProxy.findOne(ctx.getAgentService().getAgentuserid()).ifPresent(p -> { - ctx.setAgentUser(p); - }); - } - - // TODO 如果是 INSERVICE 那么 agentService.getAgentuserid 就一定不能为空? -// // TODO 此处需要考虑 agentService.getAgentuserid 为空的情况 -// // 那么什么情况下,agentService.getAgentuserid为空? -// if (StringUtils.isNotBlank(agentService.getAgentuserid())) { -// logger.info("[handle] set Agent User with agentUser Id {}", agentService.getAgentuserid()); -// getAgentUserProxy().findOne(agentService.getAgentuserid()).ifPresent(p -> { -// outMessage.setChannelMessage(p); -// }); -// } else { -// logger.info("[handle] agent user id is null."); -// } - break; - case INQUENE: - if (ctx.getAgentService().getQueneindex() > 0) { - // 当前有坐席,要排队 - ctx.setMessage(acdMessageHelper.getQueneMessage( - ctx.getAgentService().getQueneindex(), - ctx.getAgentUser().getChannel(), - ctx.getOrgi())); - } else { - // TODO 什么是否返回 noAgentMessage, 是否在是 INQUENE 时 getQueneindex == 0 - // 当前没有坐席,要留言 - ctx.setMessage(acdMessageHelper.getNoAgentMessage( - ctx.getAgentService().getQueneindex(), - ctx.getChannel(), - ctx.getOrgi())); - } - break; - case END: - logger.info("[handler] should not happen for new onlineUser service request."); - default: - } - ctx.setChannelMessage(ctx.getAgentUser()); - } - - } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/controller/admin/OrganController.java b/contact-center/app/src/main/java/com/chatopera/cc/controller/admin/OrganController.java index 27db8c92..af22c3df 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/controller/admin/OrganController.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/controller/admin/OrganController.java @@ -103,14 +103,16 @@ public class OrganController extends Handler { } if (organData != null) { map.addAttribute( - "userList", userProxy.findByOrganAndOrgiAndDatastatus(organData.getId(), + "userList", userProxy.findByOrganAndOrgiAndDatastatus( + organData.getId(), super.getOrgiByTenantshare(request), false)); } } map.addAttribute("areaList", areaRepository.findByOrgi(super.getOrgiByTenantshare(request))); map.addAttribute( - "roleList", roleRepository.findByOrgiAndOrgid(super.getOrgiByTenantshare(request), + "roleList", roleRepository.findByOrgiAndOrgid( + super.getOrgiByTenantshare(request), super.getOrgid(request))); map.put("msg", msg); return request(super.createAdminTempletResponse("/admin/organ/index")); @@ -128,7 +130,8 @@ public class OrganController extends Handler { } map.addAttribute( - "organList", organRepository.findByOrgiAndOrgid(super.getOrgiByTenantshare(request), + "organList", organRepository.findByOrgiAndOrgid( + super.getOrgiByTenantshare(request), super.getOrgid(request))); return request(super.createRequestPageTempletResponse("/admin/organ/add")); @@ -142,7 +145,7 @@ public class OrganController extends Handler { String msg = "admin_organ_new_success"; String firstId = null; if (tempOrgan != null) { - msg = "admin_organ_update_name_not";//分类名字重复 + msg = "admin_organ_update_name_not"; //分类名字重复 } else { organ.setOrgi(super.getOrgiByTenantshare(request)); @@ -174,7 +177,7 @@ public class OrganController extends Handler { public ModelAndView seluser(ModelMap map, HttpServletRequest request, @Valid String organ) { map.addAttribute( "userList", userRepository.findByOrgiAndDatastatusAndOrgid(super.getOrgiByTenantshare(request), false, - super.getOrgid(request))); + super.getOrgid(request))); Organ organData = organRepository.findByIdAndOrgi(organ, super.getOrgiByTenantshare(request)); map.addAttribute("userOrganList", userProxy .findByOrganAndOrgiAndDatastatus(organ, super.getOrgiByTenantshare(request), false)); @@ -197,7 +200,7 @@ public class OrganController extends Handler { HttpServletRequest request, final @Valid String[] users, final @Valid String organ - ) { + ) { logger.info("[saveuser] save users {} into organ {}", StringUtils.join(users, ","), organ); final User loginUser = super.getUser(request); @@ -208,6 +211,20 @@ public class OrganController extends Handler { for (final User user : organUserList) { OrganUser ou = organUserRes.findByUseridAndOrgan(user.getId(), organ); + /** + * 检查人员和技能组关系 + */ + if (organData.isSkill()) { + // 该组织机构是技能组 + if (!user.isAgent()) { + // 该人员不是坐席 + if (ou != null) { + organUserRes.delete(ou); + } + continue; + } + } + if (ou == null) { ou = new OrganUser(); } @@ -218,18 +235,20 @@ public class OrganController extends Handler { organUserRes.save(ou); - /** - * 以下更新技能组状态 - */ - AgentStatus agentStatus = cache.findOneAgentStatusByAgentnoAndOrig( - user.getId(), super.getOrgiByTenantshare(request)); + if (user.isAgent()) { + /** + * 以下更新技能组状态 + */ + AgentStatus agentStatus = cache.findOneAgentStatusByAgentnoAndOrig( + user.getId(), super.getOrgiByTenantshare(request)); - // TODO 因为一个用户可以包含在多个技能组中,所以,skill应该对应 - // 一个List列表,此处需要重构Skill为列表 - if (agentStatus != null) { - userProxy.attachOrgansPropertiesForUser(user); - agentStatus.setSkills(user.getSkills()); - cache.putAgentStatusByOrgi(agentStatus, super.getOrgiByTenantshare(request)); + // TODO 因为一个用户可以包含在多个技能组中,所以,skill应该对应 + // 一个List列表,此处需要重构Skill为列表 + if (agentStatus != null) { + userProxy.attachOrgansPropertiesForUser(user); + agentStatus.setSkills(user.getSkills()); + cache.putAgentStatusByOrgi(agentStatus, super.getOrgiByTenantshare(request)); + } } } userRepository.save(organUserList); @@ -245,7 +264,7 @@ public class OrganController extends Handler { final HttpServletRequest request, final @Valid String id, final @Valid String organ - ) { + ) { logger.info("[userroledelete] user id {}, organ {}", id, organ); if (id != null) { organUserRes.deleteOrganUserByUseridAndOrgan(id, organ); @@ -262,7 +281,8 @@ public class OrganController extends Handler { view.addObject("organData", organRepository.findByIdAndOrgi(id, super.getOrgiByTenantshare(request))); map.addAttribute( - "organList", organRepository.findByOrgiAndOrgid(super.getOrgiByTenantshare(request), + "organList", organRepository.findByOrgiAndOrgid( + super.getOrgiByTenantshare(request), super.getOrgid(request))); return view; } diff --git a/contact-center/app/src/main/java/com/chatopera/cc/controller/api/ApiUserController.java b/contact-center/app/src/main/java/com/chatopera/cc/controller/api/ApiUserController.java index 65e955b8..da835c59 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/controller/api/ApiUserController.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/controller/api/ApiUserController.java @@ -227,7 +227,6 @@ public class ApiUserController extends Handler { */ private JsonObject update(final HttpServletRequest request, final JsonObject payload) { logger.info("[update] payload {}", payload.toString()); - final User logined = super.getUser(request); JsonObject resp = new JsonObject(); final User updated = userProxy.parseUserFromJson(payload); if (StringUtils.isBlank(updated.getId())) { @@ -244,14 +243,16 @@ public class ApiUserController extends Handler { // 由坐席切换成非坐席 判断是否坐席 以及 是否有对话 if (!updated.isAgent()) { AgentStatus agentStatus = cache.findOneAgentStatusByAgentnoAndOrig( - logined.getId(), logined.getOrgi()); - if (!(agentStatus == null && cache.getInservAgentUsersSizeByAgentnoAndOrgi( - logined.getId(), logined.getOrgi()) == 0)) { - msg = "t1"; + previous.getId(), previous.getOrgi()); + if (agentStatus != null && agentStatus.getUsers() > 0) { resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC); - resp.addProperty(RestUtils.RESP_KEY_DATA, msg); + resp.addProperty(RestUtils.RESP_KEY_DATA, "t1"); return resp; } + + // TODO 检查该用户是否在其它技能组 + // https://gitlab.chatopera.com/chatopera/cosinee/issues/751 + // 如果在其它技能组,禁止修改,返回提示:"该用户在其它技能组中,不支持取消坐席。取消坐席设置前需要不包括在任何技能组中。" } // 通过验证,可以更新数据库 @@ -260,13 +261,11 @@ public class ApiUserController extends Handler { previous.setEmail(updated.getEmail()); previous.setMobile(updated.getMobile()); previous.setSipaccount(updated.getSipaccount()); - previous.setAgent(updated.isAgent()); - previous.setOrgi(super.getOrgiByTenantshare(request)); - if (StringUtils.isNotBlank(logined.getOrgid())) { - previous.setOrgid(logined.getOrgid()); + if (StringUtils.isNotBlank(previous.getOrgid())) { + previous.setOrgid(previous.getOrgid()); } else { previous.setOrgid(MainContext.SYSTEM_ORGI); } @@ -284,7 +283,7 @@ public class ApiUserController extends Handler { previous.setAdmin(updated.isAdmin()); previous.setSuperadmin(false); userRes.save(previous); - OnlineUserProxy.clean(logined.getOrgi()); + OnlineUserProxy.clean(previous.getOrgi()); } resp.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/peer/im/ComposeMw1.java b/contact-center/app/src/main/java/com/chatopera/cc/peer/im/ComposeMw1.java index c369e36a..36d2b722 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/peer/im/ComposeMw1.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/peer/im/ComposeMw1.java @@ -127,7 +127,10 @@ public class ComposeMw1 implements Middleware { received.getMessage().length() > 100 ? received.getMessage().substring( 0, 100) : received.getMessage()); - agentUserTask.setTokenum(agentUserTask.getTokenum() + 1); + + if (StringUtils.equals(received.getType(), MainContext.MessageType.MESSAGE.toString())) { + agentUserTask.setTokenum(agentUserTask.getTokenum() + 1); + } received.setTokenum(agentUserTask.getTokenum()); agentUserTaskRes.save(agentUserTask); diff --git a/contact-center/app/src/main/java/com/chatopera/cc/util/HashMapUtils.java b/contact-center/app/src/main/java/com/chatopera/cc/util/HashMapUtils.java new file mode 100644 index 00000000..9b4ad46e --- /dev/null +++ b/contact-center/app/src/main/java/com/chatopera/cc/util/HashMapUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 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.util; + +import java.util.HashMap; +import java.util.stream.Collectors; + +public class HashMapUtils { + + + public static String concatKeys(final HashMap map) { + return concatKeys(map, ","); + } + + /** + * 将String Key连接起来 + * + * @param map + * @param + * @return + */ + public static String concatKeys(final HashMap map, String separator) { + String result = map.entrySet().stream() + .map(x -> x.getKey()) + .collect(Collectors.joining(separator)); + return result; + } +} diff --git a/contact-center/app/src/main/java/com/chatopera/cc/util/OnlineUserUtils.java b/contact-center/app/src/main/java/com/chatopera/cc/util/OnlineUserUtils.java index e1cef5c2..73c8cc90 100644 --- a/contact-center/app/src/main/java/com/chatopera/cc/util/OnlineUserUtils.java +++ b/contact-center/app/src/main/java/com/chatopera/cc/util/OnlineUserUtils.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2019 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.util; import com.chatopera.cc.basic.MainUtils;