瀏覽代碼

Merge remote-tracking branch 'origin/master-ai-cell' into master-ai-cell

lmx 3 周之前
父節點
當前提交
0a0ce823cd

+ 2 - 3
fs-service/src/main/java/com/fs/company/param/CompanyWorkflowSaveParam.java

@@ -28,15 +28,14 @@ public class CompanyWorkflowSaveParam implements Serializable {
 
     /** 工作流类型 */
     private Integer workflowType;
+    private String startNodeKey;
+    private String endNodeKey;
 
     /** 画布数据JSON */
     private String canvasData;
     private Long companyId;
     private Long companyUserId;
 
-    private String startNodeKey = "start";
-    private String endNodeKey;
-
     /** 节点列表 */
     private List<CompanyWorkflowNode> nodes;
 

+ 0 - 3
fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticServiceImpl.java

@@ -34,9 +34,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.support.TransactionSynchronization;
-import org.springframework.transaction.support.TransactionSynchronizationManager;
 
 import java.util.*;
 import java.util.concurrent.CompletableFuture;

+ 1 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowServiceImpl.java

@@ -86,9 +86,9 @@ public class CompanyWorkflowServiceImpl implements ICompanyWorkflowService {
             workflow.setWorkflowDesc(param.getWorkflowDesc());
             workflow.setWorkflowType(param.getWorkflowType());
             workflow.setCanvasData(param.getCanvasData());
-            workflow.setUpdateTime(now);
             workflow.setStartNodeKey(param.getStartNodeKey());
             workflow.setEndNodeKey(param.getEndNodeKey());
+            workflow.setUpdateTime(now);
             companyWorkflowMapper.updateCompanyWorkflow(workflow);
             // 删除旧的节点和连线
             companyWorkflowNodeMapper.deleteCompanyWorkflowNodeByWorkflowId(workflowId);

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

@@ -75,8 +75,16 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
     public ExecutionResult continueExecute(ExecutionContext context) {
         try {
             CompanyAiWorkflowExec companyAiWorkflowExec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(context.getWorkflowInstanceId());
-            if (!Integer.valueOf(ExecutionStatusEnum.PAUSED.getValue()).equals(companyAiWorkflowExec.getStatus())) {
-                return handleExecutionError(new CustomException("状态不符合"), context);
+            log.info("收到继续执行请求 - workflowInstanceId: {}, nodeKey: {}, 当前状态: {}",
+                    context.getWorkflowInstanceId(), context.getCurrentNodeKey(), companyAiWorkflowExec.getStatus());
+            
+            // 允许 PAUSED 或 WAITING 状态继续执行
+            if (!Integer.valueOf(ExecutionStatusEnum.PAUSED.getValue()).equals(companyAiWorkflowExec.getStatus())
+                    && !Integer.valueOf(ExecutionStatusEnum.WAITING.getValue()).equals(companyAiWorkflowExec.getStatus())) {
+                log.error("工作流状态不符合 - workflowInstanceId: {}, 当前状态: {}, 期望状态: PAUSED({}) 或 WAITING({})",
+                        context.getWorkflowInstanceId(), companyAiWorkflowExec.getStatus(),
+                        ExecutionStatusEnum.PAUSED.getValue(), ExecutionStatusEnum.WAITING.getValue());
+                return handleExecutionError(new CustomException("状态不符合,当前状态: " + companyAiWorkflowExec.getStatus()), context);
             }
             return doContinue(context);
         } catch (Exception e) {
@@ -120,7 +128,7 @@ public abstract class AbstractWorkflowNode implements IWorkflowNode {
         context.setVariable("node_end_time_" + nodeKey, endTime);
         log.info("Completed execution of node: {} ({})", nodeKey, nodeName);
         //todo 写入执行日志等后置操作
-        Integer logStatus = 0;
+        int logStatus;
         if (ExecutionStatusEnum.SUCCESS.equals(result.getStatus()) ) {
             logStatus = ExecutionStatusEnum.SUCCESS.getValue();
             updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.SUCCESS);

+ 29 - 16
fs-service/src/main/java/com/fs/company/service/impl/call/node/AiAddWxTaskNode.java

@@ -44,6 +44,7 @@ public class AiAddWxTaskNode extends AbstractWorkflowNode {
 
     /**
      * 收到加微回调后,继续判定和执行下一步动作
+     * 加微成功后不直接流转,而是改为等待状态,等待下一次回调
      *
      * @param context 执行上下文
      * @return 执行结果
@@ -58,24 +59,36 @@ public class AiAddWxTaskNode extends AbstractWorkflowNode {
         // 获取加微记录
         CompanyWxClient wxClient = companyWxClientMapper.selectById(business.getWxClientId());
 
-        // 根据加微结果判断走哪条边
-        for (CompanyWorkflowEdge edge : edges) {
-            if (edge.getConditionExpr() == null || edge.getConditionExpr().isEmpty()) {
-                // 无条件边,直接执行
-                this.runNextNode(context, edge);
-                return null;
-            }
-            AiAddWxWorkflowConditionVo condition = JSONObject.parseObject(edge.getConditionExpr(), AiAddWxWorkflowConditionVo.class);
-            // 判断加微是否成功 (isAdd: 0否 1是 2待添加 3作废)
-            boolean addSuccess = wxClient != null && Integer.valueOf(1).equals(wxClient.getIsAdd());
-            if (condition.isAddSuccess() == addSuccess) {
-                this.runNextNode(context, edge);
-                return null;
+        log.info("收到加微回调 - workflowInstanceId: {}, wxClientId: {}, isAdd: {}",
+                context.getWorkflowInstanceId(), business.getWxClientId(), wxClient != null ? wxClient.getIsAdd() : null);
+
+        // 判断加微是否成功 (isAdd: 0否 1是 2待添加 3作废)
+        boolean addSuccess = wxClient != null && Integer.valueOf(1).equals(wxClient.getIsAdd());
+        
+        if (addSuccess) {
+            // 加微成功,设置为等待状态,等待下一次回调
+            log.info("加微成功,设置工作流为等待状态 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+            super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), 
+                    context, ExecutionStatusEnum.WAITING);
+            return ExecutionResult.waiting().nextNodeKey("").build();
+        } else {
+            // 加微失败,根据条件判断走哪条边
+            for (CompanyWorkflowEdge edge : edges) {
+                if (edge.getConditionExpr() == null || edge.getConditionExpr().isEmpty()) {
+                    continue; // 跳过无条件边
+                }
+                AiAddWxWorkflowConditionVo condition = JSONObject.parseObject(edge.getConditionExpr(), AiAddWxWorkflowConditionVo.class);
+                // 匹配失败条件
+                if (!condition.isAddSuccess()) {
+                    log.info("加微失败,执行失败分支 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+                    this.runNextNode(context, edge);
+                    return null;
+                }
             }
+            
+            log.error("加微失败但未找到失败分支 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+            return null;
         }
-
-        log.error("未找到满足条件的下一节点 - workflowInstanceId: {}", context.getWorkflowInstanceId());
-        return null;
     }
 
     /**

+ 89 - 4
fs-service/src/main/java/com/fs/company/service/impl/call/node/AiSendMsgTaskNode.java

@@ -1,18 +1,26 @@
 package com.fs.company.service.impl.call.node;
 
 import com.fs.common.utils.spring.SpringUtils;
+import com.fs.company.domain.CompanyAiWorkflowExec;
+import com.fs.company.domain.CompanyVoiceRoboticBusiness;
+import com.fs.company.domain.CompanyWorkflowEdge;
+import com.fs.company.domain.CompanyWorkflowNode;
 import com.fs.company.mapper.CompanyWorkflowNodeMapper;
 import com.fs.company.param.ExecutionContext;
+import com.fs.company.service.IWorkflowNode;
 import com.fs.company.vo.ExecutionResult;
 import com.fs.enums.NodeTypeEnum;
+import lombok.extern.slf4j.Slf4j;
 
+import java.util.List;
 import java.util.Map;
 
 /**
  * @author MixLiu
  * @date 2026/1/28 13:39
- * @description AI外呼电话任务节点
+ * @description AI发送短信任务节点
  */
+@Slf4j
 public class AiSendMsgTaskNode extends AbstractWorkflowNode {
     private static final CompanyWorkflowNodeMapper companyWorkflowNodeMapper = SpringUtils.getBean(CompanyWorkflowNodeMapper.class);
 
@@ -20,15 +28,71 @@ public class AiSendMsgTaskNode extends AbstractWorkflowNode {
         super(nodeKey, nodeName, properties);
     }
 
+    /**
+     * 收到短信发送回调后的处理(当前不需要)
+     *
+     * @param context 执行上下文
+     * @return 执行结果
+     */
     @Override
     protected ExecutionResult doContinue(ExecutionContext context) {
+        // 短信发送不需要回调,直接执行下一个节点
+        List<CompanyWorkflowEdge> edges = companyWorkflowEdgeMapper.selectListByWorkflowIdAndNodeKey(
+                context.getVariable("workflowId", Long.class), nodeKey);
+        
+        if (edges != null && !edges.isEmpty()) {
+            this.runNextNode(context, edges.get(0));
+        }
         return null;
     }
 
+    /**
+     * 执行发送短信节点逻辑
+     * 直接标记为发送成功,然后流转到下一个节点
+     *
+     * @param context 执行上下文
+     * @return 执行结果
+     */
     @Override
     protected ExecutionResult doExecute(ExecutionContext context) {
-
-        return null;
+        try {
+            // 获取业务数据
+            CompanyVoiceRoboticBusiness business = super.getRoboticBusiness(context.getWorkflowInstanceId());
+            if (business == null) {
+                log.error("未找到业务数据 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+                return ExecutionResult.failure().errorMessage("未找到业务数据").build();
+            }
+            
+            log.info("执行发送短信任务 - workflowInstanceId: {}, roboticId: {}, calleeId: {}",
+                    context.getWorkflowInstanceId(), business.getRoboticId(), business.getCalleeId());
+            
+            // TODO: 这里可以添加实际的发送短信逻辑
+            // 更新业务表的发短信动作标记
+            companyVoiceRoboticBusinessMapper.updateActionCount(3, business.getRoboticId(),
+                    business.getCalleeId(), null);
+            
+            log.info("短信发送成功 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+            
+            // 获取下一个节点并执行
+            CompanyAiWorkflowExec exec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(context.getWorkflowInstanceId());
+            List<CompanyWorkflowEdge> edges = companyWorkflowEdgeMapper.selectListByWorkflowIdAndNodeKey(
+                    exec.getWorkflowId(), nodeKey);
+            
+            if (edges != null && !edges.isEmpty()) {
+                // 直接执行下一个节点
+                this.runNextNode(context, edges.get(0));
+                return ExecutionResult.success().nextNodeKey("").build();
+            } else {
+                log.warn("没有找到下一个节点 - workflowInstanceId: {}", context.getWorkflowInstanceId());
+                return ExecutionResult.success().nextNodeKey("").build();
+            }
+            
+        } catch (Exception e) {
+            log.error("发送短信异常 - workflowInstanceId: {}", context.getWorkflowInstanceId(), e);
+            return ExecutionResult.failure()
+                    .errorMessage("发送短信异常: " + e.getMessage())
+                    .build();
+        }
     }
 
     @Override
@@ -38,6 +102,27 @@ public class AiSendMsgTaskNode extends AbstractWorkflowNode {
 
     @Override
     public Boolean isAsync() {
-        return true;
+        return false; // 短信发送为同步节点,直接执行并流转
+    }
+
+    /**
+     * 运行下一个节点
+     *
+     * @param context 执行上下文
+     * @param edge    边
+     */
+    public void runNextNode(ExecutionContext context, CompanyWorkflowEdge edge) {
+        ExecutionContext nextContext = context.clone();
+        nextContext.setCurrentNodeKey(edge.getTargetNodeKey());
+        super.execPointNextNode(nextContext);
+        
+        CompanyWorkflowNode nextNode = companyWorkflowNodeMapper.selectNodeByNodeKey(edge.getTargetNodeKey());
+        IWorkflowNode node = super.workflowNodeFactory.createNode(
+                nextNode.getNodeKey(),
+                NodeTypeEnum.fromCode(nextNode.getNodeType()),
+                nextNode.getNodeName(),
+                null
+        );
+        node.execute(nextContext);
     }
 }

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

@@ -36,5 +36,9 @@ public class ExecutionResult {
         ExecutionResult result = new ExecutionResult();
         return ExecutionResult.builder().success(true).status(ExecutionStatusEnum.PAUSED);
     }
+    public static ExecutionResultBuilder waiting(){
+        ExecutionResult result = new ExecutionResult();
+        return ExecutionResult.builder().success(true).status(ExecutionStatusEnum.WAITING);
+    }
 
 }