1
0
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:
Hai Liang Wang 2019-11-19 20:21:47 +08:00
parent 20287ead02
commit 26baf7a5d7
11 changed files with 423 additions and 198 deletions

View File

@ -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(

View File

@ -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;
}
}

View File

@ -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);
/**

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;