mirror of
https://github.com/chatopera/cosin.git
synced 2025-08-01 16:38:02 +08:00
#227 route visitor with ACD rules
This commit is contained in:
parent
20287ead02
commit
26baf7a5d7
@ -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(
|
||||
|
@ -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<AgentStatus> 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<AgentStatus> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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<ACDComposeContext> {
|
||||
@Autowired
|
||||
private Cache cache;
|
||||
|
||||
@Autowired
|
||||
private ACDQueueService acdQueueService;
|
||||
|
||||
@Autowired
|
||||
private AgentServiceRepository agentServiceRes;
|
||||
|
||||
@ -66,54 +64,127 @@ public class ACDVisAllocatorMw implements Middleware<ACDComposeContext> {
|
||||
@Autowired
|
||||
private AgentUserProxy agentUserProxy;
|
||||
|
||||
@Autowired
|
||||
private ACDPolicyService acdPolicyService;
|
||||
|
||||
@Override
|
||||
public void apply(final ACDComposeContext ctx, final Functional next) {
|
||||
|
||||
/**
|
||||
* 查询条件,当前在线的 坐席,并且 未达到最大 服务人数的坐席
|
||||
*/
|
||||
List<AgentStatus> agentStatuses = filterOutAvailableAgentStatus(
|
||||
final List<AgentStatus> 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<AgentStatus> 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<com.chatopera.cc.util.WebIMReport> 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<ACDComposeContext> {
|
||||
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<AgentStatus> agentStatuses = new ArrayList<>();
|
||||
Map<String, AgentStatus> map = cache.findAllReadyAgentStatusByOrgi(orgi);
|
||||
|
||||
// DEBUG
|
||||
if (map.size() > 0) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("[filterOutAvailableAgentStatus] ready agents online: \n");
|
||||
for (final Map.Entry<String, AgentStatus> 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<String, AgentStatus> entry : map.entrySet()) {
|
||||
if ((!entry.getValue().isBusy()) && (StringUtils.equals(
|
||||
entry.getValue().getAgentno(), agentUser.getAgentno()))) {
|
||||
for (final Map.Entry<String, AgentStatus> entry : map.entrySet()) {
|
||||
if (StringUtils.equals(
|
||||
entry.getValue().getAgentno(), agentUser.getAgentno())) {
|
||||
agentStatuses.add(entry.getValue());
|
||||
logger.info(
|
||||
"[filterOutAvailableAgentStatus] <Agent> 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<String, AgentStatus> 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<String, AgentStatus> entry : map.entrySet()) {
|
||||
if ((!entry.getValue().isBusy()) &&
|
||||
(entry.getValue().getSkills() != null &&
|
||||
entry.getValue().getSkills().containsKey(agentUser.getSkill()))) {
|
||||
logger.info(
|
||||
"[filterOutAvailableAgentStatus] <Skill#{}> 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] <Skill#{}> 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<String, AgentStatus> entry : map.entrySet()) {
|
||||
for (final Map.Entry<String, AgentStatus> entry : map.entrySet()) {
|
||||
if (!entry.getValue().isBusy()) {
|
||||
agentStatuses.add(entry.getValue());
|
||||
logger.info(
|
||||
"[filterOutAvailableAgentStatus] <Redundance> 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<ACDComposeContext> {
|
||||
return agentStatuses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 为agentUser生成对应的AgentService
|
||||
* 使用场景:
|
||||
@ -192,8 +314,7 @@ public class ACDVisAllocatorMw implements Middleware<ACDComposeContext> {
|
||||
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<ACDComposeContext> {
|
||||
} else if (agentStatus != null) {
|
||||
agentService.setAgent(agentStatus.getAgentno());
|
||||
agentService.setSkill(agentUser.getSkill());
|
||||
|
||||
if (sessionConfig.isLastagent()) {
|
||||
// 启用了历史坐席优先 , 查找 历史服务坐席
|
||||
List<com.chatopera.cc.util.WebIMReport> 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());
|
||||
|
@ -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<ACDComposeContext> {
|
||||
@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<ACDComposeContext> {
|
||||
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<ACDComposeContext> {
|
||||
|
||||
return nickname;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ public class ACDVisServiceMw implements Middleware<ACDComposeContext> {
|
||||
@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<ACDComposeContext> {
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -127,7 +127,10 @@ public class ComposeMw1 implements Middleware<PeerContext> {
|
||||
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);
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* 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 <T> String concatKeys(final HashMap<String, T> map) {
|
||||
return concatKeys(map, ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String Key连接起来
|
||||
*
|
||||
* @param map
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> String concatKeys(final HashMap<String, T> map, String separator) {
|
||||
String result = map.entrySet().stream()
|
||||
.map(x -> x.getKey())
|
||||
.collect(Collectors.joining(separator));
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Chatopera Inc, <https://www.chatopera.com>
|
||||
*
|
||||
* 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user