Quellcode durchsuchen

今正cid加微后工作流需求变更优化

lk vor 14 Stunden
Ursprung
Commit
e538c74b39
18 geänderte Dateien mit 363 neuen und 75 gelöschten Zeilen
  1. 44 6
      fs-company/src/main/java/com/fs/company/controller/aiAddwxSop/AiAddwxSopController.java
  2. 4 0
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  3. 6 0
      fs-service/src/main/java/com/fs/company/domain/AiAddwxSopTemplateNode.java
  4. 2 0
      fs-service/src/main/java/com/fs/company/mapper/AiAddwxSopTagTemplateBindMapper.java
  5. 2 0
      fs-service/src/main/java/com/fs/company/mapper/AiAddwxSopTemplateNodeMapper.java
  6. 2 0
      fs-service/src/main/java/com/fs/company/mapper/AiAddwxSopUserWorkflowMapper.java
  7. 38 0
      fs-service/src/main/java/com/fs/company/param/AiAddwxSopTagTemplateBindParam.java
  8. 2 0
      fs-service/src/main/java/com/fs/company/service/IAiAddwxSopTagTemplateBindService.java
  9. 5 0
      fs-service/src/main/java/com/fs/company/service/IAiAddwxSopTemplateNodeService.java
  10. 4 0
      fs-service/src/main/java/com/fs/company/service/IAiAddwxSopUserNodeService.java
  11. 6 0
      fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTagTemplateBindServiceImpl.java
  12. 29 0
      fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTemplateNodeServiceImpl.java
  13. 24 13
      fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTemplateServiceImpl.java
  14. 125 53
      fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopUserNodeServiceImpl.java
  15. 37 1
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  16. 8 0
      fs-service/src/main/resources/mapper/aiAddwxSop/AiAddwxSopTagTemplateBindMapper.xml
  17. 18 2
      fs-service/src/main/resources/mapper/company/AiAddwxSopTemplateNodeMapper.xml
  18. 7 0
      fs-service/src/main/resources/mapper/company/AiAddwxSopUserWorkflowMapper.xml

+ 44 - 6
fs-company/src/main/java/com/fs/company/controller/aiAddwxSop/AiAddwxSopController.java

@@ -8,6 +8,7 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.*;
+import com.fs.company.param.AiAddwxSopTagTemplateBindParam;
 import com.fs.company.param.InitAddWxSopTestParam;
 import com.fs.company.service.*;
 import com.fs.framework.security.LoginUser;
@@ -181,6 +182,16 @@ public class AiAddwxSopController extends BaseController {
         return AjaxResult.success();
     }
 
+    /**
+     * 批量保存画布节点(含位置坐标)
+     */
+    @Log(title = "AI添加微信SOP画布节点", businessType = BusinessType.UPDATE)
+    @PostMapping("/templateNode/saveCanvasNodes")
+    public AjaxResult saveCanvasNodes(@RequestBody List<AiAddwxSopTemplateNode> nodes) {
+        templateNodeService.saveCanvasNodes(nodes);
+        return AjaxResult.success();
+    }
+
     // ==================== 模板节点规则管理 ====================
 
     /**
@@ -473,14 +484,41 @@ public class AiAddwxSopController extends BaseController {
      */
     @Log(title = "标签模板绑定", businessType = BusinessType.INSERT)
     @PostMapping("/tagTemplateBind")
-    public AjaxResult addTagTemplateBind(@RequestBody AiAddwxSopTagTemplateBind bind) {
-        // 检查该标签是否已绑定
+    public AjaxResult addTagTemplateBind(@RequestBody AiAddwxSopTagTemplateBindParam param) {
+        List<Long> tagIds = param.getQwTagIds();
+        if (tagIds != null && !tagIds.isEmpty()) {
+            // 多标签批量绑定
+            List<AiAddwxSopTagTemplateBind> list = new java.util.ArrayList<>();
+            for (Long tagId : tagIds) {
+                AiAddwxSopTagTemplateBind exist = new AiAddwxSopTagTemplateBind();
+                exist.setQwTagId(tagId);
+                exist.setTemplateId(param.getTemplateId());
+                AiAddwxSopTagTemplateBind check = tagTemplateBindService.selectTagBindBytagIdAndTemplateId(exist);
+                if (check != null) {
+                    continue; // 已存在则跳过
+                }
+                AiAddwxSopTagTemplateBind item = new AiAddwxSopTagTemplateBind();
+                item.setQwTagId(tagId);
+                item.setTemplateId(param.getTemplateId());
+                item.setTemplateName(param.getTemplateName());
+                item.setPriority(param.getPriority());
+                list.add(item);
+            }
+            if (list.isEmpty()) {
+                return AjaxResult.error("所选标签均已存在绑定关系");
+            }
+            tagTemplateBindService.batchInsertAiAddwxSopTagTemplateBind(list);
+            return AjaxResult.success(list.size());
+        }
+        // 单标签绑定(兼容原有逻辑)
+        AiAddwxSopTagTemplateBind bind = new AiAddwxSopTagTemplateBind();
+        bind.setQwTagId(param.getQwTagId());
+        bind.setQwTagName(param.getQwTagName());
+        bind.setTemplateId(param.getTemplateId());
+        bind.setTemplateName(param.getTemplateName());
+        bind.setPriority(param.getPriority());
         AiAddwxSopTagTemplateBind exist = tagTemplateBindService.selectTagBindBytagIdAndTemplateId(bind);
-//        AiAddwxSopTagTemplateBind exist = tagTemplateBindService.selectAiAddwxSopTagTemplateBindByTagId(bind.getQwTagId());
         if (exist != null) {
-//            // 已存在则更新
-//            bind.setBindId(exist.getBindId());
-//            tagTemplateBindService.updateAiAddwxSopTagTemplateBind(bind);
             return AjaxResult.error("已存在该标签与该模板绑定关系");
         }
         tagTemplateBindService.insertAiAddwxSopTagTemplateBind(bind);

+ 4 - 0
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -587,6 +587,10 @@ public class SendMsg {
         List<AiAddwxSopUserWorkflow> aiAddwxSopUserWorkflows = aiAddwxSopUserWorkflowMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserWorkflow>()
                 .eq(AiAddwxSopUserWorkflow::getDelFlag, 0).ne(AiAddwxSopUserWorkflow::getCurrentNodeType, AiAddWechatSopNodeEnum.NOT_DEAL.getCode())
                 .ne(AiAddwxSopUserWorkflow::getCurrentNodeType, AiAddWechatSopNodeEnum.DEAL.getCode()));
+//        if(aiAddwxSopUserWorkflows.isEmpty()){
+//            log.info("没有需要处理的cid加微后sop任务");
+//            return;
+//        }
         Map<String, List<AiAddwxSopUserWorkflow>> workflowsMap = aiAddwxSopUserWorkflows
                 .stream()
                 .collect(Collectors.groupingBy(AiAddwxSopUserWorkflow::getCurrentNodeType));

+ 6 - 0
fs-service/src/main/java/com/fs/company/domain/AiAddwxSopTemplateNode.java

@@ -55,6 +55,12 @@ public class AiAddwxSopTemplateNode implements Serializable {
     /** 排序 */
     private Integer sortOrder;
 
+    /** 画布X坐标 */
+    private Integer positionX;
+
+    /** 画布Y坐标 */
+    private Integer positionY;
+
     /** 创建时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createTime;

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

@@ -31,4 +31,6 @@ public interface AiAddwxSopTagTemplateBindMapper extends BaseMapper<AiAddwxSopTa
     Long selectOneAiAddwxSopTagTemplateBindByTagIds(@Param("tagIds") List<Long> tagIds);
 
     AiAddwxSopTagTemplateBind selectTagBindBytagIdAndTemplateId(AiAddwxSopTagTemplateBind bind);
+
+    int batchInsertAiAddwxSopTagTemplateBind(@Param("list") List<AiAddwxSopTagTemplateBind> list);
 }

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

@@ -29,4 +29,6 @@ public interface AiAddwxSopTemplateNodeMapper extends BaseMapper<AiAddwxSopTempl
     int deleteAiAddwxSopTemplateNodeByTemplateId(Long templateId);
 
     int batchInsertAiAddwxSopTemplateNode(@Param("list") List<AiAddwxSopTemplateNode> list);
+
+    int updatePosition(AiAddwxSopTemplateNode node);
 }

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

@@ -25,4 +25,6 @@ public interface AiAddwxSopUserWorkflowMapper extends BaseMapper<AiAddwxSopUserW
     int deleteAiAddwxSopUserWorkflowById(Long userWorkflowId);
 
     int deleteAiAddwxSopUserWorkflowByIds(@Param("userWorkflowIds") Long[] userWorkflowIds);
+
+    AiAddwxSopUserWorkflow selectOnUseWorkflowByExtId(Long id);
 }

+ 38 - 0
fs-service/src/main/java/com/fs/company/param/AiAddwxSopTagTemplateBindParam.java

@@ -0,0 +1,38 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 企微标签与工作流模板绑定请求参数
+ *
+ * @author fs
+ * @date 2026-07-21
+ */
+@Data
+public class AiAddwxSopTagTemplateBindParam implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 绑定ID(编辑时使用) */
+    private Long bindId;
+
+    /** 企微标签ID(单标签场景) */
+    private Long qwTagId;
+
+    /** 企微标签ID列表(多标签场景) */
+    private List<Long> qwTagIds;
+
+    /** 企微标签名称 */
+    private String qwTagName;
+
+    /** 模板ID */
+    private Long templateId;
+
+    /** 模板名称 */
+    private String templateName;
+
+    /** 优先级 */
+    private Integer priority;
+}

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

@@ -27,4 +27,6 @@ public interface IAiAddwxSopTagTemplateBindService {
     int deleteAiAddwxSopTagTemplateBindByTagId(String qwTagId);
 
     AiAddwxSopTagTemplateBind selectTagBindBytagIdAndTemplateId(AiAddwxSopTagTemplateBind bind);
+
+    int batchInsertAiAddwxSopTagTemplateBind(List<AiAddwxSopTagTemplateBind> list);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/company/service/IAiAddwxSopTemplateNodeService.java

@@ -27,4 +27,9 @@ public interface IAiAddwxSopTemplateNodeService {
     int deleteAiAddwxSopTemplateNodeByTemplateId(Long templateId);
 
     int batchInsertAiAddwxSopTemplateNode(List<AiAddwxSopTemplateNode> list);
+
+    /**
+     * 批量保存画布节点(含增删改及位置坐标)
+     */
+    void saveCanvasNodes(List<AiAddwxSopTemplateNode> nodes);
 }

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

@@ -1,5 +1,6 @@
 package com.fs.company.service;
 
+import com.fs.common.core.domain.R;
 import com.fs.company.domain.AiAddwxSopUserNode;
 import com.fs.company.domain.AiAddwxSopUserWorkflow;
 
@@ -34,4 +35,7 @@ public interface IAiAddwxSopUserNodeService {
     int updateNodeStatus(Long userNodeId, Integer status);
 
     void nextNode(Map.Entry<String, List<AiAddwxSopUserWorkflow>> next);
+
+    /** 根据AI返回结果进行节点流转处理 */
+    void processNodeTransitionFromAi(R r, AiAddwxSopUserWorkflow workflow);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTagTemplateBindServiceImpl.java

@@ -37,6 +37,7 @@ public class AiAddwxSopTagTemplateBindServiceImpl implements IAiAddwxSopTagTempl
 
     @Override
     public int insertAiAddwxSopTagTemplateBind(AiAddwxSopTagTemplateBind bind) {
+        //todo 将已有对应标签的用户加工作流
         return tagTemplateBindMapper.insertAiAddwxSopTagTemplateBind(bind);
     }
 
@@ -59,4 +60,9 @@ public class AiAddwxSopTagTemplateBindServiceImpl implements IAiAddwxSopTagTempl
     public AiAddwxSopTagTemplateBind selectTagBindBytagIdAndTemplateId(AiAddwxSopTagTemplateBind bind) {
         return tagTemplateBindMapper.selectTagBindBytagIdAndTemplateId(bind);
     }
+
+    @Override
+    public int batchInsertAiAddwxSopTagTemplateBind(List<AiAddwxSopTagTemplateBind> list) {
+        return tagTemplateBindMapper.batchInsertAiAddwxSopTagTemplateBind(list);
+    }
 }

+ 29 - 0
fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTemplateNodeServiceImpl.java

@@ -59,4 +59,33 @@ public class AiAddwxSopTemplateNodeServiceImpl implements IAiAddwxSopTemplateNod
     public int batchInsertAiAddwxSopTemplateNode(List<AiAddwxSopTemplateNode> list) {
         return templateNodeMapper.batchInsertAiAddwxSopTemplateNode(list);
     }
+
+    @Override
+    public void saveCanvasNodes(List<AiAddwxSopTemplateNode> nodes) {
+        if (nodes == null || nodes.isEmpty()) {
+            return;
+        }
+        Long templateId = nodes.get(0).getTemplateId();
+        // 获取该模板下已有节点ID
+        List<AiAddwxSopTemplateNode> existingNodes = templateNodeMapper.selectAiAddwxSopTemplateNodeByTemplateId(templateId);
+        List<Long> existingIds = existingNodes.stream().map(AiAddwxSopTemplateNode::getNodeId).collect(java.util.stream.Collectors.toList());
+        // 收集传入的已有节点ID
+        List<Long> incomingIds = nodes.stream().map(AiAddwxSopTemplateNode::getNodeId).filter(id -> id != null).collect(java.util.stream.Collectors.toList());
+        // 删除不在传入列表中的节点
+        for (Long existId : existingIds) {
+            if (!incomingIds.contains(existId)) {
+                templateNodeMapper.deleteAiAddwxSopTemplateNodeById(existId);
+            }
+        }
+        // 更新已有节点位置,插入新节点
+        for (AiAddwxSopTemplateNode node : nodes) {
+            if (node.getNodeId() != null && existingIds.contains(node.getNodeId())) {
+                // 已有节点:仅更新位置坐标
+                templateNodeMapper.updatePosition(node);
+            } else {
+                // 新节点:插入
+                templateNodeMapper.insertAiAddwxSopTemplateNode(node);
+            }
+        }
+    }
 }

+ 24 - 13
fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopTemplateServiceImpl.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.spring.SpringUtils;
@@ -95,6 +96,8 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
     @Autowired
     private FastGptChatMsgMapper fastGptChatMsgMapper;
 
+    private static final ObjectMapper mapper = new ObjectMapper();
+
     @Value("${cid.addwx.sop.appKey:fastgpt-hPSZNV9pPlX5GlHyjzsGqEPbB2D8tb9mYQcnD1y5lEUIEMgGaO0PFm7BHHjF}")
     private String CID_ADDWX_SOP_APPKEY;
     @Autowired
@@ -158,7 +161,7 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
     }
 
     @Override
-//    @Async
+    @Async
     public void initAddWxSop(List<QwExternalContact> externalUsers, List<String> tagIds) {
         //qw_tag_id获取优先级最高的那条template_id
         if (tagIds == null || tagIds.isEmpty()) return;
@@ -195,6 +198,7 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
         for (QwExternalContact externalUser : externalUsers) {
             QwUser qwUser = qwUserMap.get(externalUser.getQwUserId());
             Long qwUserId = qwUser != null ? qwUser.getId() : null;
+            String sendWords ="";
 
             // 创建个人工作流
             AiAddwxSopUserWorkflow userWorkflow = new AiAddwxSopUserWorkflow();
@@ -289,7 +293,12 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
 
                                 // 转为 Date 类型(如果 userNodeRule.setRealSendTime 需要 Date)
                                 userNodeRule.setRealSendTime(Date.from(targetTime.atZone(ZoneId.systemDefault()).toInstant()));
-
+                                try {
+                                    JsonNode root =mapper.readTree(templateRule.getContentJson());
+                                    sendWords = root.path("prompt").asText();
+                                } catch (Exception e) {
+                                    e.printStackTrace();
+                                }
                             }
                         }
                         allUserNodeRules.add(userNodeRule);
@@ -300,19 +309,21 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
             if (!allUserNodeRules.isEmpty()) {
                 userNodeRuleMapper.batchInsertAiAddwxSopUserNodeRule(allUserNodeRules);
             }
-            Map<String, Object> requestParam = new HashMap<>();
 
-            initAiChatParam(requestParam, userWorkflow,null,AiAddWechatSopNodeEnum.ADD_WECHAT.getDesc(),externalUser);
+//            Map<String, Object> requestParam = new HashMap<>();
+
+//            initAiChatParam(requestParam, userWorkflow,null,AiAddWechatSopNodeEnum.ADD_WECHAT.getDesc(),externalUser);
 
-            R r = callAiService(requestParam, ThreadLocalRandom.current().nextLong(100_000_000_000L, 999_999_999_999L + 1)
-                    , CID_ADDWX_SOP_APPKEY);
+//            R r = callAiService(requestParam, ThreadLocalRandom.current().nextLong(100_000_000_000L, 999_999_999_999L + 1)
+//                    , CID_ADDWX_SOP_APPKEY);
 
 //            System.out.println(r);
-            JSONObject content = extractContentJson(r);
+//            JSONObject content = extractContentJson(r);
 //            System.out.println(content);
-            if (content != null) {
-                if (content.containsKey("aiContent")){
-                    String sendWords = (String) content.get("aiContent");
+//            if (content != null) {
+//                if (content.containsKey("aiContent")){
+//                    String sendWords = (String) content.get("aiContent");
+
                     // 新增fastgptchatsession新生成必须发第一条
                     qwHookNotifyAddMsgNew(qwUser,externalUser,sendWords);
                     AiAddwxSopUserNodeRule aiAddwxSopUserNodeRule = userNodeRuleMapper.selectOne(new LambdaQueryWrapper<AiAddwxSopUserNodeRule>()
@@ -323,8 +334,8 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
                     firstRUle.setUserNodeRuleId(aiAddwxSopUserNodeRule.getUserNodeRuleId());
                     firstRUle.setContentJson(sendWords);
                     userNodeRuleMapper.updateById(firstRUle);
-                }
-            }
+//                }
+//            }
 
             //
         }
@@ -364,7 +375,7 @@ public class AiAddwxSopTemplateServiceImpl implements IAiAddwxSopTemplateService
         }
     }
 
-    private static final ObjectMapper mapper = new ObjectMapper();
+
     public static R callAiService(Map<String, Object> requestParam, Long logId,String appKey) {
         try {
             String requestJson = mapper.writeValueAsString(requestParam);

+ 125 - 53
fs-service/src/main/java/com/fs/company/service/impl/AiAddwxSopUserNodeServiceImpl.java

@@ -4,6 +4,8 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.AiAddwxSopUserNode;
 import com.fs.company.domain.AiAddwxSopUserNodeRule;
@@ -31,6 +33,7 @@ import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
 import java.time.ZoneId;
+import java.util.Date;
 import java.util.*;
 
 /**
@@ -59,6 +62,12 @@ public class AiAddwxSopUserNodeServiceImpl implements IAiAddwxSopUserNodeService
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
 
+    @Value("${ai.add.wx.sop.key:fastgpt-hPSZNV9pPlX5GlHyjzsGqEPbB2D8tb9mYQcnD1y5lEUIEMgGaO0PFm7BHHjF}")
+    private String AI_ADD_WC_SOP_KEY;
+
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+
 
     @Override
     public AiAddwxSopUserNode selectAiAddwxSopUserNodeById(Long userNodeId) {
@@ -108,8 +117,6 @@ public class AiAddwxSopUserNodeServiceImpl implements IAiAddwxSopUserNodeService
         return userNodeMapper.updateAiAddwxSopUserNode(node);
     }
 
-    @Value("${ai.add.wx.sop.key:fastgpt-hPSZNV9pPlX5GlHyjzsGqEPbB2D8tb9mYQcnD1y5lEUIEMgGaO0PFm7BHHjF}")
-    private String AI_ADD_WC_SOP_KEY;
     @Override
     public void nextNode(Map.Entry<String, List<AiAddwxSopUserWorkflow>> next) {
         AiAddWechatSopNodeEnum nodeTypeE = AiAddWechatSopNodeEnum.fromCode(next.getKey());
@@ -153,7 +160,13 @@ public class AiAddwxSopUserNodeServiceImpl implements IAiAddwxSopUserNodeService
                             targetTime = targetTime.plusDays(1).withHour(9).withMinute(0);
                         }
                         firstRule.setRealSendTime(Date.from(targetTime.atZone(ZoneId.systemDefault()).toInstant()));
-
+                        try {
+                            JsonNode root =mapper.readTree(firstRule.getContentJson());
+                            firstRule.setContentJson(root.path("prompt").asText());
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            log.info("解析发送话术失败,用户节点id:{}",firstRule.getUserNodeRuleId());
+                        }
                         aiAddwxSopUserNodeRuleMapper.updateById(firstRule);
                         continue;
                     }
@@ -162,62 +175,121 @@ public class AiAddwxSopUserNodeServiceImpl implements IAiAddwxSopUserNodeService
             QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(workflow.getQwExternalId());
             initAiChatParam(requestParam, workflow,session,nodeTypeE.getDesc(),qwExternalContact);
 
-            R r = aiAddwxSopTemplateServiceImpl.callAiService(requestParam, session.getSessionId(), AI_ADD_WC_SOP_KEY);
-            JSONObject content = extractContentJson(r);
+//            R r = aiAddwxSopTemplateServiceImpl.callAiService(requestParam, session.getSessionId(), AI_ADD_WC_SOP_KEY);
+//            JSONObject content = extractContentJson(r);
 
             //根据ai生成的结果进行节点转换
-            if (content!= null){
-                JSONObject userInfo = JSON.parseObject(content.getString("userInfo"));
-                if (userInfo != null){
-                    String currentGeneration = null;
-                    Boolean suc = false;
-                    try {
-                        currentGeneration = userInfo.getString("当前阶段");
-                        suc =true;
-                    }catch (Exception e){
-                        log.error("获取当前阶段失败,无需流转", e);
-                    }
-                    if (suc){
-                        AiAddWechatSopNodeEnum nodeEnum = AiAddWechatSopNodeEnum.fromCode(workflow.getCurrentNodeType());
-                        if (nodeEnum == null){
-                            log.error("当前阶段异常,请检查AI返回的当前阶段");
-                            return;
-                        }
-                        if (!currentGeneration.equals(nodeEnum.getDesc() )){
-                            AiAddwxSopUserWorkflow newWorkflow = new AiAddwxSopUserWorkflow();
-                            AiAddwxSopUserNode currentNode = userNodeMapper.selectById(workflow.getCurrentNodeId());
-                            List<AiAddwxSopUserNode> aiAddwxSopUserNodes = userNodeMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNode>()
-                                    .eq(AiAddwxSopUserNode::getPrevNodeId, currentNode.getTemplateNodeId())
-                                    .eq(AiAddwxSopUserNode::getUserWorkflowId,currentNode.getUserWorkflowId()));
-                            AiAddwxSopUserNode nextNode = aiAddwxSopUserNodes.stream().filter(node -> node.getNodeType().equals(nodeEnum.getCode())).findFirst().orElse(null);
-                            if (nextNode != null){
-                                newWorkflow.setUserWorkflowId(workflow.getUserWorkflowId())
-                                        .setCurrentNodeId(nextNode.getUserNodeId())
-                                        .setCurrentNodeKey(nextNode.getNodeKey())
-                                        .setCurrentNodeType(nextNode.getNodeType()); //新节点
-                                aiAddwxSopUserWorkflowMapper.updateById(newWorkflow);
-                            }
-                        }
-                    }else {
-                        //未变更就继续对话
-                        String aiContent = content.getString("aiContent");
-                        List<AiAddwxSopUserNodeRule> aiAddwxSopUserNodeRules = aiAddwxSopUserNodeRuleMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNodeRule>()
-                                .eq(AiAddwxSopUserNodeRule::getUserNodeId, workflow.getCurrentNodeId()).eq(AiAddwxSopUserNodeRule::getIsSent, 1).orderByAsc(AiAddwxSopUserNodeRule::getUpdateTime));
-                        if (!aiAddwxSopUserNodeRules.isEmpty()){
-                            AiAddwxSopUserNodeRule finalRule = aiAddwxSopUserNodeRules.get(aiAddwxSopUserNodeRules.size() - 1);
-                            Date date = new Date();
-                            Date datePlus5Seconds = new Date(date.getTime() + 5000);
-                            finalRule.setIsSent(0).setUserNodeRuleId(null).setRealSendTime(datePlus5Seconds).setContentJson(aiContent);
-                            aiAddwxSopUserNodeRuleMapper.updateById(finalRule);
-                        }
-                    }
+//            if (content!= null){
+//                JSONObject userInfo = JSON.parseObject(content.getString("userInfo"));
+//                if (userInfo != null){
+//                    String currentGeneration = null;
+//                    Boolean suc = false;
+//                    try {
+//                        currentGeneration = userInfo.getString("当前阶段");
+//                        suc =true;
+//                    }catch (Exception e){
+//                        log.error("获取当前阶段失败,无需流转", e);
+//                    }
+//                    if (suc){
+//                        AiAddWechatSopNodeEnum nodeEnum = AiAddWechatSopNodeEnum.fromCode(workflow.getCurrentNodeType());
+//                        if (nodeEnum == null){
+//                            log.error("当前阶段异常,请检查AI返回的当前阶段");
+//                            return;
+//                        }
+//                        if (!currentGeneration.equals(nodeEnum.getDesc() )){
+//                            AiAddwxSopUserWorkflow newWorkflow = new AiAddwxSopUserWorkflow();
+//                            AiAddwxSopUserNode currentNode = userNodeMapper.selectById(workflow.getCurrentNodeId());
+//                            List<AiAddwxSopUserNode> aiAddwxSopUserNodes = userNodeMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNode>()
+//                                    .eq(AiAddwxSopUserNode::getPrevNodeId, currentNode.getTemplateNodeId())
+//                                    .eq(AiAddwxSopUserNode::getUserWorkflowId,currentNode.getUserWorkflowId()));
+//                            AiAddwxSopUserNode nextNode = aiAddwxSopUserNodes.stream().filter(node -> node.getNodeType().equals(nodeEnum.getCode())).findFirst().orElse(null);
+//                            if (nextNode != null){
+//                                newWorkflow.setUserWorkflowId(workflow.getUserWorkflowId())
+//                                        .setCurrentNodeId(nextNode.getUserNodeId())
+//                                        .setCurrentNodeKey(nextNode.getNodeKey())
+//                                        .setCurrentNodeType(nextNode.getNodeType()); //新节点
+//                                aiAddwxSopUserWorkflowMapper.updateById(newWorkflow);
+//                            }
+//                        }
+//                    }else {
+//                        //未变更就继续对话
+//                        String aiContent = content.getString("aiContent");
+//                        List<AiAddwxSopUserNodeRule> aiAddwxSopUserNodeRules = aiAddwxSopUserNodeRuleMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNodeRule>()
+//                                .eq(AiAddwxSopUserNodeRule::getUserNodeId, workflow.getCurrentNodeId()).eq(AiAddwxSopUserNodeRule::getIsSent, 1).orderByAsc(AiAddwxSopUserNodeRule::getUpdateTime));
+//                        if (!aiAddwxSopUserNodeRules.isEmpty()){
+//                            AiAddwxSopUserNodeRule finalRule = aiAddwxSopUserNodeRules.get(aiAddwxSopUserNodeRules.size() - 1);
+//                            Date date = new Date();
+//                            Date datePlus5Seconds = new Date(date.getTime() + 5000);
+//                            finalRule.setIsSent(0).setUserNodeRuleId(null).setRealSendTime(datePlus5Seconds).setContentJson(aiContent);
+//                            aiAddwxSopUserNodeRuleMapper.updateById(finalRule);
+//                        }
+//                    }
+//
+//                }
+//            }
 
+        }
+
+    }
+
+    @Override
+    public void processNodeTransitionFromAi(R r, AiAddwxSopUserWorkflow workflow) {
+        JSONObject content = extractContentJson(r);
+        if (content == null) {
+            return;
+        }
+        JSONObject userInfo = JSON.parseObject(content.getString("userInfo"));
+        if (userInfo == null) {
+            return;
+        }
+        String currentGeneration = null;
+        boolean suc = false;
+        try {
+            currentGeneration = userInfo.getString("当前阶段");
+            suc = true;
+        } catch (Exception e) {
+            log.error("获取当前阶段失败,无需流转", e);
+        }
+        if (suc) {
+            AiAddWechatSopNodeEnum nodeEnum = AiAddWechatSopNodeEnum.fromCode(workflow.getCurrentNodeType());
+            if (nodeEnum == null) {
+                log.error("当前阶段异常,请检查AI返回的当前阶段");
+                return;
+            }
+            if (!currentGeneration.equals(nodeEnum.getDesc())) {
+                AiAddwxSopUserWorkflow newWorkflow = new AiAddwxSopUserWorkflow();
+                AiAddwxSopUserNode currentNode = userNodeMapper.selectById(workflow.getCurrentNodeId());
+                List<AiAddwxSopUserNode> aiAddwxSopUserNodes = userNodeMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNode>()
+                        .eq(AiAddwxSopUserNode::getPrevNodeId, currentNode.getTemplateNodeId())
+                        .eq(AiAddwxSopUserNode::getUserWorkflowId, currentNode.getUserWorkflowId()));
+                AiAddwxSopUserNode nextNode = aiAddwxSopUserNodes.stream()
+                        .filter(node -> node.getNodeType().equals(nodeEnum.getCode()))
+                        .findFirst().orElse(null);
+                if (nextNode != null) {
+                    newWorkflow.setUserWorkflowId(workflow.getUserWorkflowId())
+                            .setCurrentNodeId(nextNode.getUserNodeId())
+                            .setCurrentNodeKey(nextNode.getNodeKey())
+                            .setCurrentNodeType(nextNode.getNodeType());
+                    aiAddwxSopUserWorkflowMapper.updateById(newWorkflow);
                 }
             }
-
+        } else {
+            // 未变更就继续对话
+            String aiContent = content.getString("aiContent");
+            List<AiAddwxSopUserNodeRule> aiAddwxSopUserNodeRules = aiAddwxSopUserNodeRuleMapper.selectList(new LambdaQueryWrapper<AiAddwxSopUserNodeRule>()
+                    .eq(AiAddwxSopUserNodeRule::getUserNodeId, workflow.getCurrentNodeId())
+                    .eq(AiAddwxSopUserNodeRule::getIsSent, 1)
+                    .orderByAsc(AiAddwxSopUserNodeRule::getUpdateTime));
+            if (!aiAddwxSopUserNodeRules.isEmpty()) {
+                AiAddwxSopUserNodeRule finalRule = aiAddwxSopUserNodeRules.get(aiAddwxSopUserNodeRules.size() - 1);
+                Date date = new Date();
+                Date datePlus5Seconds = new Date(date.getTime() + 5000);
+                finalRule.setIsSent(0).setUserNodeRuleId(null).setRealSendTime(datePlus5Seconds).setContentJson(aiContent);
+                aiAddwxSopUserNodeRuleMapper.updateById(finalRule);
+            }
         }
-
     }
+
     private static JSONObject extractContentJson(R result) {
         try {
             Object data = result.get("data");
@@ -252,7 +324,7 @@ public class AiAddwxSopUserNodeServiceImpl implements IAiAddwxSopUserNodeService
         }
     }
 
-    private void initAiChatParam(Map<String, Object> requestParam, AiAddwxSopUserWorkflow workflow, FastGptChatSession session, String nodeTypeE, QwExternalContact externalUser) {
+    public void initAiChatParam(Map<String, Object> requestParam, AiAddwxSopUserWorkflow workflow, FastGptChatSession session, String nodeTypeE, QwExternalContact externalUser) {
         // 1. 构建 userInfo
         Map<String, Object> userInfo = new HashMap<>();
         userInfo.put("姓名", externalUser.getName()!=null?externalUser.getName():"");

+ 37 - 1
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -8,10 +8,17 @@ import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.CloudHostUtils;
+import com.fs.company.domain.AiAddwxSopUserWorkflow;
 import com.fs.company.domain.CompanyConfig;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.enums.AiAddWechatSopNodeEnum;
+import com.fs.company.mapper.AiAddwxSopUserNodeMapper;
+import com.fs.company.mapper.AiAddwxSopUserNodeRuleMapper;
+import com.fs.company.mapper.AiAddwxSopUserWorkflowMapper;
 import com.fs.company.mapper.CompanyConfigMapper;
 import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.service.impl.AiAddwxSopTemplateServiceImpl;
+import com.fs.company.service.impl.AiAddwxSopUserNodeServiceImpl;
 import com.fs.config.ai.AiHostProper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.domain.FsUserCourseVideo;
@@ -194,6 +201,14 @@ public class AiHookServiceImpl implements AiHookService {
 
     @Value("${cloud_host.company_name}")
     private String signProjectName;
+    @Autowired
+    private AiAddwxSopUserWorkflowMapper aiAddwxSopUserWorkflowMapper;
+    @Autowired
+    private AiAddwxSopUserNodeRuleMapper aiAddwxSopUserNodeRuleMapper;
+    @Autowired
+    private AiAddwxSopUserNodeServiceImpl aiAddwxSopUserNodeServiceImpl;
+    @Autowired
+    private AiAddwxSopTemplateServiceImpl aiAddwxSopTemplateServiceImpl;
 
 
     /** Ai半小时未回复提醒 **/
@@ -1720,6 +1735,8 @@ public class AiHookServiceImpl implements AiHookService {
 
     }
 
+    @Value("${ai.add.wx.sop.key:fastgpt-hPSZNV9pPlX5GlHyjzsGqEPbB2D8tb9mYQcnD1y5lEUIEMgGaO0PFm7BHHjF}")
+    private String AI_ADD_WC_SOP_KEY;
     /** 发送Ai消息,-- -----从saas移动过来,主要是返回结构和内部逻辑跟之前的有区别 (2026年4月17日16点08分) **/
     private R  sendAiMsgNew(Integer i,FastGptChatSession fastGptChatSession, FastGptRole role,QwUser user,Long qwExternalContactsId,String appKey,QwExternalContact qwExternalContacts,Long sender){
         //等待5秒
@@ -1762,7 +1779,23 @@ public class AiHookServiceImpl implements AiHookService {
 
             //添加关键词
             addPromptWordNew(messageList,msgC,qwExternalContactsId,role,fastGptChatSession);
-            R r = chatService.initiatingTakeChat(param, "http://129.28.170.206:3000/api/", appKey);
+            R r = null;
+            if (("今正科技".equals(cloudHostProper.getCompanyName()))) {
+//                Gson gson = new Gson();
+//                List<String> tagIdList = gson.fromJson(qwExternalContacts.getTagIds(), new TypeToken<List<String>>(){}.getType());
+//                List<Long> qwTagIds = qwTagMapper.selectIdsByTagIds(tagIdList);
+                 AiAddwxSopUserWorkflow workflow = aiAddwxSopUserWorkflowMapper.selectOnUseWorkflowByExtId(qwExternalContacts.getId());
+                 if (workflow != null){
+                     AiAddWechatSopNodeEnum nodeEnum = AiAddWechatSopNodeEnum.fromCode(workflow.getCurrentNodeType());
+                     Map<String, Object> requestParam = new HashMap<>();
+                     aiAddwxSopUserNodeServiceImpl.initAiChatParam(requestParam, workflow, fastGptChatSession,nodeEnum.getDesc(),qwExternalContacts );
+                     r = aiAddwxSopTemplateServiceImpl.callAiService(requestParam, fastGptChatSession.getSessionId(), AI_ADD_WC_SOP_KEY);
+//                     r = chatService.initiatingTakeChat(requestParam, "http://129.28.170.206:3000/api/", appKey);
+                     // 根据AI返回结果进行节点流转处理
+                     aiAddwxSopUserNodeServiceImpl.processNodeTransitionFromAi(r, workflow);
+                 }
+            }
+            if (r == null)r = chatService.initiatingTakeChat(param, "http://129.28.170.206:3000/api/", appKey);
             Object data1 = r.get("data");
             if(!(data1 instanceof KnowledgeBaseResult)){
                 ChatDetailTStreamFResult data = (ChatDetailTStreamFResult) r.get("data");
@@ -1836,6 +1869,9 @@ public class AiHookServiceImpl implements AiHookService {
                 for (String name : split) {
                     if (name != null) {
                         //课程章节 看课状态
+                        if(name.equals("看课状态")){
+                            continue;
+                        }
                         if(name.equals("课程章节")){
                             FsCourseWatchLogVO log = fsCourseWatchLogMapper.selectFsCourseWatchLogByExtId(fastGptChatSession.getQwExtId());
                             String courseTitleName = log.getCourseName() + log.getTitle();

+ 8 - 0
fs-service/src/main/resources/mapper/aiAddwxSop/AiAddwxSopTagTemplateBindMapper.xml

@@ -105,4 +105,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         delete from ai_addwx_sop_tag_template_bind where qw_tag_id = #{qwTagId}
     </delete>
 
+    <insert id="batchInsertAiAddwxSopTagTemplateBind" parameterType="java.util.List">
+        insert into ai_addwx_sop_tag_template_bind (qw_tag_id, qw_tag_name, template_id, template_name, priority, create_time)
+        values
+        <foreach collection="list" item="item" separator=",">
+            (#{item.qwTagId}, #{item.qwTagName}, #{item.templateId}, #{item.templateName}, #{item.priority}, now())
+        </foreach>
+    </insert>
+
 </mapper>

+ 18 - 2
fs-service/src/main/resources/mapper/company/AiAddwxSopTemplateNodeMapper.xml

@@ -17,13 +17,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="nodeConfig"   column="node_config"   />
         <result property="isValidReply" column="is_valid_reply" />
         <result property="sortOrder"    column="sort_order"    />
+        <result property="positionX"    column="position_x"    />
+        <result property="positionY"    column="position_y"    />
         <result property="createTime"   column="create_time"   />
         <result property="updateTime"   column="update_time"   />
     </resultMap>
 
     <sql id="selectAiAddwxSopTemplateNodeVo">
         select node_id, template_id, node_key, node_name, node_type, prev_node_id, next_node_id, node_icon, node_color,
-               node_config, is_valid_reply, sort_order, create_time, update_time
+               node_config, is_valid_reply, sort_order, position_x, position_y, create_time, update_time
         from ai_addwx_sop_template_node
     </sql>
 
@@ -63,6 +65,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="nodeConfig != null">node_config,</if>
             <if test="isValidReply != null">is_valid_reply,</if>
             <if test="sortOrder != null">sort_order,</if>
+            <if test="positionX != null">position_x,</if>
+            <if test="positionY != null">position_y,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateTime != null">update_time,</if>
         </trim>
@@ -78,6 +82,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="nodeConfig != null">#{nodeConfig},</if>
             <if test="isValidReply != null">#{isValidReply},</if>
             <if test="sortOrder != null">#{sortOrder},</if>
+            <if test="positionX != null">#{positionX},</if>
+            <if test="positionY != null">#{positionY},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateTime != null">#{updateTime},</if>
         </trim>
@@ -97,6 +103,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="nodeConfig != null">node_config = #{nodeConfig},</if>
             <if test="isValidReply != null">is_valid_reply = #{isValidReply},</if>
             <if test="sortOrder != null">sort_order = #{sortOrder},</if>
+            <if test="positionX != null">position_x = #{positionX},</if>
+            <if test="positionY != null">position_y = #{positionY},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where node_id = #{nodeId}
@@ -113,7 +121,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <insert id="batchInsertAiAddwxSopTemplateNode" parameterType="java.util.List">
         insert into ai_addwx_sop_template_node (
             template_id, node_key, node_name, node_type, prev_node_id, next_node_id, node_icon, node_color,
-            node_config, is_valid_reply, sort_order, create_time, update_time
+            node_config, is_valid_reply, sort_order, position_x, position_y, create_time, update_time
         ) values
         <foreach collection="list" item="item" separator=",">
             (
@@ -128,10 +136,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 #{item.nodeConfig},
                 #{item.isValidReply},
                 #{item.sortOrder},
+                #{item.positionX},
+                #{item.positionY},
                 #{item.createTime},
                 #{item.updateTime}
             )
         </foreach>
     </insert>
 
+    <update id="updatePosition" parameterType="AiAddwxSopTemplateNode">
+        update ai_addwx_sop_template_node
+        set position_x = #{positionX}, position_y = #{positionY}, update_time = now()
+        where node_id = #{nodeId}
+    </update>
+
 </mapper>

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

@@ -54,6 +54,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectAiAddwxSopUserWorkflowVo"/>
         where user_workflow_id = #{userWorkflowId}
     </select>
+    <select id="selectOnUseWorkflowByExtId" resultType="com.fs.company.domain.AiAddwxSopUserWorkflow">
+        <include refid="selectAiAddwxSopUserWorkflowVo"/>
+         where qw_external_id = #{qwExternalId} and status = 1 and del_flag = 0
+         and current_node_type != 10 and current_node_type != 11
+         order by create_time desc
+         limit 1
+    </select>
 
     <insert id="insertAiAddwxSopUserWorkflow" parameterType="AiAddwxSopUserWorkflow" useGeneratedKeys="true" keyProperty="userWorkflowId">
         insert into ai_addwx_sop_user_workflow