lmx 3 weeks ago
parent
commit
9f493adde8

+ 2 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyVoiceRoboticCallLogCallphone.java

@@ -28,6 +28,8 @@ public class CompanyVoiceRoboticCallLogCallphone extends BaseEntity{
     @TableId(value = "log_id", type = IdType.AUTO)
     private Long logId;
 
+    private String callbackUuid;
+
     /** 任务id */
     @Excel(name = "任务id")
     private Long roboticId;

+ 2 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyVoiceRoboticCallLogCallphoneMapper.java

@@ -66,4 +66,6 @@ public interface CompanyVoiceRoboticCallLogCallphoneMapper extends BaseMapper<Co
     CompanyVoiceRoboticCallLogCallphone selectLogByRoboticIdAndCallerId(@Param("roboticId") Long roboticId,@Param("callerId") Long callerId);
 
     List<CompanyVoiceRoboticCallLogCallphone>  selectCompanyVoiceRoboticCallLogCallphoneListData(CompanyVoiceRoboticCallLogCallphone companyVoiceRoboticCallLogCallphone);
+
+    CompanyVoiceRoboticCallLogCallphone selectCallLogByCallbackUuid(@Param("uuid") String uuid);
 }

+ 13 - 0
fs-service/src/main/java/com/fs/company/param/ExecutionContext.java

@@ -22,6 +22,7 @@ public class ExecutionContext {
     /**
      * 当前节点ID
      */
+    @Deprecated
     private Long currentNodeId;
 
     /**
@@ -89,4 +90,16 @@ public class ExecutionContext {
         }
         return type.isInstance(value) ? type.cast(value) : null;
     }
+    public ExecutionContext clone() {
+        ExecutionContext cloned = new ExecutionContext();
+        cloned.setWorkflowInstanceId(this.workflowInstanceId);
+        cloned.setCurrentNodeId(this.currentNodeId);
+        cloned.setCurrentNodeKey(this.currentNodeKey);
+        cloned.setVariables(new HashMap<>(this.variables)); // 深拷贝本地变量
+        cloned.setGlobalVariables(new HashMap<>(this.globalVariables)); // 深拷贝全局变量
+        cloned.setStartTime(this.startTime);
+        cloned.setCurrentTime(this.currentTime);
+        cloned.setBusinessId(this.businessId);
+        return cloned;
+    }
 }

+ 2 - 1
fs-service/src/main/java/com/fs/company/service/ICompanyVoiceRoboticService.java

@@ -5,6 +5,7 @@ import com.fs.aicall.domain.apiresult.PushIIntentionResult;
 import com.fs.aicall.domain.result.CalltaskcreateaiCustomizeResult;
 import com.fs.company.domain.CompanyVoiceRobotic;
 import com.fs.company.vo.AddWxClientVo;
+import com.fs.company.vo.AiCallConfigVO;
 import com.fs.company.vo.CompanyVoiceRoboticQwUserListVo;
 
 import java.util.List;
@@ -43,7 +44,7 @@ public interface ICompanyVoiceRoboticService extends IService<CompanyVoiceRoboti
     public int insertCompanyVoiceRobotic(CompanyVoiceRobotic companyVoiceRobotic);
     CalltaskcreateaiCustomizeResult addTask(CompanyVoiceRobotic companyVoiceRobotic);
     CalltaskcreateaiCustomizeResult callPhoneOne(Long roboticId,Long callerId);
-    CalltaskcreateaiCustomizeResult workflowCallPhoneOne(Long roboticId,Long callerId,String workflowInstanceId);
+    CalltaskcreateaiCustomizeResult workflowCallPhoneOne(Long roboticId,Long callerId,String workflowInstanceId, AiCallConfigVO callConfigVo);
     void sendMsgOne(Long roboticId,Long callerId);
 
     /**

+ 14 - 9
fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticServiceImpl.java

@@ -303,27 +303,28 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
      * @param roboticId
      * @param callerId
      * @param workflowInstanceId
+     * @param callConfigVo
      * @return
      */
     @Override
-    public CalltaskcreateaiCustomizeResult workflowCallPhoneOne(Long roboticId, Long callerId, String workflowInstanceId) {
+    public CalltaskcreateaiCustomizeResult workflowCallPhoneOne(Long roboticId, Long callerId, String workflowInstanceId,AiCallConfigVO callConfigVo) {
         try {
             CompanyVoiceRobotic robotic = companyVoiceRoboticMapper.selectById(roboticId);
             CompanyVoiceRoboticCallees callees = companyVoiceRoboticCalleesMapper.selectById(callerId);
             CalleeDomain build = CalleeDomain.builder().number(callees.getPhone()).userData(callees.getId().toString()).build();
             List<CalleeDomain> mobileList = new ArrayList<>();
             mobileList.add(build);
-
+            String callBackUuid = UUID.randomUUID().toString();
             // 构建三方接口请求数据
             CalltaskcreateaiCustomizeDomain param = new CalltaskcreateaiCustomizeDomain();
-            param.setRobot(robotic.getRobot());
-            param.setDialogID(robotic.getDialogId());
-            param.setMode(robotic.getMode());
+            param.setRobot(callConfigVo.getRobot());
+            param.setDialogID(callConfigVo.getDialogId());
+            param.setMode(callConfigVo.getMode());
             param.setCallees(mobileList);
             param.setTaskName(robotic.getName());
-            param.setMultiplier(robotic.getMultiplier());
-            param.setAutoRecall(robotic.getAutoRecall());
-            param.setRecallTimes(robotic.getRecallTimes());
+            param.setMultiplier(callConfigVo.getMultiplier());
+            param.setAutoRecall(callConfigVo.getAutoRecall());
+            param.setRecallTimes(callConfigVo.getRecallTimes());
             if (StringUtils.isNotEmpty(robotic.getWeekDay1())) {
                 param.setWeekday1(Arrays.asList(robotic.getWeekDay1().split(",")));
             }
@@ -333,7 +334,10 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
             if (StringUtils.isNotEmpty(robotic.getEndTime1())) {
                 param.setEndTime1(robotic.getEndTime1() + ":00");
             }
-            param.setUserData(workflowInstanceId);
+            JSONObject userDataJson=new JSONObject();
+            userDataJson.put("callBackUuid",callBackUuid);
+            userDataJson.put("workflowInstanceId",workflowInstanceId);
+            param.setUserData(userDataJson.toJSONString());
             JSONObject runParam = (JSONObject) JSON.toJSON(param);
             runParam.put("companyId", robotic.getCompanyId());
             CompanyVoiceRoboticCallLogCallphone addLog = CompanyVoiceRoboticCallLogCallphone.initCallLog(
@@ -341,6 +345,7 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
             // 请求外呼接口
             CalltaskcreateaiCustomizeResult result = aiCallService.calltaskcreateaiCustomize(param, robotic.getCompanyId());
             addLog.setStatus(1);
+            addLog.setCallbackUuid(callBackUuid);
             companyVoiceRoboticCallLogCallphoneService.asyncInsertCompanyVoiceRoboticCallLog(addLog);
             // 设置返回数据
 //            robotic.setTaskId(result.getTaskID());

+ 29 - 3
fs-service/src/main/java/com/fs/company/service/impl/call/node/AbstractWorkflowNode.java

@@ -2,6 +2,7 @@ package com.fs.company.service.impl.call.node;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.spring.SpringUtils;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
@@ -29,6 +30,9 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
     public static final CompanyAiWorkflowExecLogMapper companyAiWorkflowExecLogMapper = SpringUtils.getBean(CompanyAiWorkflowExecLogMapper.class);
     public static final CompanyWorkflowEdgeMapper companyWorkflowEdgeMapper = SpringUtils.getBean(CompanyWorkflowEdgeMapper.class);
     public static final CompanyVoiceRoboticBusinessMapper companyVoiceRoboticBusinessMapper = SpringUtils.getBean(CompanyVoiceRoboticBusinessMapper.class);
+    public static final CompanyVoiceRoboticCallLogCallphoneMapper companyVoiceRoboticCallLogCallphoneMapper = SpringUtils.getBean(CompanyVoiceRoboticCallLogCallphoneMapper.class);
+    public static final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
+
     public static final ObjectMapper objectMapper = new ObjectMapper();
 
     protected String nodeKey;
@@ -168,7 +172,7 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
 
     public CompanyVoiceRoboticBusiness getRoboticBusiness(String workflowInstanceId) {
         CompanyVoiceRoboticBusiness companyVoiceRoboticBusiness = companyVoiceRoboticBusinessMapper.selectCompanyVoiceRoboticBusinessByWorkflowInstanceId(workflowInstanceId);
-        if(null == companyVoiceRoboticBusiness){
+        if (null == companyVoiceRoboticBusiness) {
             throw new RuntimeException("缺失业务数据");
         }
         return companyVoiceRoboticBusiness;
@@ -248,11 +252,11 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
      * @param nodeKey
      * @param context
      */
-    public void pauseWorkflowForBlockingNode(String workflowInstanceId, String nodeKey, ExecutionContext context) {
+    public void asyncWorkflowForBlockingNode(String workflowInstanceId, String nodeKey, ExecutionContext context, ExecutionStatusEnum status) {
         try {
             CompanyAiWorkflowExec update = new CompanyAiWorkflowExec();
             update.setWorkflowInstanceId(workflowInstanceId);
-            update.setStatus(ExecutionStatusEnum.WAITING.getValue());
+            update.setStatus(status.getValue());
             update.setLastUpdateTime(LocalDateTime.now());
             update.setVariables(objectMapper.writeValueAsString(context.getVariables()));
             companyAiWorkflowExecMapper.updateByWorkflowInstanceId(update);
@@ -283,4 +287,26 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
         }
         return companyAiWorkflowExec;
     }
+
+    protected ExecutionContext createExecutionContext(String workflowInstanceId, String nodeKey) {
+        ExecutionContext context = new ExecutionContext();
+        context.setWorkflowInstanceId(workflowInstanceId);
+        context.setCurrentNodeKey(nodeKey);
+        return context;
+    }
+
+    protected void execPointNextNode(ExecutionContext context) {
+        try {
+            CompanyAiWorkflowExec update = new CompanyAiWorkflowExec();
+            update.setWorkflowInstanceId(context.getWorkflowInstanceId());
+            update.setCurrentNodeKey(context.getCurrentNodeKey());
+            update.setStatus(ExecutionStatusEnum.RUNNING.getValue());
+            update.setLastUpdateTime(LocalDateTime.now());
+            update.setVariables(objectMapper.writeValueAsString(context.getVariables()));
+            companyAiWorkflowExecMapper.updateByWorkflowInstanceId(update);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
 }

+ 50 - 24
fs-service/src/main/java/com/fs/company/service/impl/call/node/AiCallTaskNode.java

@@ -4,18 +4,18 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.aicall.domain.result.CalltaskcreateaiCustomizeResult;
 import com.fs.common.utils.spring.SpringUtils;
-import com.fs.company.domain.CompanyAiWorkflowExec;
-import com.fs.company.domain.CompanyVoiceRoboticBusiness;
-import com.fs.company.domain.CompanyVoiceRoboticCallees;
-import com.fs.company.domain.CompanyWorkflowEdge;
+import com.fs.company.domain.*;
 import com.fs.company.mapper.CompanyWorkflowNodeMapper;
 import com.fs.company.param.ExecutionContext;
 import com.fs.company.service.ICompanyVoiceRoboticService;
 import com.fs.company.service.impl.CompanyVoiceRoboticServiceImpl;
+import com.fs.company.vo.AiCallConfigVO;
 import com.fs.company.vo.AiCallWorkflowConditionVo;
 import com.fs.company.vo.ExecutionResult;
+import com.fs.enums.ExecutionStatusEnum;
 import com.fs.enums.NodeTypeEnum;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -27,6 +27,7 @@ import java.util.Map;
 public class AiCallTaskNode extends AbstractWorkflowNode {
     private static final CompanyWorkflowNodeMapper companyWorkflowNodeMapper = SpringUtils.getBean(CompanyWorkflowNodeMapper.class);
     private static final ICompanyVoiceRoboticService companyVoiceRoboticService = SpringUtils.getBean(ICompanyVoiceRoboticService.class);
+    public static final String DELAY_CALL_KEY = "aiCallTask:delay:%s:%s:";
 
     public AiCallTaskNode(String nodeKey, String nodeName, Map<String, Object> properties) {
         super(nodeKey, nodeName, properties);
@@ -40,45 +41,58 @@ public class AiCallTaskNode extends AbstractWorkflowNode {
      */
     @Override
     protected ExecutionResult doContinue(ExecutionContext context) {
-
         CompanyAiWorkflowExec exec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(context.getWorkflowInstanceId());
-
         List<CompanyWorkflowEdge> edges = companyWorkflowEdgeMapper.selectListByWorkflowIdAndNodeKey(exec.getWorkflowId(), nodeKey);
-        //获取外呼回调结果
-        CompanyVoiceRoboticCallees callees = super.companyVoiceRoboticBusinessMapper.selectCalleesByBusinessId(Long.valueOf(exec.getBusinessKey()));
+        //获取外呼回调结果日志
+        CompanyVoiceRoboticCallLogCallphone callRes = super.companyVoiceRoboticCallLogCallphoneMapper.selectCallLogByCallbackUuid(context.getVariable("callbackUuid", String.class));
         edges.forEach(edge -> {
             AiCallWorkflowConditionVo condition = JSONObject.parseObject(edge.getConditionExpr(), AiCallWorkflowConditionVo.class);
+            //拨通
+            if (condition.isCallConnected() && callRes.getCallTime() > 0) {
+                //如果含有意向度过滤
+                if(null != condition.getIntention()){
+                   if( condition.getIntention().equals(callRes.getIntention())){
+                       this.runNextNode(context,edge);
+                   }
+                } else {
+                    this.runNextNode(context,edge);
+                }
+            }
             //未拨通
-            if (!condition.isCallConnected()) {
+            else if (!condition.isCallConnected() && (Long.valueOf(0).equals(callRes.getCallTime()) || callRes.getCallAnswerTime() == null)) {
+                //延时操作
                 if (null != condition.getCallTime()) {
-
+                    //计算延时分片分钟
+                    Date nowDay = new Date(System.currentTimeMillis() + condition.getCallTime() * 60 * 1000);
+                    //添加到延时扫描redis
+                    super.redisCache.setCacheObject(String.format(DELAY_CALL_KEY, nowDay.getHours(), nowDay.getMinutes()) + exec.getWorkflowInstanceId(), context);
+                    super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), context, ExecutionStatusEnum.WAITING);
+                }
+                //无时间驱动
+                else {
+                    this.runNextNode(context,edge);
                 }
-            }
-            //拨通
-            else {
-
             }
         });
-        //加入到计时组
-
-        //同步监听回调回调
         return null;
     }
 
     @Override
     protected ExecutionResult doExecute(ExecutionContext context) {
         if (isAsync()) {
-
+            //当前节点信息
+            CompanyWorkflowNode node = companyWorkflowNodeMapper.selectNodeByNodeKey(context.getCurrentNodeKey());
+            String nodeConfig = node.getNodeConfig();
+            AiCallConfigVO callConfigVo = JSONObject.parseObject(nodeConfig, AiCallConfigVO.class);
             //执行外呼逻辑 需要传入节点信息
             CompanyVoiceRoboticBusiness bus = super.getRoboticBusiness(context.getWorkflowInstanceId());
-            CalltaskcreateaiCustomizeResult result = companyVoiceRoboticService.workflowCallPhoneOne(bus.getRoboticId(), bus.getCalleeId(), context.getWorkflowInstanceId());
-
-
+            companyVoiceRoboticService.workflowCallPhoneOne(bus.getRoboticId(), bus.getCalleeId(), context.getWorkflowInstanceId(), callConfigVo);
+            super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), context, ExecutionStatusEnum.PAUSED);
             return ExecutionResult.paused()
-                    .nextNodeKey(getNextNodeKey(context.getWorkflowInstanceId(), nodeKey)).build();
+                    .nextNodeKey("").build();
         } else {
-            return ExecutionResult.success()
-                    .nextNodeKey(getNextNodeKey(context.getWorkflowInstanceId(), nodeKey)).build();
+            return ExecutionResult.failure()
+                    .nextNodeKey("").build();
         }
 
     }
@@ -116,4 +130,16 @@ public class AiCallTaskNode extends AbstractWorkflowNode {
         //没有满足的节点
         return null;
     }
+
+    /**
+     * 运行下一个节点
+     * @param context
+     * @param edge
+     */
+    private void runNextNode(ExecutionContext context, CompanyWorkflowEdge edge){
+        ExecutionContext nextContext = context.clone();
+        nextContext.setCurrentNodeKey(edge.getTargetNodeKey());
+        super.execPointNextNode(nextContext);
+        super.execute(nextContext);
+    }
 }

+ 41 - 0
fs-service/src/main/java/com/fs/company/vo/AiCallConfigVO.java

@@ -0,0 +1,41 @@
+package com.fs.company.vo;
+
+import lombok.Data;
+
+/**
+ * @author MixLiu
+ * @date 2026/2/3 10:13
+ * @description
+ */
+@Data
+public class AiCallConfigVO {
+    /**
+     * 模式
+     */
+    private Long mode;
+    /**
+     * 呼叫倍率
+     */
+    private Long multiplier;
+    /**
+     * 自动重呼
+     */
+    private Long autoRecall;
+    /**
+     * 重呼次数
+     */
+    private Long recallTimes;
+    /**
+     * 机器人
+     */
+    private Long robot;
+    /**
+     * 话术
+     */
+    private Long dialogId;
+    /**
+     * 主叫分组
+     */
+    private String cidGroupId;
+
+}

+ 1 - 1
fs-service/src/main/java/com/fs/company/vo/ExecutionResult.java

@@ -34,7 +34,7 @@ public class ExecutionResult {
     }
     public static ExecutionResultBuilder paused(){
         ExecutionResult result = new ExecutionResult();
-        return ExecutionResult.builder().success(true).status(ExecutionStatusEnum.WAITING);
+        return ExecutionResult.builder().success(true).status(ExecutionStatusEnum.PAUSED);
     }
 
 }

+ 5 - 1
fs-service/src/main/java/com/fs/enums/ExecutionStatusEnum.java

@@ -39,7 +39,11 @@ public enum ExecutionStatusEnum {
     /**
      * 超时
      */
-    TIMEOUT("TIMEOUT", "执行超时", 7);
+    TIMEOUT("TIMEOUT", "执行超时", 7),
+    /**
+     * 中断
+     */
+    TIMEOUT("TIMEOUT", "执行中断", 7);
 
     private final String code;
     private final String description;

+ 7 - 0
fs-service/src/main/resources/mapper/company/CompanyVoiceRoboticCallLogCallphoneMapper.xml

@@ -63,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <insert id="insertCompanyVoiceRoboticCallLogCallphone" parameterType="CompanyVoiceRoboticCallLogCallphone" useGeneratedKeys="true" keyProperty="logId">
         insert into company_voice_robotic_call_log_callphone
         <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="callbackUuid != null">callback_uuid,</if>
             <if test="roboticId != null">robotic_id,</if>
             <if test="callerId != null">caller_id,</if>
             <if test="runTime != null">run_time,</if>
@@ -84,6 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="cost != null">cost,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="callbackUuid != null">#{callbackUuid},</if>
             <if test="roboticId != null">#{roboticId},</if>
             <if test="callerId != null">#{callerId},</if>
             <if test="runTime != null">#{runTime},</if>
@@ -170,4 +172,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
         </where>
     </select>
+
+
+    <select id="selectCallLogByCallbackUuid" resultType="CompanyVoiceRoboticCallLogCallphone">
+        select * from company_voice_robotic_call_log_callphone where callback_uuid = #{uuid}
+    </select>
 </mapper>