|
|
@@ -22,28 +22,31 @@ import com.fs.company.domain.*;
|
|
|
import com.fs.company.mapper.*;
|
|
|
import com.fs.company.param.ExecutionContext;
|
|
|
import com.fs.company.service.*;
|
|
|
+import com.fs.company.util.RandomNameGeneratorUtil;
|
|
|
import com.fs.company.vo.*;
|
|
|
import com.fs.company.vo.easycall.EasyCallCallPhoneVO;
|
|
|
import com.fs.crm.domain.CrmCustomer;
|
|
|
import com.fs.crm.mapper.CrmCustomerMapper;
|
|
|
import com.fs.crm.param.SmsSendBatchParam;
|
|
|
import com.fs.crm.service.impl.CrmCustomerServiceImpl;
|
|
|
+import com.fs.enums.ExecutionStatusEnum;
|
|
|
+import com.fs.enums.NodeTypeEnum;
|
|
|
+import com.fs.his.config.CidPhoneConfig;
|
|
|
import com.fs.qw.domain.QwUser;
|
|
|
import com.fs.qw.mapper.QwUserMapper;
|
|
|
import com.fs.qw.service.impl.QwExternalContactServiceImpl;
|
|
|
+import com.fs.system.domain.SysConfig;
|
|
|
+import com.fs.system.mapper.SysConfigMapper;
|
|
|
import com.fs.system.mapper.SysDictDataMapper;
|
|
|
import com.fs.system.service.ISysConfigService;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.Synchronized;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
|
-import java.util.concurrent.CompletableFuture;
|
|
|
-import java.util.concurrent.Executor;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static com.fs.company.service.impl.call.node.AiCallTaskNode.EASYCALL_WORKFLOW_REDIS_KEY;
|
|
|
@@ -107,8 +110,18 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
|
|
|
private final CompanyAiWorkflowServerMapper companyAiWorkflowServerMapper;
|
|
|
private final QwUserMapper qwUserMapper;
|
|
|
private final EasyCallMapper easyCallMapper;
|
|
|
+
|
|
|
+ private final SysConfigMapper sysConfigMapper;
|
|
|
+ private final CompanyConfigMapper companyConfigMapper;
|
|
|
+
|
|
|
+ private final CompanyWorkflowMapper companyWorkflowMapper;
|
|
|
+
|
|
|
+ private final CompanyWorkflowEdgeMapper edgeMapper;
|
|
|
+
|
|
|
private final QwExternalContactServiceImpl qwExternalContactService;
|
|
|
|
|
|
+ final int BATCH_SIZE = 1500;
|
|
|
+
|
|
|
/**
|
|
|
* 查询机器人外呼任务
|
|
|
*
|
|
|
@@ -1047,7 +1060,7 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
|
|
|
|
|
|
// 查询业务列表
|
|
|
List<CompanyVoiceRoboticBusiness> roboticBusinesseList = companyVoiceRoboticBusinessMapper
|
|
|
- .selectList(new QueryWrapper<CompanyVoiceRoboticBusiness>().eq("robotic_id", id));
|
|
|
+ .selectList(new QueryWrapper<CompanyVoiceRoboticBusiness>().eq("robotic_id", id).eq("is_generate",0));
|
|
|
|
|
|
if (roboticBusinesseList.isEmpty()) {
|
|
|
log.warn("任务没有业务数据: {}", id);
|
|
|
@@ -1175,12 +1188,43 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
|
|
|
return resArr;
|
|
|
}
|
|
|
|
|
|
- public void buildTaskBussiness(CompanyVoiceRobotic robotic){
|
|
|
- List<CompanyVoiceRoboticCallees> calleesList = companyVoiceRoboticCalleesMapper.selectByRoboticId(robotic.getId());
|
|
|
+ public void buildTaskBussiness(CompanyVoiceRobotic robotic){ List<CompanyVoiceRoboticCallees> calleesList = companyVoiceRoboticCalleesMapper.selectByRoboticId(robotic.getId());
|
|
|
+ //获取电话生成配置
|
|
|
+ CidPhoneConfig phoneConfig = null;
|
|
|
+ //获取销售公司手机配置
|
|
|
+ CompanyConfig companyConfig = companyConfigMapper.selectCompanyConfigByKey(robotic.getCompanyId(),"cid.config");
|
|
|
+ //如果配置为空就获取总后台配置
|
|
|
+ if(companyConfig == null){
|
|
|
+ SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("cid.config");
|
|
|
+ if(sysConfig != null){
|
|
|
+ phoneConfig = JSONObject.parseObject(sysConfig.getConfigValue(),CidPhoneConfig.class);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ phoneConfig = JSONObject.parseObject(companyConfig.getConfigValue(),CidPhoneConfig.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取工作流主表信息
|
|
|
+ CompanyWorkflow workflow = companyWorkflowMapper.selectCompanyWorkflowById(robotic.getCompanyAiWorkflowId());
|
|
|
+
|
|
|
+ //获取相关节点信息
|
|
|
+ CompanyNodeInfoVo nodeInfoVo = edgeMapper.slectNodeInfoByWorkflowId(workflow.getWorkflowId(),workflow.getStartNodeKey());
|
|
|
+
|
|
|
List<CompanyWxClient> companyWxClients = companyWxClientMapper.selectListByRoboticId(robotic.getId());
|
|
|
Map<String, CompanyWxClient> clientMp = companyWxClients.stream().collect(Collectors.toMap(e -> e.getRoboticId() + "-" + e.getCustomerId(), e -> e));
|
|
|
List<CompanyVoiceRoboticBusiness> addList = new ArrayList<>();
|
|
|
+ List<CompanyVoiceRoboticCallees> batchToInsert = new LinkedList<>();
|
|
|
for (CompanyVoiceRoboticCallees callees : calleesList) {
|
|
|
+ //根据配置随机生成电话号
|
|
|
+ if(phoneConfig != null && phoneConfig.getEnablePhoneConfig()){//配置不为空并且开启了
|
|
|
+ List<CompanyVoiceRoboticCallees> roboticCallees = generatePhoneNumber(phoneConfig,callees);
|
|
|
+ if(!roboticCallees.isEmpty()){
|
|
|
+ batchToInsert.addAll(roboticCallees);
|
|
|
+ if(batchToInsert.size() >= BATCH_SIZE){
|
|
|
+ flushGeneratedCalleesBatch(new LinkedList<>(batchToInsert), clientMp, robotic,workflow,nodeInfoVo);
|
|
|
+ batchToInsert.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
CompanyVoiceRoboticBusiness companyVoiceRoboticBusiness = new CompanyVoiceRoboticBusiness();
|
|
|
companyVoiceRoboticBusiness.setRoboticId(robotic.getId());
|
|
|
companyVoiceRoboticBusiness.setCalleeId(callees.getId());
|
|
|
@@ -1191,7 +1235,15 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
|
|
|
companyVoiceRoboticBusiness.setCreateTime(new Date());
|
|
|
addList.add(companyVoiceRoboticBusiness);
|
|
|
}
|
|
|
+
|
|
|
+ //处理剩余数据
|
|
|
+ if(!batchToInsert.isEmpty()){
|
|
|
+ flushGeneratedCalleesBatch(new LinkedList<>(batchToInsert), clientMp, robotic,workflow,nodeInfoVo);
|
|
|
+ batchToInsert.clear();
|
|
|
+ }
|
|
|
+
|
|
|
companyVoiceRoboticBusinessMapper.insertBatch(addList);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
private void taskJoin2Workflow(){
|
|
|
@@ -1315,4 +1367,219 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
|
|
|
return vo;
|
|
|
}).collect(Collectors.toList());
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据配置生成手机号
|
|
|
+ * @param config 配置对象(包含是否启用、生成数量、起始位置、结束位置)
|
|
|
+ * @param callees 任务外呼电话对象
|
|
|
+ * @return 生成的手机号列表
|
|
|
+ */
|
|
|
+ public static List<CompanyVoiceRoboticCallees> generatePhoneNumber(CidPhoneConfig config,CompanyVoiceRoboticCallees callees) {
|
|
|
+ String basePhone = callees.getPhone();
|
|
|
+ if (basePhone == null || basePhone.length() != 11) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ int start = config.getStartIndex();
|
|
|
+ int end = config.getEndIndex();
|
|
|
+ int count = config.getGenerateCount();
|
|
|
+
|
|
|
+ // 校验索引范围
|
|
|
+ if (start < 1 || start > 11 || end < 1 || end > 11 || start > end) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ int startIdx = start;
|
|
|
+ int endIdx = end-1;
|
|
|
+
|
|
|
+ char[] baseChars = basePhone.toCharArray();
|
|
|
+ //预加载随机
|
|
|
+ List<String> nameList = RandomNameGeneratorUtil.generateBatch(count);
|
|
|
+ Random random = new Random();
|
|
|
+ List<CompanyVoiceRoboticCallees> result = new ArrayList<>(count);
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ CompanyVoiceRoboticCallees roboticCallees = new CompanyVoiceRoboticCallees();
|
|
|
+ // 克隆基础数组
|
|
|
+ char[] newChars = baseChars.clone();
|
|
|
+ for (int j = startIdx; j <= endIdx; j++) {
|
|
|
+ // 生成0-9的随机数字字符
|
|
|
+ newChars[j] = (char) ('0' + random.nextInt(10));
|
|
|
+ }
|
|
|
+ String phone = String.valueOf(newChars);
|
|
|
+ roboticCallees.setPhone(phone);//电话
|
|
|
+ roboticCallees.setRoboticId(callees.getRoboticId());//任务ID
|
|
|
+ roboticCallees.setTaskFlow(callees.getTaskFlow());//任务流程
|
|
|
+ roboticCallees.setRunTaskFlow(callees.getRunTaskFlow());//已执行流程
|
|
|
+ roboticCallees.setIsWeCom(callees.getIsWeCom());
|
|
|
+ roboticCallees.setUserId(0L);//用户ID
|
|
|
+ roboticCallees.setUserName(nameList.get(i));//姓名
|
|
|
+ roboticCallees.setIsGenerate(1);
|
|
|
+ result.add(roboticCallees);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成外呼任务表
|
|
|
+ * **/
|
|
|
+ public void generateVoiceRoboticBusiness(List<CompanyVoiceRoboticCallees> calleesList,
|
|
|
+ Map<String, CompanyWxClient> clientMp
|
|
|
+ ,CompanyVoiceRobotic robotic
|
|
|
+ ,CompanyWorkflow workflow
|
|
|
+ ,CompanyNodeInfoVo nodeInfoVo){
|
|
|
+ if(!calleesList.isEmpty()){
|
|
|
+ List<CompanyVoiceRoboticBusiness> addList = new LinkedList<>();
|
|
|
+ Date date =new Date();
|
|
|
+ for (CompanyVoiceRoboticCallees callees : calleesList){
|
|
|
+ CompanyVoiceRoboticBusiness companyVoiceRoboticBusiness = new CompanyVoiceRoboticBusiness();
|
|
|
+ companyVoiceRoboticBusiness.setRoboticId(callees.getRoboticId());
|
|
|
+ companyVoiceRoboticBusiness.setCalleeId(callees.getId());
|
|
|
+ companyVoiceRoboticBusiness.setWxClientId(clientMp.getOrDefault(callees.getRoboticId() + "-" + callees.getUserId(),new CompanyWxClient()).getId());
|
|
|
+ companyVoiceRoboticBusiness.setAddWxDone(0);
|
|
|
+ companyVoiceRoboticBusiness.setCallPhoneDone(0);
|
|
|
+ companyVoiceRoboticBusiness.setSendMsgDone(0);
|
|
|
+ companyVoiceRoboticBusiness.setCreateTime(date);
|
|
|
+ companyVoiceRoboticBusiness.setIsGenerate(1);
|
|
|
+ addList.add(companyVoiceRoboticBusiness);
|
|
|
+ }
|
|
|
+
|
|
|
+ //批量插入ai外呼业务对象
|
|
|
+ if(!addList.isEmpty()){
|
|
|
+ flushGeneratedBusinessBatch(addList,robotic,workflow,nodeInfoVo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新并批量插入生成的被叫数据,并生成对应的业务对象
|
|
|
+ * @param batchToInsert 待插入的被叫列表
|
|
|
+ * @param clientMap 微信客户映射,用于关联客户ID
|
|
|
+ */
|
|
|
+ @Async("calleeTaskExecutor")
|
|
|
+ public void flushGeneratedCalleesBatch(List<CompanyVoiceRoboticCallees> batchToInsert,
|
|
|
+ Map<String, CompanyWxClient> clientMap,
|
|
|
+ CompanyVoiceRobotic robotic,
|
|
|
+ CompanyWorkflow workflow,
|
|
|
+ CompanyNodeInfoVo nodeInfoVo) {
|
|
|
+ if (batchToInsert.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ int rows = companyVoiceRoboticCalleesMapper.batchInsertGenerateInfo(batchToInsert);
|
|
|
+ if (rows > 0) {
|
|
|
+ generateVoiceRoboticBusiness(batchToInsert, clientMap,robotic,workflow,nodeInfoVo);
|
|
|
+ }
|
|
|
+ batchToInsert.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量插入业务对象,并创建工作流执行记录
|
|
|
+ */
|
|
|
+ private void flushGeneratedBusinessBatch(List<CompanyVoiceRoboticBusiness> batchToInsert,CompanyVoiceRobotic robotic,CompanyWorkflow workflow,CompanyNodeInfoVo nodeInfoVo){
|
|
|
+ int rows = companyVoiceRoboticBusinessMapper.insertBatchGenerateInfo(batchToInsert);
|
|
|
+ if(rows > 0){
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ List<CompanyAiWorkflowExec> workflowExecs = new LinkedList<>();
|
|
|
+ //插入执行流程代码
|
|
|
+ for (CompanyVoiceRoboticBusiness business : batchToInsert){
|
|
|
+ // 第一个节点(开始节点)
|
|
|
+ CompanyAiWorkflowExec startExec = new CompanyAiWorkflowExec();
|
|
|
+ startExec.setWorkflowInstanceId(generateInstanceId());
|
|
|
+ startExec.setWorkflowId(robotic.getCompanyAiWorkflowId());
|
|
|
+ startExec.setCurrentNodeKey(workflow.getStartNodeKey());
|
|
|
+ startExec.setCurrentNodeType(NodeTypeEnum.START.getValue());
|
|
|
+ startExec.setCurrentNodeName(NodeTypeEnum.START.getDescription());
|
|
|
+ startExec.setStatus(ExecutionStatusEnum.SUCCESS.getValue()); // 开始节点执行成功
|
|
|
+ startExec.setStartTime(now);
|
|
|
+
|
|
|
+ JSONObject variables = new JSONObject();
|
|
|
+ variables.put("roboticId", robotic.getId());
|
|
|
+ variables.put("businessId", business.getId());
|
|
|
+ variables.put("cidGroupNo", robotic.getCidGroupNo());
|
|
|
+ variables.put("runtimeRangeStart", robotic.getRuntimeRangeStart());
|
|
|
+ variables.put("runtimeRangeEnd", robotic.getRuntimeRangeEnd());
|
|
|
+ startExec.setVariables(variables.toJSONString());
|
|
|
+ startExec.setBusinessKey(business.getId().toString());
|
|
|
+ startExec.setStartNodeKey(workflow.getStartNodeKey());
|
|
|
+ startExec.setEndNodeKey(workflow.getEndNodeKey());
|
|
|
+ startExec.setCidGroupNo(robotic.getCidGroupNo());
|
|
|
+ startExec.setRuntimeRangeStart(robotic.getRuntimeRangeStart());
|
|
|
+ startExec.setRuntimeRangeEnd(robotic.getRuntimeRangeEnd());
|
|
|
+ startExec.setIsGenerate(1);
|
|
|
+ workflowExecs.add(startExec);
|
|
|
+ CompanyAiWorkflowExec targetExec = new CompanyAiWorkflowExec();
|
|
|
+
|
|
|
+ // 复制公共字段
|
|
|
+ targetExec.setWorkflowInstanceId(generateInstanceId());
|
|
|
+ targetExec.setWorkflowId(startExec.getWorkflowId());
|
|
|
+ targetExec.setCurrentNodeKey(nodeInfoVo.getTargetNodeKey());
|
|
|
+ targetExec.setCurrentNodeName(nodeInfoVo.getNodeName());
|
|
|
+ targetExec.setCurrentNodeType(NodeTypeEnum.fromCode(nodeInfoVo.getNodeType()).getValue());
|
|
|
+ targetExec.setStatus(ExecutionStatusEnum.FAILURE.getValue());
|
|
|
+ targetExec.setStartTime(now);
|
|
|
+ targetExec.setVariables(variables.toJSONString());
|
|
|
+ targetExec.setBusinessKey(startExec.getBusinessKey());
|
|
|
+ targetExec.setStartNodeKey(startExec.getStartNodeKey());
|
|
|
+ targetExec.setEndNodeKey(startExec.getEndNodeKey());
|
|
|
+ targetExec.setCidGroupNo(startExec.getCidGroupNo());
|
|
|
+ targetExec.setRuntimeRangeStart(startExec.getRuntimeRangeStart());
|
|
|
+ targetExec.setRuntimeRangeEnd(startExec.getRuntimeRangeEnd());
|
|
|
+ targetExec.setIsGenerate(1);
|
|
|
+ workflowExecs.add(targetExec);
|
|
|
+
|
|
|
+ if(workflowExecs.size() >= BATCH_SIZE){
|
|
|
+ generateVoiceRoboticCurrentExecLogs(new LinkedList<>(workflowExecs));
|
|
|
+ workflowExecs.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!workflowExecs.isEmpty()){
|
|
|
+ generateVoiceRoboticCurrentExecLogs(new LinkedList<>(workflowExecs));
|
|
|
+ workflowExecs.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ batchToInsert.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void generateVoiceRoboticCurrentExecLogs(List<CompanyAiWorkflowExec> workflowExecs){
|
|
|
+ if (workflowExecs.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int rows = companyAiWorkflowExecMapper.insertBatchInfo(workflowExecs);
|
|
|
+ if(rows > 0){
|
|
|
+
|
|
|
+ //插入日志记录表
|
|
|
+ Date date =new Date();
|
|
|
+ List<CompanyAiWorkflowExecLog> batch = new LinkedList<>();
|
|
|
+ workflowExecs.forEach(w->{
|
|
|
+ CompanyAiWorkflowExecLog execLog = new CompanyAiWorkflowExecLog();
|
|
|
+ execLog.setWorkflowInstanceId(w.getWorkflowInstanceId());
|
|
|
+ execLog.setNodeKey(w.getCurrentNodeKey());
|
|
|
+ execLog.setNodeName(w.getCurrentNodeName());
|
|
|
+ execLog.setNodeType(w.getCurrentNodeType());
|
|
|
+ execLog.setInputData(w.getVariables());
|
|
|
+ execLog.setStatus(w.getStatus());
|
|
|
+ execLog.setOutputData("null");
|
|
|
+ execLog.setStartTime(date);
|
|
|
+ execLog.setEndTime(date);
|
|
|
+ execLog.setIsGenerate(1);
|
|
|
+ batch.add(execLog);
|
|
|
+ });
|
|
|
+ if(!batch.isEmpty()){
|
|
|
+ companyAiWorkflowExecLogMapper.batchInsert(batch);
|
|
|
+ batch.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ workflowExecs.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成工作流实例ID
|
|
|
+ */
|
|
|
+ private String generateInstanceId() {
|
|
|
+ return "wf_" + System.currentTimeMillis() + "_" +
|
|
|
+ UUID.randomUUID().toString().replace("-", "");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
}
|