|
@@ -2,7 +2,10 @@ package com.cyksj.service.corp.impl;
|
|
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.io.FileUtil;
|
|
import cn.hutool.core.io.FileUtil;
|
|
|
|
|
+import cn.hutool.core.util.RandomUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.http.HttpUtil;
|
|
import cn.hutool.http.HttpUtil;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
@@ -10,6 +13,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.cyksj.common.constant.Constant;
|
|
import com.cyksj.common.constant.Constant;
|
|
|
import com.cyksj.common.exception.BusinessRuntimeException;
|
|
import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
|
|
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
|
|
|
|
|
+import com.cyksj.common.util.Jsons;
|
|
|
import com.cyksj.common.util.StringUtil;
|
|
import com.cyksj.common.util.StringUtil;
|
|
|
import com.cyksj.common.util.Xmls;
|
|
import com.cyksj.common.util.Xmls;
|
|
|
import com.cyksj.common.util.corp.WxCryptUtil;
|
|
import com.cyksj.common.util.corp.WxCryptUtil;
|
|
@@ -18,8 +22,11 @@ import com.cyksj.config.corp.YHLXWxCorpConfig;
|
|
|
import com.cyksj.dto.CorpOauth2UserInfo;
|
|
import com.cyksj.dto.CorpOauth2UserInfo;
|
|
|
import com.cyksj.mapper.OrderDonMapper;
|
|
import com.cyksj.mapper.OrderDonMapper;
|
|
|
import com.cyksj.mapper.corp.*;
|
|
import com.cyksj.mapper.corp.*;
|
|
|
|
|
+import com.cyksj.mapper.corp.kf.*;
|
|
|
|
|
+import com.cyksj.model.dto.CorpKfServicerDto;
|
|
|
import com.cyksj.model.dto.WxCorpEncryptDto;
|
|
import com.cyksj.model.dto.WxCorpEncryptDto;
|
|
|
import com.cyksj.model.entity.*;
|
|
import com.cyksj.model.entity.*;
|
|
|
|
|
+import com.cyksj.model.kf.*;
|
|
|
import com.cyksj.model.request.corp.Attachments;
|
|
import com.cyksj.model.request.corp.Attachments;
|
|
|
import com.cyksj.model.request.corp.CorpXmlMessage;
|
|
import com.cyksj.model.request.corp.CorpXmlMessage;
|
|
|
import com.cyksj.model.request.corp.CorpXmlOutMessage;
|
|
import com.cyksj.model.request.corp.CorpXmlOutMessage;
|
|
@@ -42,10 +49,9 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
import javax.annotation.PostConstruct;
|
|
|
import java.io.File;
|
|
import java.io.File;
|
|
|
-import java.util.HashMap;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
-import java.util.Map;
|
|
|
|
|
|
|
+import java.util.*;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
*项目名: netflix
|
|
*项目名: netflix
|
|
@@ -97,6 +103,30 @@ public class CorpServiceImpl implements CorpService {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private CorpUserFollowRelationMapper corpUserFollowRelationMapper;
|
|
private CorpUserFollowRelationMapper corpUserFollowRelationMapper;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfAccountMapper corpKfAccountMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfAccountFollowMapper corpKfAccountFollowMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfAccountSessionConfigMapper corpKfAccountSessionConfigMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfWaitingQueueUserMapper corpKfWaitingQueueUserMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfServicerSessionRecordMapper corpKfServicerSessionRecordMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfAccountSyncMsgProgressMapper corpKfAccountSyncMsgProgressMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfAccountSessionChatMapper corpKfAccountSessionChatMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CorpKfUserMapper corpKfUserMapper;
|
|
|
|
|
+
|
|
|
@PostConstruct
|
|
@PostConstruct
|
|
|
private void init() {
|
|
private void init() {
|
|
|
corpMessageRouter = new CorpMessageRouter(this)
|
|
corpMessageRouter = new CorpMessageRouter(this)
|
|
@@ -208,6 +238,16 @@ public class CorpServiceImpl implements CorpService {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
.end()
|
|
.end()
|
|
|
|
|
+ //企业微信客服 用户进入会话事件
|
|
|
|
|
+ .rule().async(false).msgType("event").event("kf_msg_or_event")
|
|
|
|
|
+ .handler(new CorpMessageHandler() {
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public CorpXmlOutMessage handle(CorpXmlMessage message, Map<String, Object> context, CorpService corpService) throws Exception {
|
|
|
|
|
+ //
|
|
|
|
|
+ return kfMsgOrEvent(message, context);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .end()
|
|
|
;
|
|
;
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -489,4 +529,544 @@ public class CorpServiceImpl implements CorpService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 企业微信客服 时间消息回调
|
|
|
|
|
+ */
|
|
|
|
|
+ private CorpXmlOutMessage kfMsgOrEvent(CorpXmlMessage message, Map<String, Object> context) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String openKfId = message.getOpenKfId();
|
|
|
|
|
+ CorpKfAccount corpKfAccount = corpKfAccountMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccount.class)
|
|
|
|
|
+ .eq(CorpKfAccount::getOpenKfId, openKfId)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (corpKfAccount == null) {
|
|
|
|
|
+ log.info("客服账号:{}已被删除或不存在", openKfId);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ //账号会话设置
|
|
|
|
|
+ CorpKfAccountSessionConfig corpKfAccountSessionConfig = Optional.ofNullable(corpKfAccountSessionConfigMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccountSessionConfig.class)
|
|
|
|
|
+ .eq(CorpKfAccountSessionConfig::getOpenKfId, openKfId)
|
|
|
|
|
+ .last("limit 1"))).orElse(new CorpKfAccountSessionConfig());
|
|
|
|
|
+ //同步消息进度
|
|
|
|
|
+ CorpKfAccountSyncMsgProgress corpKfAccountSyncMsgProgress = Optional.ofNullable(corpKfAccountSyncMsgProgressMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccountSyncMsgProgress.class)
|
|
|
|
|
+ .eq(CorpKfAccountSyncMsgProgress::getOpenKfId, openKfId)
|
|
|
|
|
+ .last("limit 1"))).orElse(new CorpKfAccountSyncMsgProgress());
|
|
|
|
|
+ String cursor = null;
|
|
|
|
|
+ if (corpKfAccountSyncMsgProgress.getNextCursor() != null) {
|
|
|
|
|
+ cursor = corpKfAccountSyncMsgProgress.getNextCursor();
|
|
|
|
|
+ }
|
|
|
|
|
+ String corpId = message.getToUserName();
|
|
|
|
|
+ String token = message.getToken();
|
|
|
|
|
+ syncCorpKfMsg(corpKfAccount, corpKfAccountSessionConfig, corpId, cursor, token, null, corpKfAccountSyncMsgProgress);
|
|
|
|
|
+ corpKfAccountSyncMsgProgress.setOpenKfId(openKfId);
|
|
|
|
|
+ //同步记录
|
|
|
|
|
+ if (corpKfAccountSyncMsgProgress.getId() == null) {
|
|
|
|
|
+ corpKfAccountSyncMsgProgressMapper.insert(corpKfAccountSyncMsgProgress);
|
|
|
|
|
+ } else corpKfAccountSyncMsgProgressMapper.updateById(corpKfAccountSyncMsgProgress);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("企业微信客服 事件消息回调 错误,e:{}", StringUtil.getErrorText(e));
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void syncCorpKfMsg(CorpKfAccount corpKfAccount, CorpKfAccountSessionConfig corpKfAccountSessionConfig, String corpId, String cursor, String token, Integer limit, CorpKfAccountSyncMsgProgress corpKfAccountSyncMsgProgress) throws Exception {
|
|
|
|
|
+ String openKfId = corpKfAccount.getOpenKfId();
|
|
|
|
|
+ WxCpKfMsgListResp wxCpKfMsgListResp = wxCorpOps.syncMsg(corpId, cursor, token, limit, openKfId);
|
|
|
|
|
+ List<WxCpKfMsgListResp.WxCpKfMsgItem> msgList = wxCpKfMsgListResp.getMsgList();
|
|
|
|
|
+ for (WxCpKfMsgListResp.WxCpKfMsgItem item : msgList) {
|
|
|
|
|
+ if (item.getEvent() != null) {
|
|
|
|
|
+ //会话事件
|
|
|
|
|
+ if (item.getEvent().getEventType().equals("enter_session")) {
|
|
|
|
|
+ //进入会话事件
|
|
|
|
|
+ enterSessionEvent(corpKfAccount, corpKfAccountSessionConfig, corpId, item);
|
|
|
|
|
+ }
|
|
|
|
|
+ //会话变更事件
|
|
|
|
|
+ if (item.getEvent().getEventType().equals("session_status_change")) {
|
|
|
|
|
+ //会话更变事件
|
|
|
|
|
+ sessionStatusChange(corpId, openKfId, item, corpKfAccountSessionConfig);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //同步微信客服账号信息
|
|
|
|
|
+ syncKfChatMsg(corpId, openKfId, corpKfAccountSessionConfig, item);
|
|
|
|
|
+ }
|
|
|
|
|
+ cursor = wxCpKfMsgListResp.getNextCursor();
|
|
|
|
|
+ corpKfAccountSyncMsgProgress.setNextCursor(cursor);
|
|
|
|
|
+ //同步信息
|
|
|
|
|
+ while (wxCpKfMsgListResp.getHasMore() == 1) {
|
|
|
|
|
+ syncCorpKfMsg(corpKfAccount, corpKfAccountSessionConfig, corpId, wxCpKfMsgListResp.getNextCursor(), token, limit, corpKfAccountSyncMsgProgress);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void syncKfChatMsg(String corpId, String openKfId, CorpKfAccountSessionConfig corpKfAccountSessionConfig, WxCpKfMsgListResp.WxCpKfMsgItem item) throws Exception {
|
|
|
|
|
+ CorpKfAccountSessionChat corpKfAccountSessionChat = new CorpKfAccountSessionChat();
|
|
|
|
|
+ Boolean record = false;
|
|
|
|
|
+ //文本
|
|
|
|
|
+ if (item.getText() != null) {
|
|
|
|
|
+ corpKfAccountSessionChat.setMsgContent(item.getText().getContent());
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getMergedMsg() != null) {
|
|
|
|
|
+ //聊天记录 转发
|
|
|
|
|
+ corpKfAccountSessionChat.setMsgContent(Jsons.toJson(item.getMergedMsg().getItem()));
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getVideo() != null) {
|
|
|
|
|
+ //视频记录
|
|
|
|
|
+ setMediaUrl(corpKfAccountSessionChat, item.getVideo().getMedia_id());
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getFile() != null) {
|
|
|
|
|
+ //文件
|
|
|
|
|
+ setMediaUrl(corpKfAccountSessionChat, item.getFile().getMedia_id());
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getImage() != null) {
|
|
|
|
|
+ //图片
|
|
|
|
|
+ setMediaUrl(corpKfAccountSessionChat, item.getImage().getMedia_id());
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getLink() != null) {
|
|
|
|
|
+ //链接
|
|
|
|
|
+ WxCorpLinkMsg link = item.getLink();
|
|
|
|
|
+ corpKfAccountSessionChat.setLinkTitle(link.getTitle());
|
|
|
|
|
+ corpKfAccountSessionChat.setLinkDesc(link.getDesc());
|
|
|
|
|
+ corpKfAccountSessionChat.setLinkUrl(link.getUrl());
|
|
|
|
|
+ corpKfAccountSessionChat.setLinkPicUrl(link.getPic_url());
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ } else if (item.getMsgmenu() != null) {
|
|
|
|
|
+ //菜单 结束语统计
|
|
|
|
|
+ corpKfAccountSessionChat.setMsgContent(Jsons.toJson(item.getMsgmenu()));
|
|
|
|
|
+ record = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (record) {
|
|
|
|
|
+ //新增
|
|
|
|
|
+ String openKfid = item.getOpenKfid();
|
|
|
|
|
+ //客户id
|
|
|
|
|
+ String externalUserId = item.getExternalUserId();
|
|
|
|
|
+ //接待人员id
|
|
|
|
|
+ String servicerUserId = item.getServicerUserId();
|
|
|
|
|
+ //消息来源。3-微信客户发送的消息 4-系统推送的事件消息 5-接待人员在企业微信客户端发送的消息
|
|
|
|
|
+ Integer origin = item.getOrigin();
|
|
|
|
|
+ //发送时间
|
|
|
|
|
+ Long sendTime = item.getSendTime();
|
|
|
|
|
+ //消息id
|
|
|
|
|
+ String msgId = item.getMsgId();
|
|
|
|
|
+ //消息类型
|
|
|
|
|
+ String msgType = item.getMsgType();
|
|
|
|
|
+ corpKfAccountSessionChat.setOpenKfId(openKfid);
|
|
|
|
|
+ corpKfAccountSessionChat.setMsgid(msgId);
|
|
|
|
|
+ corpKfAccountSessionChat.setServicerUserid(servicerUserId);
|
|
|
|
|
+ corpKfAccountSessionChat.setExternalUserid(externalUserId);
|
|
|
|
|
+ corpKfAccountSessionChat.setOrign(origin);
|
|
|
|
|
+ corpKfAccountSessionChat.setMsgType(msgType);
|
|
|
|
|
+ corpKfAccountSessionChat.setSendTime(sendTime);
|
|
|
|
|
+ corpKfAccountSessionChat.setSendDate(DateTime.of(sendTime * 1000));
|
|
|
|
|
+ saveChatMsg(corpKfAccountSessionChat);
|
|
|
|
|
+
|
|
|
|
|
+ if (StrUtil.isNotEmpty(externalUserId)) {
|
|
|
|
|
+ //会话状态为待处理 则分配接待人员
|
|
|
|
|
+ assignKfSession(corpId, openKfId, externalUserId, corpKfAccountSessionConfig, corpKfAccountSessionChat.getSendDate(), msgId);
|
|
|
|
|
+ }
|
|
|
|
|
+ syncSessionSatisfaction(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void syncSessionSatisfaction(WxCpKfMsgListResp.WxCpKfMsgItem item) throws Exception {
|
|
|
|
|
+ Integer satisfaction = null;
|
|
|
|
|
+ WxCorpTextMsg text = item.getText();
|
|
|
|
|
+ //自定义菜单id
|
|
|
|
|
+ if (text != null && text.getMenu_id() != null) {
|
|
|
|
|
+ String[] split = text.getMenu_id().split("-");
|
|
|
|
|
+ if (split.length < 2) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ String menuId = split[0];
|
|
|
|
|
+ String msgid = split[1];
|
|
|
|
|
+ //满意
|
|
|
|
|
+ if ("101".equals(menuId)) {
|
|
|
|
|
+ satisfaction = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ //一般
|
|
|
|
|
+ if ("102".equals(menuId)) {
|
|
|
|
|
+ satisfaction = 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ //不满意
|
|
|
|
|
+ if ("103".equals(menuId)) {
|
|
|
|
|
+ satisfaction = 3;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (satisfaction != null) {
|
|
|
|
|
+ CorpKfServicerSessionRecord corpKfServicerSessionRecord = corpKfServicerSessionRecordMapper.selectOne(Wrappers.lambdaQuery(CorpKfServicerSessionRecord.class)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getMsgid, msgid)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (corpKfServicerSessionRecord != null) {
|
|
|
|
|
+ corpKfServicerSessionRecord.setSatisfaction(satisfaction);
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.updateById(corpKfServicerSessionRecord);
|
|
|
|
|
+ //设置会话id
|
|
|
|
|
+ corpKfAccountSessionChatMapper.update(null, Wrappers.lambdaUpdate(CorpKfAccountSessionChat.class)
|
|
|
|
|
+ .set(CorpKfAccountSessionChat::getSessionId, corpKfServicerSessionRecord.getId())
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getMsgid, item.getMsgId()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void sessionStatusChange(String corpId, String openKfId, WxCpKfMsgListResp.WxCpKfMsgItem wxCpKfMsgItem, CorpKfAccountSessionConfig corpKfAccountSessionConfig) throws Exception {
|
|
|
|
|
+ WxCpKfEventMsg event = wxCpKfMsgItem.getEvent();
|
|
|
|
|
+ Integer changeType = event.getChangeType();
|
|
|
|
|
+ String externalUserId = event.getExternalUserId();
|
|
|
|
|
+ Long sendTime = wxCpKfMsgItem.getSendTime();
|
|
|
|
|
+ DateTime sendDate = DateTime.of(sendTime * 1000);
|
|
|
|
|
+ //1-从接待池接入会话 2-转接会话 3-结束会话 4-重新接入已结束/已转接会话
|
|
|
|
|
+ String oldServicerUserId = event.getOldServicerUserId();
|
|
|
|
|
+ String newServicerUserId = event.getNewServicerUserId();
|
|
|
|
|
+ String msgId = wxCpKfMsgItem.getMsgId();
|
|
|
|
|
+ String msgCode = wxCpKfMsgItem.getEvent().getMsgCode();
|
|
|
|
|
+ if (changeType == 1) {
|
|
|
|
|
+ CorpKfServicerSessionRecord newSession = new CorpKfServicerSessionRecord();
|
|
|
|
|
+ newSession.setOpenKfId(openKfId);
|
|
|
|
|
+ newSession.setServicerUserid(newServicerUserId);
|
|
|
|
|
+ newSession.setExternalUserid(externalUserId);
|
|
|
|
|
+ newSession.setChangeType(changeType);
|
|
|
|
|
+ newSession.setServiceState(0);
|
|
|
|
|
+ newSession.setStartTime(sendDate);
|
|
|
|
|
+ newSession.setMsgid(msgId);
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.insert(newSession);
|
|
|
|
|
+ //等待用户删除
|
|
|
|
|
+ delQueueUser(openKfId, externalUserId, sendDate);
|
|
|
|
|
+ syncUserChatSession(newSession.getId(), externalUserId);
|
|
|
|
|
+ }
|
|
|
|
|
+ //2-转接会话
|
|
|
|
|
+ if (changeType == 2) {
|
|
|
|
|
+ //设置表中会话状态
|
|
|
|
|
+ setDbSessionState(openKfId, oldServicerUserId, externalUserId, sendDate, changeType, msgId, msgCode);
|
|
|
|
|
+ //记录新的客服会话
|
|
|
|
|
+ String newServicerUserid = wxCpKfMsgItem.getNewServicerUserid();
|
|
|
|
|
+ transCorpKfSessionToServicer(corpId, openKfId, oldServicerUserId, newServicerUserid, externalUserId, changeType, 3, sendDate, msgId);
|
|
|
|
|
+ }
|
|
|
|
|
+ //3-结束会话 4-重新接入已结束/已转接会话
|
|
|
|
|
+ if (changeType == 3 || changeType == 4) {
|
|
|
|
|
+ setDbSessionState(openKfId, oldServicerUserId, externalUserId, sendDate, changeType, msgId, msgCode);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * enter_session 会话事件
|
|
|
|
|
+ */
|
|
|
|
|
+ public void enterSessionEvent(CorpKfAccount corpKfAccount, CorpKfAccountSessionConfig corpKfAccountSessionConfig, String corpId, WxCpKfMsgListResp.WxCpKfMsgItem wxCpKfMsgItem) throws Exception {
|
|
|
|
|
+ String openKfId = corpKfAccount.getOpenKfId();
|
|
|
|
|
+ DateTime sendDate = DateTime.of(wxCpKfMsgItem.getSendTime() * 1000);
|
|
|
|
|
+ WxCpKfEventMsg event = wxCpKfMsgItem.getEvent();
|
|
|
|
|
+ String welcomeCode = event.getWelcomeCode();
|
|
|
|
|
+ String msgid = wxCpKfMsgItem.getMsgId();
|
|
|
|
|
+ //如果满足发送欢迎语条件(条件为:用户在过去48小时里未收过欢迎语,且未向客服发过消息),会返回该字段。
|
|
|
|
|
+ //可用该welcome_code调用发送事件响应消息接口给客户发送欢迎语。
|
|
|
|
|
+ if (StrUtil.isNotEmpty(welcomeCode)) {
|
|
|
|
|
+ WxCorpKfMsgSendRequest wxCpKfMsgSendRequest = new WxCorpKfMsgSendRequest();
|
|
|
|
|
+ wxCpKfMsgSendRequest.setMsgType("text");
|
|
|
|
|
+ wxCpKfMsgSendRequest.setCode(welcomeCode);
|
|
|
|
|
+ WxCorpKfMsgSendRequest.WxCpKfTextMsg textMsg = new WxCorpKfMsgSendRequest.WxCpKfTextMsg();
|
|
|
|
|
+ if (StrUtil.isNotEmpty(corpKfAccountSessionConfig.getWelcomeContent())) {
|
|
|
|
|
+ textMsg.setContent(corpKfAccountSessionConfig.getWelcomeContent());
|
|
|
|
|
+ wxCpKfMsgSendRequest.setText(textMsg);
|
|
|
|
|
+ wxCpKfMsgSendRequest.setToUser(welcomeCode);
|
|
|
|
|
+ wxCpKfMsgSendRequest.setOpenKfid(event.getOpenKfid());
|
|
|
|
|
+ try {
|
|
|
|
|
+ wxCorpOps.sendMsgOnEvent(corpId, wxCpKfMsgSendRequest);
|
|
|
|
|
+ //同步用户信息
|
|
|
|
|
+ syncCorpkfUserInfo(event.getExternalUserId());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("发送微信客服欢迎语失败:{}", StringUtil.getErrorText(e));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //分配接待人员
|
|
|
|
|
+ assignKfSession(corpId, openKfId, event.getExternalUserId(), corpKfAccountSessionConfig, sendDate, msgid);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 同步用户信息
|
|
|
|
|
+ */
|
|
|
|
|
+ private void syncCorpkfUserInfo(String externalUserid) throws Exception {
|
|
|
|
|
+ CorpKfUserInfo corpKfUserInfo = wxCorpOps.getCorpKfUserInfo(externalUserid);
|
|
|
|
|
+ CorpKfUser corpKfUser = corpKfUserMapper.selectOne(Wrappers.lambdaQuery(CorpKfUser.class)
|
|
|
|
|
+ .eq(CorpKfUser::getExternalUserid, externalUserid)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (corpKfUser == null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ corpKfUser = new CorpKfUser();
|
|
|
|
|
+ BeanUtil.copyProperties(corpKfUserInfo, corpKfUser);
|
|
|
|
|
+ corpKfUserMapper.insert(corpKfUser);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ corpKfUser.setAvatar(corpKfUserInfo.getAvatar());
|
|
|
|
|
+ corpKfUser.setNickname(corpKfUserInfo.getNickname());
|
|
|
|
|
+ corpKfUser.setGender(corpKfUserInfo.getGender());
|
|
|
|
|
+ corpKfUserMapper.updateById(corpKfUser);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 变更用户会话状态
|
|
|
|
|
+ * serviceState 0未处理 1由智能助手接待 2待接入池排队中 3由人工接待 4 已结束/未开始
|
|
|
|
|
+ */
|
|
|
|
|
+ private void transCorpKfSessionToServicer(String corpId, String openKfId, String oldServicerUserId, String userid, String externalUserId, Integer changeType, Integer serviceState, Date sendTime, String msgid) throws Exception {
|
|
|
|
|
+ //记录接待人员会话
|
|
|
|
|
+ CorpKfServicerSessionRecord corpKfServicerSessionRecord = new CorpKfServicerSessionRecord();
|
|
|
|
|
+ corpKfServicerSessionRecord.setOpenKfId(openKfId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setServiceState(0);
|
|
|
|
|
+ corpKfServicerSessionRecord.setFromUserid(oldServicerUserId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setServicerUserid(userid);
|
|
|
|
|
+ corpKfServicerSessionRecord.setExternalUserid(externalUserId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setChangeType(changeType);
|
|
|
|
|
+ corpKfServicerSessionRecord.setStartTime(sendTime);
|
|
|
|
|
+ corpKfServicerSessionRecord.setMsgid(msgid);
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.insert(corpKfServicerSessionRecord);
|
|
|
|
|
+ //调用微信客服接口 变更会话状态
|
|
|
|
|
+ wxCorpOps.transCorpKfSessionToServicer(corpId, openKfId, userid, externalUserId, serviceState);
|
|
|
|
|
+ //等待用户删除
|
|
|
|
|
+ delQueueUser(openKfId, externalUserId, sendTime);
|
|
|
|
|
+ //同步这一次接待记录
|
|
|
|
|
+ if (serviceState == 3) {
|
|
|
|
|
+ corpKfAccountSessionChatMapper.update(null, Wrappers.lambdaUpdate(CorpKfAccountSessionChat.class)
|
|
|
|
|
+ .set(CorpKfAccountSessionChat::getSessionId, corpKfServicerSessionRecord.getId())
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getOpenKfId, openKfId)
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getExternalUserid, externalUserId)
|
|
|
|
|
+ .isNull(CorpKfAccountSessionChat::getSessionId));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 发送微信客服会话文本消息
|
|
|
|
|
+ */
|
|
|
|
|
+ public void sendCorpKfTextTips(String corpId, String externalUserid, String openKfId, String content) throws Exception {
|
|
|
|
|
+ if (StrUtil.isEmpty(content)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //查询会话状态
|
|
|
|
|
+ ServiceStateResponse serviceStateResponse = wxCorpOps.getCorpKfSessionStatus(corpId, openKfId, externalUserid);
|
|
|
|
|
+ Integer state = serviceStateResponse.getService_state();
|
|
|
|
|
+ //0.未处理 1.人工智能接待 可调用api发送消息
|
|
|
|
|
+ if (state == null && state != 0 && state != 1) {
|
|
|
|
|
+ log.info("目前openKfId:{}用户eid:{}会话处于状态中state:{},无法发送消息", openKfId, externalUserid, state);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ WxCorpKfMsgSendRequest wxCpKfMsgSendRequest = new WxCorpKfMsgSendRequest();
|
|
|
|
|
+ wxCpKfMsgSendRequest.setMsgType("text");
|
|
|
|
|
+ wxCpKfMsgSendRequest.setToUser(externalUserid);
|
|
|
|
|
+ wxCpKfMsgSendRequest.setOpenKfid(openKfId);
|
|
|
|
|
+ WxCorpKfMsgSendRequest.WxCpKfTextMsg textMsg = new WxCorpKfMsgSendRequest.WxCpKfTextMsg();
|
|
|
|
|
+ textMsg.setContent(content);
|
|
|
|
|
+ wxCpKfMsgSendRequest.setText(textMsg);
|
|
|
|
|
+ wxCorpOps.sendTextMsg(corpId, wxCpKfMsgSendRequest);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 会话状态为待处理 则分配接待人员
|
|
|
|
|
+ */
|
|
|
|
|
+ public void assignKfSession(String corpId, String openKfId, String externalUserid, CorpKfAccountSessionConfig corpKfAccountSessionConfig, Date sendDate, String msgid) throws Exception {
|
|
|
|
|
+ //超过15分钟 进入会话时间不处理
|
|
|
|
|
+ if (sendDate.compareTo(DateUtil.offsetMinute(DateTime.now(), -15)) <= 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //查询用户会话状态 0待处理 1智能助手接待
|
|
|
|
|
+ ServiceStateResponse serviceStateResponse = wxCorpOps.getCorpKfSessionStatus(corpId, openKfId, externalUserid);
|
|
|
|
|
+ Integer state = serviceStateResponse.getService_state();
|
|
|
|
|
+ if (state != 0 && state != 1) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //当前时间段值班客服
|
|
|
|
|
+ Set<String> dutyFollowIds = getDutyFollowIds(openKfId);
|
|
|
|
|
+ //非值班
|
|
|
|
|
+ if (dutyFollowIds.isEmpty()) {
|
|
|
|
|
+ //非值班时间 移入等待池
|
|
|
|
|
+ log.info("当前客服账号:{}处于非值班时间:{},将客户:{}移入等待池", openKfId, DateTime.now(), externalUserid);
|
|
|
|
|
+ //发送进入等待池消息
|
|
|
|
|
+ sendCorpKfTextTips(corpId, externalUserid, openKfId, corpKfAccountSessionConfig.getQueueTips());
|
|
|
|
|
+ moveWaitingQueue(corpId, openKfId, externalUserid);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //获取接待人员列表
|
|
|
|
|
+ CorpKfServicerResp corpKfServicerResp = wxCorpOps.getKfServicerList(corpId, openKfId);
|
|
|
|
|
+ List<CorpKfServicerResp.CorpKfServicer> servicerList = corpKfServicerResp.getServicerList();
|
|
|
|
|
+ //目前值班客服id
|
|
|
|
|
+ List<String> corpKfServiceIds = new ArrayList<>();
|
|
|
|
|
+ servicerList.forEach(servicer -> {
|
|
|
|
|
+ if (servicer.getUserid() != null && servicer.getStatus() == 0 && dutyFollowIds.contains(servicer.getUserid())) {
|
|
|
|
|
+ corpKfServiceIds.add(servicer.getUserid());
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ //客服是否已经开始值班
|
|
|
|
|
+ //没有值班人员
|
|
|
|
|
+ if (CollUtil.isEmpty(corpKfServiceIds)) {
|
|
|
|
|
+ log.info("当前客服账号:{}无值班人员,将客户:{}移入等待池", openKfId, externalUserid);
|
|
|
|
|
+ sendCorpKfTextTips(corpId, externalUserid, openKfId, corpKfAccountSessionConfig.getQueueTips());
|
|
|
|
|
+ //接入等待池
|
|
|
|
|
+ moveWaitingQueue(corpId, openKfId, externalUserid);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //是否有空闲接待人员
|
|
|
|
|
+ List<CorpKfServicerDto> corpKfServicerDtos = corpKfServicerSessionRecordMapper.getAvailableServicer(openKfId);
|
|
|
|
|
+ //暂无分配会话 初始化
|
|
|
|
|
+ if (CollUtil.isEmpty(corpKfServicerDtos)) {
|
|
|
|
|
+ //随机分配一个客服
|
|
|
|
|
+ String servicerUserid = corpKfServiceIds.get(RandomUtil.randomInt(corpKfServiceIds.size()));
|
|
|
|
|
+ //变更会话状态
|
|
|
|
|
+ transCorpKfSessionToServicer(corpId, openKfId, null, servicerUserid, externalUserid, null, 3, sendDate, msgid);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //移入未接入的客服
|
|
|
|
|
+ List<String> collect = corpKfServicerDtos.stream().map(CorpKfServicerDto::getServicerUserid).collect(Collectors.toList());
|
|
|
|
|
+ corpKfServiceIds.forEach(serviceId -> {
|
|
|
|
|
+ if (!collect.contains(serviceId)) {
|
|
|
|
|
+ CorpKfServicerDto corpKfServicerDto = new CorpKfServicerDto();
|
|
|
|
|
+ corpKfServicerDto.setServicerUserid(serviceId);
|
|
|
|
|
+ corpKfServicerDto.setNum(0);
|
|
|
|
|
+ corpKfServicerDtos.add(corpKfServicerDto);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ //筛选符合条件的客服
|
|
|
|
|
+ //默认接入最少的优先分配 接待人最少的在集合前面
|
|
|
|
|
+ //不超过最多接待人数 默认30
|
|
|
|
|
+ Integer maxNum = Optional.ofNullable(corpKfAccountSessionConfig.getMaxNum()).orElse(30);
|
|
|
|
|
+ List<CorpKfServicerDto> filterKfCs = corpKfServicerDtos.stream().filter(kfCs -> corpKfServiceIds.contains(kfCs.getServicerUserid()) && kfCs.getNum() < maxNum).collect(Collectors.toList());
|
|
|
|
|
+ if (CollUtil.isEmpty(filterKfCs)) {
|
|
|
|
|
+ log.info("当前客服账号:{}值班接待上限:{},将客户:{}移入等待池", openKfId, maxNum, externalUserid);
|
|
|
|
|
+ //发送进入等待池消息
|
|
|
|
|
+ sendCorpKfTextTips(corpId, externalUserid, openKfId, corpKfAccountSessionConfig.getQueueTips());
|
|
|
|
|
+ //移入等待池
|
|
|
|
|
+ moveWaitingQueue(corpId, openKfId, externalUserid);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //选择第一个分配接待
|
|
|
|
|
+ CorpKfServicerDto corpKfServicerDto = filterKfCs.get(0);
|
|
|
|
|
+ //变更会话状态
|
|
|
|
|
+ transCorpKfSessionToServicer(corpId, openKfId, null, corpKfServicerDto.getServicerUserid(), externalUserid, null, 3, sendDate, msgid);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void moveWaitingQueue(String corpId, String openKfId, String externalUserId) throws Exception {
|
|
|
|
|
+ CorpKfWaitingQueueUser corpKfWaitingQueueUser = new CorpKfWaitingQueueUser();
|
|
|
|
|
+ corpKfWaitingQueueUser.setOpenKfId(openKfId);
|
|
|
|
|
+ corpKfWaitingQueueUser.setExternalUserid(externalUserId);
|
|
|
|
|
+ try {
|
|
|
|
|
+ corpKfWaitingQueueUserMapper.insert(corpKfWaitingQueueUser);
|
|
|
|
|
+ //移入等待池
|
|
|
|
|
+ wxCorpOps.moveWaitingQueue(corpId, openKfId, externalUserId);
|
|
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置临时素材url
|
|
|
|
|
+ */
|
|
|
|
|
+ private void setMediaUrl(CorpKfAccountSessionChat corpKfAccountSessionChat, String mediaId) throws Exception {
|
|
|
|
|
+ String url = wxCorpOps.getMedia(null, mediaId);
|
|
|
|
|
+ corpKfAccountSessionChat.setUrl(url);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 新增记录
|
|
|
|
|
+ */
|
|
|
|
|
+ public void saveChatMsg(CorpKfAccountSessionChat chat) {
|
|
|
|
|
+ CorpKfServicerSessionRecord corpKfServicerSessionRecord = corpKfServicerSessionRecordMapper.selectOne(Wrappers.lambdaQuery(CorpKfServicerSessionRecord.class)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getOpenKfId, chat.getOpenKfId())
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getExternalUserid, chat.getExternalUserid())
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getServiceState, 0)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (corpKfServicerSessionRecord != null) chat.setSessionId(corpKfServicerSessionRecord.getId());
|
|
|
|
|
+ corpKfAccountSessionChatMapper.insert(chat);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置会话记录状态
|
|
|
|
|
+ */
|
|
|
|
|
+ public void setDbSessionState(String openKfId, String servicerUserid, String externalUserId, Date sendDate, Integer changeType, String msgid, String msgCode) throws Exception {
|
|
|
|
|
+ CorpKfServicerSessionRecord corpKfServicerSessionRecord = corpKfServicerSessionRecordMapper.selectOne(Wrappers.lambdaQuery(CorpKfServicerSessionRecord.class)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getOpenKfId, openKfId)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getServicerUserid, servicerUserid)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getExternalUserid, externalUserId)
|
|
|
|
|
+ .eq(CorpKfServicerSessionRecord::getServiceState, 0)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (corpKfServicerSessionRecord != null) {
|
|
|
|
|
+ //同步用户该次接待记录
|
|
|
|
|
+ syncUserChatSession(corpKfServicerSessionRecord.getId(), externalUserId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setCloseTime(sendDate);
|
|
|
|
|
+ corpKfServicerSessionRecord.setServiceState(changeType != 2 ? 1 : changeType);
|
|
|
|
|
+ if (changeType != 2) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ wxCorpOps.sendSatisfactionMsg(corpKfServicerSessionRecord.getMsgid(), openKfId, externalUserId, msgCode);
|
|
|
|
|
+ corpKfServicerSessionRecord.setSendSatisfaction(true);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("发送满意度调查内容错误:{}", StringUtil.getErrorText(e));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.updateById(corpKfServicerSessionRecord);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ corpKfServicerSessionRecord = new CorpKfServicerSessionRecord();
|
|
|
|
|
+ corpKfServicerSessionRecord.setOpenKfId(openKfId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setServicerUserid(servicerUserid);
|
|
|
|
|
+ corpKfServicerSessionRecord.setExternalUserid(externalUserId);
|
|
|
|
|
+ corpKfServicerSessionRecord.setServiceState(changeType != 2 ? 1 : changeType);
|
|
|
|
|
+ corpKfServicerSessionRecord.setMsgid(msgid);
|
|
|
|
|
+ corpKfServicerSessionRecord.setCloseTime(sendDate);
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.insert(corpKfServicerSessionRecord);
|
|
|
|
|
+ //同步用户该次接待记录
|
|
|
|
|
+ syncUserChatSession(corpKfServicerSessionRecord.getId(), externalUserId);
|
|
|
|
|
+ //同步会话最早德发送时间
|
|
|
|
|
+ CorpKfAccountSessionChat beginChat = corpKfAccountSessionChatMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccountSessionChat.class)
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getSessionId, corpKfServicerSessionRecord.getId())
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getExternalUserid, externalUserId)
|
|
|
|
|
+ .orderByAsc(CorpKfAccountSessionChat::getId)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (beginChat != null) {
|
|
|
|
|
+ corpKfServicerSessionRecord.setStartTime(beginChat.getSendDate());
|
|
|
|
|
+ corpKfServicerSessionRecordMapper.updateById(corpKfServicerSessionRecord);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 删除等待池中用户
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param openKfId
|
|
|
|
|
+ * @param externalUserid
|
|
|
|
|
+ */
|
|
|
|
|
+ public void delQueueUser(String openKfId, String externalUserid, Date sendDate) {
|
|
|
|
|
+ //等待用户删除
|
|
|
|
|
+ Optional.ofNullable(corpKfWaitingQueueUserMapper.selectOne(Wrappers.lambdaQuery(CorpKfWaitingQueueUser.class)
|
|
|
|
|
+ .eq(CorpKfWaitingQueueUser::getOpenKfId, openKfId)
|
|
|
|
|
+ .eq(CorpKfWaitingQueueUser::getExternalUserid, externalUserid)
|
|
|
|
|
+ .le(CorpKfWaitingQueueUser::getCreatedTime, sendDate)
|
|
|
|
|
+ .last("limit 1"))).ifPresent(queueUser -> corpKfWaitingQueueUserMapper.deleteById(queueUser.getId()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取当前值班客服
|
|
|
|
|
+ */
|
|
|
|
|
+ public Set<String> getDutyFollowIds(String openKfId) throws Exception {
|
|
|
|
|
+ DateTime now = DateTime.now();
|
|
|
|
|
+ String thisHourDate = DateUtil.format(now, "HH:mm");
|
|
|
|
|
+ //星期天为1
|
|
|
|
|
+ int dayOfWeek = DateUtil.dayOfWeek(now);
|
|
|
|
|
+ //是否值班期
|
|
|
|
|
+ List<CorpKfAccountFollow> corpKfAccountFollows = corpKfAccountFollowMapper.selectList(Wrappers.lambdaQuery(CorpKfAccountFollow.class)
|
|
|
|
|
+ .eq(CorpKfAccountFollow::getOpenKfId, openKfId)
|
|
|
|
|
+ .le(CorpKfAccountFollow::getStartTime, thisHourDate)
|
|
|
|
|
+ .ge(CorpKfAccountFollow::getEndTime, thisHourDate)
|
|
|
|
|
+ .eq(CorpKfAccountFollow::getDutyWeek, dayOfWeek));
|
|
|
|
|
+
|
|
|
|
|
+ //目前时间段需要值班的客服
|
|
|
|
|
+ Set<String> thisHourDutyFollows = new HashSet<>();
|
|
|
|
|
+ for (CorpKfAccountFollow corpKfAccountFollow : corpKfAccountFollows) {
|
|
|
|
|
+ thisHourDutyFollows.addAll(Jsons.parseList(corpKfAccountFollow.getUserIdList(), String.class));
|
|
|
|
|
+ }
|
|
|
|
|
+ return thisHourDutyFollows;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 同步接待用户聊天会话id
|
|
|
|
|
+ */
|
|
|
|
|
+ public void syncUserChatSession(Long sessionId, String externalUserid) {
|
|
|
|
|
+ //同步用户该次接待记录
|
|
|
|
|
+ corpKfAccountSessionChatMapper.update(null, Wrappers.lambdaUpdate(CorpKfAccountSessionChat.class)
|
|
|
|
|
+ .set(CorpKfAccountSessionChat::getSessionId, sessionId)
|
|
|
|
|
+ .eq(CorpKfAccountSessionChat::getExternalUserid, externalUserid)
|
|
|
|
|
+ .isNull(CorpKfAccountSessionChat::getSessionId));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|