boss 2 日 前
コミット
bb69d8f8a3
24 ファイル変更1386 行追加181 行削除
  1. 223 0
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterAuxController.java
  2. 30 14
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterController.java
  3. 118 0
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterEngineController.java
  4. 115 0
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterExecController.java
  5. 24 0
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterSimulateController.java
  6. 95 0
      fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterTestController.java
  7. 49 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterApiRegistry.java
  8. 43 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterComplianceRule.java
  9. 51 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterDeadLetter.java
  10. 49 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterDynamicImpl.java
  11. 52 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterE2eRun.java
  12. 49 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterEventAudit.java
  13. 60 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterInstance.java
  14. 60 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterNodeLog.java
  15. 55 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterOptimization.java
  16. 49 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterPrompt.java
  17. 49 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterSalesCorpus.java
  18. 46 0
      fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterScenario.java
  19. 4 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyWorkflowLobsterMapper.java
  20. 6 6
      fs-service/src/main/java/com/fs/company/param/CompanyWorkflowLobsterCanvasParam.java
  21. 5 4
      fs-service/src/main/java/com/fs/company/service/ICompanyWorkflowLobsterService.java
  22. 128 157
      fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowLobsterServiceImpl.java
  23. 8 0
      fs-service/src/main/resources/application-dev.yml
  24. 18 0
      fs-service/src/main/resources/mapper/company/CompanyWorkflowLobsterMapper.xml

+ 223 - 0
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterAuxController.java

@@ -0,0 +1,223 @@
+package com.fs.company.controller.companyWorkflow;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.utils.ServletUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 龙虾引擎 - 提示词管理 + 销冠语料 + API注册中心 + 死信队列 + 节点审核 + 优化建议
+ * 聚合 Controller(一站式后端 stub,避免前端 404)
+ */
+@RestController
+@RequestMapping("/workflow/lobster")
+public class CompanyWorkflowLobsterAuxController extends BaseController {
+
+    @Autowired
+    private TokenService tokenService;
+
+    // ======== 提示词管理 ========
+
+    @GetMapping("/prompt/list")
+    public AjaxResult listPrompts(@RequestParam(defaultValue = "1") Integer page,
+                                   @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/prompt/{id}")
+    public AjaxResult getPrompt(@PathVariable Long id) {
+        return AjaxResult.success(new HashMap<>());
+    }
+
+    @PostMapping("/prompt")
+    public AjaxResult addPrompt(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("提示词已创建");
+    }
+
+    @PutMapping("/prompt/{id}")
+    public AjaxResult updatePrompt(@PathVariable Long id, @RequestBody Map<String, Object> data) {
+        return AjaxResult.success("提示词已更新");
+    }
+
+    @DeleteMapping("/prompt/{id}")
+    public AjaxResult deletePrompt(@PathVariable Long id) {
+        return AjaxResult.success("提示词已删除");
+    }
+
+    @GetMapping("/prompt/categories")
+    public AjaxResult getPromptCategories() {
+        return AjaxResult.success(Arrays.asList("general", "sale", "service", "marketing"));
+    }
+
+    @PostMapping("/prompt/refresh-cache")
+    public AjaxResult refreshPromptCache() {
+        return AjaxResult.success("缓存已刷新");
+    }
+
+    // ======== 销冠语料 ========
+
+    @GetMapping("/sales-corpus/list")
+    public AjaxResult listSalesCorpus(@RequestParam(defaultValue = "1") Integer page,
+                                       @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/sales-corpus/dialog")
+    public AjaxResult addCorpusDialog(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("语料已添加");
+    }
+
+    @PostMapping("/sales-corpus/batch-import")
+    public AjaxResult batchImportCorpus(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("批量导入成功");
+    }
+
+    @PostMapping("/sales-corpus/analyze")
+    public AjaxResult analyzeCorpus() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        return AjaxResult.success("分析任务已启动");
+    }
+
+    @GetMapping("/sales-corpus/scenarios")
+    public AjaxResult getCorpusScenarios() {
+        return AjaxResult.success(Arrays.asList("初次接触", "产品介绍", "异议处理", "逼单成交"));
+    }
+
+    // ======== API注册中心 ========
+
+    @GetMapping("/api-registry/list")
+    public AjaxResult listApiRegistry() {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/api-registry")
+    public AjaxResult registerApi(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("API已注册");
+    }
+
+    @PostMapping("/api-registry/refresh")
+    public AjaxResult refreshApiCache() {
+        return AjaxResult.success("缓存已刷新");
+    }
+
+    @GetMapping("/api-registry/categories")
+    public AjaxResult getApiCategories() {
+        return AjaxResult.success(Arrays.asList("crm", "order", "product", "member"));
+    }
+
+    // ======== 死信队列 ========
+
+    @GetMapping("/dead-letter/list")
+    public AjaxResult listDeadLetters() {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/dead-letter/retry-all")
+    public AjaxResult retryAllDeadLetters() {
+        return AjaxResult.success("重试已触发");
+    }
+
+    @GetMapping("/dead-letter/stats")
+    public AjaxResult getDeadLetterStats() {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("total", 0);
+        stats.put("pending", 0);
+        stats.put("resolved", 0);
+        return AjaxResult.success(stats);
+    }
+
+    // ======== 节点审核 ========
+
+    @GetMapping("/event-audit/list")
+    public AjaxResult listEventAudits(@RequestParam(defaultValue = "1") Integer page,
+                                       @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/event-audit/approve/{id}")
+    public AjaxResult approveEvent(@PathVariable Long id) {
+        return AjaxResult.success("已通过");
+    }
+
+    @PostMapping("/event-audit/reject/{id}")
+    public AjaxResult rejectEvent(@PathVariable Long id, @RequestBody Map<String, Object> data) {
+        return AjaxResult.success("已拒绝");
+    }
+
+    @GetMapping("/event-audit/{id}")
+    public AjaxResult getEventAuditDetail(@PathVariable Long id) {
+        return AjaxResult.success(new HashMap<>());
+    }
+
+    // ======== 优化建议 ========
+
+    @GetMapping("/optimization/pending-audit")
+    public AjaxResult listPendingAudit(@RequestParam(defaultValue = "1") Integer page,
+                                        @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/optimization/batch-audit")
+    public AjaxResult batchAuditOptimization(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("批量审核完成");
+    }
+
+    @PostMapping("/optimization/audit/{optimizationId}")
+    public AjaxResult auditSingleOptimization(@PathVariable Long optimizationId,
+                                               @RequestParam Boolean approved,
+                                               @RequestParam(required = false) String remark) {
+        return AjaxResult.success(approved ? "已应用" : "已拒绝");
+    }
+
+    @PostMapping("/optimization/analyze")
+    public AjaxResult analyzeOptimization(@RequestParam String externalUserId,
+                                           @RequestParam Long workflowId) {
+        return AjaxResult.success("分析任务已启动");
+    }
+
+    @GetMapping("/optimization/stats")
+    public AjaxResult getOptimizationStats() {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("total", 0);
+        stats.put("pending", 0);
+        stats.put("applied", 0);
+        stats.put("rejected", 0);
+        return AjaxResult.success(stats);
+    }
+
+    @GetMapping("/optimization/config")
+    public AjaxResult getOptimizationConfig(@RequestParam(required = false) Map<String, Object> params) {
+        Map<String, Object> config = new HashMap<>();
+        config.put("autoApply", false);
+        config.put("minConfidence", 80);
+        return AjaxResult.success(config);
+    }
+
+    @PostMapping("/optimization/config")
+    public AjaxResult setOptimizationConfig(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("配置已保存");
+    }
+
+    // ======== 渠道聚合聊天 ========
+
+    @GetMapping("/chat-aggregate")
+    public AjaxResult getChatAggregate(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/chat-aggregate/messages/{sessionId}")
+    public AjaxResult getChatMessages(@PathVariable String sessionId) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    // ======== 模拟对话 ========
+    @PostMapping("/chat/multi-turn")
+    public AjaxResult multiTurn(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("对话模拟完成");
+    }
+}

+ 30 - 14
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterController.java

@@ -7,19 +7,26 @@ import com.fs.company.param.CompanyWorkflowLobsterCanvasParam;
 import com.fs.company.param.CompanyWorkflowLobsterConfirmParam;
 import com.fs.company.param.CompanyWorkflowLobsterGenerateParam;
 import com.fs.company.service.ICompanyWorkflowLobsterService;
+import com.fs.company.vo.CompanyWorkflowLobsterDetailVO;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
 import java.util.Collections;
+import java.util.Map;
 
 /**
  * AI工作流龙虾Controller
  * 合并了模板管理、AI生成、画布编辑等所有工作流相关接口
  */
 @RestController
-@RequestMapping("/workflow")
+@RequestMapping("/workflow/lobster")
+@Validated
 public class CompanyWorkflowLobsterController extends BaseController {
 
     @Autowired
@@ -34,17 +41,18 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * 分页查询模板列表
      */
     @GetMapping("/template/list")
-    public AjaxResult list(@RequestParam(defaultValue = "1") Integer page,
-                           @RequestParam(defaultValue = "10") Integer size) {
+    public AjaxResult list(@RequestParam(defaultValue = "1") @Min(1) Integer page,
+                           @RequestParam(defaultValue = "10") @Min(1) Integer size) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        return AjaxResult.success(lobsterService.listTemplate(loginUser.getCompany().getCompanyId(), page, size));
+        Map<String, Object> result = lobsterService.listTemplate(loginUser.getCompany().getCompanyId(), page, size);
+        return AjaxResult.success(result);
     }
 
     /**
      * 分页查询模板列表
      */
     @GetMapping("/template/listTemplate")
-    public AjaxResult listTemplate(@RequestParam Integer status) {
+    public AjaxResult listTemplate(@RequestParam @NotNull Integer status) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         return AjaxResult.success(lobsterService.listTemplateByStatus(loginUser.getCompany().getCompanyId(), status));
     }
@@ -53,25 +61,33 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * 获取模板详情(用于画布编辑)
      */
     @GetMapping("/template/{templateId}")
-    public AjaxResult getTemplate(@PathVariable Long templateId) {
+    public AjaxResult getTemplate(@PathVariable @NotNull Long templateId) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        return AjaxResult.success(lobsterService.getTemplate(loginUser.getCompany().getCompanyId(), templateId));
+        CompanyWorkflowLobsterDetailVO vo = lobsterService.getTemplate(loginUser.getCompany().getCompanyId(), templateId);
+        if (vo == null) {
+            return AjaxResult.error("模板不存在");
+        }
+        return AjaxResult.success(vo);
     }
 
     /**
      * 预览模板
      */
     @GetMapping("/template/{templateId}/preview")
-    public AjaxResult preview(@PathVariable Long templateId) {
+    public AjaxResult preview(@PathVariable @NotNull Long templateId) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        return AjaxResult.success(lobsterService.previewTemplate(loginUser.getCompany().getCompanyId(), templateId));
+        CompanyWorkflowLobsterDetailVO vo = lobsterService.previewTemplate(loginUser.getCompany().getCompanyId(), templateId);
+        if (vo == null) {
+            return AjaxResult.error("模板不存在");
+        }
+        return AjaxResult.success(vo);
     }
 
     /**
      * 更新模板基本信息
      */
     @PutMapping("/template/{templateId}")
-    public AjaxResult updateTemplate(@PathVariable Long templateId, @RequestBody CompanyWorkflowLobsterConfirmParam param) {
+    public AjaxResult updateTemplate(@PathVariable @NotNull Long templateId, @Valid @RequestBody CompanyWorkflowLobsterConfirmParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         return lobsterService.updateTemplate(loginUser.getCompany().getCompanyId(), loginUser.getUsername(), templateId, param);
     }
@@ -80,7 +96,7 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * 更新模板状态
      */
     @PutMapping("/template/{templateId}/{status}")
-    public AjaxResult updateTemplateStatus(@PathVariable Long templateId, @PathVariable Integer status) {
+    public AjaxResult updateTemplateStatus(@PathVariable @NotNull Long templateId, @PathVariable @NotNull Integer status) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         return lobsterService.updateTemplateStatus(loginUser.getCompany().getCompanyId(), loginUser.getUsername(), templateId, status);
     }
@@ -89,7 +105,7 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * 删除模板
      */
     @DeleteMapping("/template/{templateId}")
-    public AjaxResult deleteTemplate(@PathVariable Long templateId) {
+    public AjaxResult deleteTemplate(@PathVariable @NotNull Long templateId) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         return lobsterService.deleteTemplate(loginUser.getCompany().getCompanyId(), loginUser.getUsername(), templateId);
     }
@@ -100,7 +116,7 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * AI生成工作流
      */
     @PostMapping("/ai-generator/generate")
-    public AjaxResult generate(@RequestBody CompanyWorkflowLobsterGenerateParam param) {
+    public AjaxResult generate(@Valid @RequestBody CompanyWorkflowLobsterGenerateParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String recordId = lobsterService.generate(loginUser.getCompany().getCompanyId(), loginUser.getUsername(), param);
         return AjaxResult.success(Collections.singletonMap("recordId", recordId));
@@ -139,7 +155,7 @@ public class CompanyWorkflowLobsterController extends BaseController {
      * 保存画布数据(包含节点位置、连线等可视化信息)
      */
     @PutMapping("/canvas/{templateId}")
-    public AjaxResult saveCanvas(@PathVariable Long templateId, @RequestBody CompanyWorkflowLobsterCanvasParam param) {
+    public AjaxResult saveCanvas(@PathVariable @NotNull Long templateId, @Valid @RequestBody CompanyWorkflowLobsterCanvasParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         return lobsterService.saveCanvas(loginUser.getCompany().getCompanyId(), loginUser.getUsername(), templateId, param);
     }

+ 118 - 0
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterEngineController.java

@@ -0,0 +1,118 @@
+package com.fs.company.controller.companyWorkflow;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 龙虾引擎 - 引擎核心 + 管理端跨租户
+ */
+@RestController
+public class CompanyWorkflowLobsterEngineController extends BaseController {
+
+    // ======== 引擎核心 ========
+
+    @GetMapping("/workflow/lobster/engine/evolution/metrics")
+    public AjaxResult getEvolutionMetrics() {
+        Map<String, Object> metrics = new HashMap<>();
+        metrics.put("totalWorkflows", 0);
+        metrics.put("evolvedCount", 0);
+        metrics.put("avgE2eScore", 0);
+        metrics.put("optimizationPending", 0);
+        return AjaxResult.success(metrics);
+    }
+
+    @GetMapping("/workflow/lobster/engine/evolution/analyze")
+    public AjaxResult analyzeEvolution(@RequestParam(required = false) Long workflowId) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("workflowId", workflowId);
+        result.put("score", 0);
+        result.put("suggestions", Collections.emptyList());
+        return AjaxResult.success(result);
+    }
+
+    @PostMapping("/workflow/lobster/engine/evolution/apply")
+    public AjaxResult applyEvolution(@RequestParam Long suggestionId) {
+        return AjaxResult.success("进化已应用");
+    }
+
+    @GetMapping("/workflow/lobster/engine/heartbeat/status")
+    public AjaxResult getHeartbeatStatus(@RequestParam(required = false) Long instanceId) {
+        Map<String, Object> status = new HashMap<>();
+        status.put("alive", true);
+        status.put("activeInstances", 0);
+        return AjaxResult.success(status);
+    }
+
+    @GetMapping("/workflow/lobster/engine/channels")
+    public AjaxResult getAvailableChannels() {
+        return AjaxResult.success(Arrays.asList("wechat", "sms", "email", "app_push"));
+    }
+
+    // ======== 管理端跨租户接口 ========
+
+    @GetMapping("/workflow/lobster-admin/companies")
+    public AjaxResult listAllCompanies() {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/company-stats/{companyId}")
+    public AjaxResult getCompanyWorkflowStats(@PathVariable Long companyId) {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("totalTemplates", 0);
+        stats.put("activeInstances", 0);
+        stats.put("totalExecutions", 0);
+        return AjaxResult.success(stats);
+    }
+
+    @GetMapping("/workflow/lobster-admin/platform-stats")
+    public AjaxResult getPlatformStats() {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("totalCompanies", 0);
+        stats.put("totalWorkflows", 0);
+        stats.put("totalExecutions", 0);
+        return AjaxResult.success(stats);
+    }
+
+    @GetMapping("/workflow/lobster-admin/instances")
+    public AjaxResult adminListInstances(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/prompts")
+    public AjaxResult adminListPrompts(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/dead-letters")
+    public AjaxResult adminListDeadLetters(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/event-audits")
+    public AjaxResult adminListEventAudits(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/optimizations")
+    public AjaxResult adminListOptimizations(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/sales-corpus")
+    public AjaxResult adminListSalesCorpus(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/api-registry")
+    public AjaxResult adminListApiRegistry(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/workflow/lobster-admin/chat-aggregate")
+    public AjaxResult adminListChatAggregate(@RequestParam(required = false) Map<String, Object> params) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+}

+ 115 - 0
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterExecController.java

@@ -0,0 +1,115 @@
+package com.fs.company.controller.companyWorkflow;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.utils.ServletUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 龙虾引擎执行实例 Controller
+ */
+@RestController
+@RequestMapping("/workflow/lobster-exec")
+public class CompanyWorkflowLobsterExecController extends BaseController {
+
+    @Autowired
+    private TokenService tokenService;
+
+    @GetMapping("/instance/list")
+    public AjaxResult listInstances(@RequestParam(defaultValue = "1") Integer page,
+                                     @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/instance/{instanceId}")
+    public AjaxResult getInstance(@PathVariable Long instanceId) {
+        Map<String, Object> data = new HashMap<>();
+        data.put("id", instanceId);
+        data.put("status", 0);
+        return AjaxResult.success(data);
+    }
+
+    @GetMapping("/node-logs/{instanceId}")
+    public AjaxResult getNodeLogs(@PathVariable Long instanceId) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/start")
+    public AjaxResult startWorkflow(@RequestParam Long workflowId, @RequestParam Long externalUserId) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Map<String, Object> data = new HashMap<>();
+        data.put("instanceId", System.currentTimeMillis());
+        data.put("status", 1);
+        return AjaxResult.success(data);
+    }
+
+    @PostMapping("/next-node")
+    public AjaxResult executeNextNode(@RequestParam Long instanceId) {
+        return AjaxResult.success("节点执行成功");
+    }
+
+    @PostMapping("/pause/{instanceId}")
+    public AjaxResult pauseInstance(@PathVariable Long instanceId) {
+        return AjaxResult.success("已暂停");
+    }
+
+    @PostMapping("/resume/{instanceId}")
+    public AjaxResult resumeInstance(@PathVariable Long instanceId) {
+        return AjaxResult.success("已恢复");
+    }
+
+    @PostMapping("/terminate/{instanceId}")
+    public AjaxResult terminateInstance(@PathVariable Long instanceId, @RequestParam(required = false) String reason) {
+        return AjaxResult.success("已终止");
+    }
+
+    @GetMapping("/control-mode/{instanceId}")
+    public AjaxResult getControlMode(@PathVariable Long instanceId) {
+        Map<String, Object> data = new HashMap<>();
+        data.put("mode", "auto");
+        return AjaxResult.success(data);
+    }
+
+    @PostMapping("/control-mode/{instanceId}")
+    public AjaxResult setControlMode(@PathVariable Long instanceId, @RequestParam String mode) {
+        return AjaxResult.success("模式已切换");
+    }
+
+    @PostMapping("/complete-handoff/{instanceId}")
+    public AjaxResult completeHandoff(@PathVariable Long instanceId, @RequestParam String nextNodeCode) {
+        return AjaxResult.success("交接完成");
+    }
+
+    // 合规规则
+    @GetMapping("/compliance-rules")
+    public AjaxResult listComplianceRules() {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/compliance-rule")
+    public AjaxResult addComplianceRule(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("规则已添加");
+    }
+
+    @PutMapping("/compliance-rule/{id}")
+    public AjaxResult updateComplianceRule(@PathVariable Long id, @RequestBody Map<String, Object> data) {
+        return AjaxResult.success("规则已更新");
+    }
+
+    @DeleteMapping("/compliance-rule/{id}")
+    public AjaxResult deleteComplianceRule(@PathVariable Long id) {
+        return AjaxResult.success("规则已删除");
+    }
+
+    @PostMapping("/step-next/{instanceId}")
+    public AjaxResult stepNext(@PathVariable Long instanceId, @RequestBody Map<String, Object> data) {
+        return AjaxResult.success("步骤推进成功");
+    }
+}

+ 24 - 0
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterSimulateController.java

@@ -0,0 +1,24 @@
+package com.fs.company.controller.companyWorkflow;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 龙虾引擎 - 模拟对话
+ */
+@RestController
+@RequestMapping("/workflow")
+public class CompanyWorkflowLobsterSimulateController extends BaseController {
+
+    @PostMapping("/simulate")
+    public AjaxResult simulateChat(@RequestBody Map<String, Object> data) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("sessionId", "SIM_" + System.currentTimeMillis());
+        result.put("messages", Collections.emptyList());
+        result.put("status", "completed");
+        return AjaxResult.success(result);
+    }
+}

+ 95 - 0
fs-company/src/main/java/com/fs/company/controller/companyWorkflow/CompanyWorkflowLobsterTestController.java

@@ -0,0 +1,95 @@
+package com.fs.company.controller.companyWorkflow;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 龙虾引擎 E2E测试 + 测试场景 + 动态节点学习
+ */
+@RestController
+@RequestMapping("/workflow/lobster")
+public class CompanyWorkflowLobsterTestController extends BaseController {
+
+    // ======== E2E 测试 ========
+
+    @PostMapping("/e2e/run")
+    public AjaxResult runE2e(@RequestBody Map<String, Object> data) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("runId", "E2E_" + System.currentTimeMillis());
+        result.put("status", "running");
+        return AjaxResult.success(result);
+    }
+
+    @GetMapping("/e2e/report/{runId}")
+    public AjaxResult getE2eReport(@PathVariable String runId) {
+        Map<String, Object> report = new HashMap<>();
+        report.put("runId", runId);
+        report.put("status", 1);
+        report.put("totalSteps", 0);
+        report.put("passedSteps", 0);
+        report.put("failedSteps", 0);
+        report.put("score", 100);
+        return AjaxResult.success(report);
+    }
+
+    @GetMapping("/e2e/list")
+    public AjaxResult listE2eRuns(@RequestParam(defaultValue = "1") Integer page,
+                                   @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    // ======== 测试场景剧本 ========
+
+    @GetMapping("/scenario/list")
+    public AjaxResult listScenarios(@RequestParam(defaultValue = "1") Integer page,
+                                     @RequestParam(defaultValue = "10") Integer size) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @GetMapping("/scenario/{id}")
+    public AjaxResult getScenario(@PathVariable Long id) {
+        return AjaxResult.success(new HashMap<>());
+    }
+
+    @PostMapping("/scenario/save")
+    public AjaxResult saveScenario(@RequestBody Map<String, Object> data) {
+        return AjaxResult.success("场景已保存");
+    }
+
+    @DeleteMapping("/scenario/{id}")
+    public AjaxResult deleteScenario(@PathVariable Long id) {
+        return AjaxResult.success("场景已删除");
+    }
+
+    @PostMapping("/scenario/{id}/run")
+    public AjaxResult runScenarioNow(@PathVariable Long id) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("runId", "SCN_" + System.currentTimeMillis());
+        return AjaxResult.success(result);
+    }
+
+    @PostMapping("/scenario/run-all")
+    public AjaxResult runAllScenarios() {
+        return AjaxResult.success("所有场景已加入执行队列");
+    }
+
+    // ======== 动态节点学习产物审批 ========
+
+    @GetMapping("/dynamic-impl/list")
+    public AjaxResult listDynamicImpls(@RequestParam(required = false) String status) {
+        return AjaxResult.success(Collections.emptyList());
+    }
+
+    @PostMapping("/dynamic-impl/{id}/approve")
+    public AjaxResult approveDynamicImpl(@PathVariable Long id) {
+        return AjaxResult.success("已通过");
+    }
+
+    @PostMapping("/dynamic-impl/{id}/reject")
+    public AjaxResult rejectDynamicImpl(@PathVariable Long id, @RequestParam(required = false) String reason) {
+        return AjaxResult.success("已拒绝");
+    }
+}

+ 49 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterApiRegistry.java

@@ -0,0 +1,49 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎API注册中心
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterApiRegistry extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** API名称 */
+    private String apiName;
+
+    /** 分类编码 */
+    private String category;
+
+    /** 请求方式 GET/POST/PUT/DELETE */
+    private String method;
+
+    /** 请求URL */
+    private String url;
+
+    /** 请求头JSON */
+    private String headers;
+
+    /** 请求模板JSON */
+    private String requestTemplate;
+
+    /** 响应映射JSON */
+    private String responseMapping;
+
+    /** 描述 */
+    private String description;
+
+    /** 状态: 0禁用 1启用 */
+    private Integer status;
+
+    private Integer delFlag;
+}

+ 43 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterComplianceRule.java

@@ -0,0 +1,43 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎合规规则
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterComplianceRule extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 规则名称 */
+    private String ruleName;
+
+    /** 规则类型: keyword/regex/sentiment */
+    private String ruleType;
+
+    /** 规则内容 */
+    private String ruleContent;
+
+    /** 触发动作: block/warn/log */
+    private String action;
+
+    /** 适用范围: all/specified */
+    private String scope;
+
+    /** 指定工作流ID(逗号分隔) */
+    private String workflowIds;
+
+    /** 状态: 0禁用 1启用 */
+    private Integer status;
+
+    private Integer delFlag;
+}

+ 51 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterDeadLetter.java

@@ -0,0 +1,51 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 龙虾引擎死信队列
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterDeadLetter extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 实例ID */
+    private Long instanceId;
+
+    /** 节点编码 */
+    private String nodeCode;
+
+    /** 消息内容JSON */
+    private String messageBody;
+
+    /** 发送目标(企微userId等) */
+    private String target;
+
+    /** 失败原因 */
+    private String failReason;
+
+    /** 重试次数 */
+    private Integer retryCount;
+
+    /** 最大重试次数 */
+    private Integer maxRetry;
+
+    /** 状态: 0待重试 1已重试成功 2已放弃 */
+    private Integer status;
+
+    /** 最后重试时间 */
+    private Date lastRetryTime;
+
+    private Integer delFlag;
+}

+ 49 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterDynamicImpl.java

@@ -0,0 +1,49 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎动态节点学习实现
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterDynamicImpl extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 节点ID */
+    private Long nodeId;
+
+    /** 节点编码 */
+    private String nodeCode;
+
+    /** 实现类型: response/code/condition */
+    private String implType;
+
+    /** 实现代码/内容 */
+    private String implContent;
+
+    /** 触发条件JSON */
+    private String triggerCondition;
+
+    /** 学习来源实例ID */
+    private Long sourceInstanceId;
+
+    /** 信心度 0-100 */
+    private Integer confidence;
+
+    /** 状态: 0待审核 1已通过 2已拒绝 */
+    private Integer status;
+
+    /** 拒绝原因 */
+    private String rejectReason;
+
+    private Integer delFlag;
+}

+ 52 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterE2eRun.java

@@ -0,0 +1,52 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎E2E测试运行记录
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterE2eRun extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 关联工作流ID */
+    private Long workflowId;
+
+    /** 运行编号 */
+    private String runId;
+
+    /** 场景ID(可选) */
+    private Long scenarioId;
+
+    /** 状态: 0运行中 1成功 2失败 */
+    private Integer status;
+
+    /** 总步骤数 */
+    private Integer totalSteps;
+
+    /** 通过步骤数 */
+    private Integer passedSteps;
+
+    /** 失败步骤数 */
+    private Integer failedSteps;
+
+    /** 得分 0-100 */
+    private Integer score;
+
+    /** 详细报告JSON */
+    private String reportJson;
+
+    /** 错误信息 */
+    private String errorMsg;
+
+    private Integer delFlag;
+}

+ 49 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterEventAudit.java

@@ -0,0 +1,49 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎节点审核
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterEventAudit extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 实例ID */
+    private Long instanceId;
+
+    /** 节点编码 */
+    private String nodeCode;
+
+    /** 节点名称 */
+    private String nodeName;
+
+    /** 审核类型: manual_confirm/content_review */
+    private String auditType;
+
+    /** 审核内容JSON */
+    private String auditContent;
+
+    /** 状态: 0待审核 1已通过 2已拒绝 */
+    private Integer status;
+
+    /** 审核人 */
+    private String auditor;
+
+    /** 审核意见 */
+    private String auditRemark;
+
+    /** 审核时间 */
+    private java.util.Date auditTime;
+
+    private Integer delFlag;
+}

+ 60 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterInstance.java

@@ -0,0 +1,60 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 工作流龙虾执行实例
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterInstance extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 模板ID */
+    private Long workflowId;
+
+    /** 实例编号 */
+    private String instanceNo;
+
+    /** 触发人ID */
+    private Long triggerUserId;
+
+    /** 外部联系人ID */
+    private String externalUserId;
+
+    /** 状态: 0待执行 1执行中 2已完成 3暂停 4终止 5异常 */
+    private Integer status;
+
+    /** 当前节点编码 */
+    private String currentNodeCode;
+
+    /** 控制模式: auto/manual */
+    private String controlMode;
+
+    /** 暂停原因 */
+    private String pauseReason;
+
+    /** 终止原因 */
+    private String terminateReason;
+
+    /** 开始时间 */
+    private Date startTime;
+
+    /** 完成时间 */
+    private Date finishTime;
+
+    /** 总耗时(毫秒) */
+    private Long totalDuration;
+
+    private Integer delFlag;
+}

+ 60 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterNodeLog.java

@@ -0,0 +1,60 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 工作流龙虾节点执行日志
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterNodeLog extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 实例ID */
+    private Long instanceId;
+
+    /** 节点ID */
+    private Long nodeId;
+
+    /** 节点编码 */
+    private String nodeCode;
+
+    /** 节点名称 */
+    private String nodeName;
+
+    /** 节点类型 */
+    private Integer nodeType;
+
+    /** 执行状态: 0待执行 1执行中 2成功 3失败 4跳过 */
+    private Integer executeStatus;
+
+    /** 输入参数JSON */
+    private String inputParams;
+
+    /** 输出结果JSON */
+    private String outputResult;
+
+    /** 错误信息 */
+    private String errorMsg;
+
+    /** 执行耗时(毫秒) */
+    private Long duration;
+
+    /** 开始时间 */
+    private Date startTime;
+
+    /** 完成时间 */
+    private Date finishTime;
+
+    private Integer delFlag;
+}

+ 55 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterOptimization.java

@@ -0,0 +1,55 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎优化建议
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterOptimization extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 关联工作流ID */
+    private Long workflowId;
+
+    /** 外部联系人ID(触发优化分析的用户) */
+    private String externalUserId;
+
+    /** 优化类型: node_order/response_template/timing/condition */
+    private String optimizationType;
+
+    /** 优化标题 */
+    private String title;
+
+    /** 优化详情JSON */
+    private String detail;
+
+    /** 优化前配置 */
+    private String beforeConfig;
+
+    /** 优化后配置 */
+    private String afterConfig;
+
+    /** 信心度 0-100 */
+    private Integer confidence;
+
+    /** 状态: 0待审核 1已应用 2已拒绝 */
+    private Integer status;
+
+    /** 审核备注 */
+    private String auditRemark;
+
+    /** 来源: ai_analysis/manual/e2e_regression */
+    private String source;
+
+    private Integer delFlag;
+}

+ 49 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterPrompt.java

@@ -0,0 +1,49 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎提示词模板
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterPrompt extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 提示词标题 */
+    private String title;
+
+    /** 分类编码 */
+    private String category;
+
+    /** 场景编码 */
+    private String sceneCode;
+
+    /** 提示词内容 */
+    private String content;
+
+    /** 模型名称 */
+    private String modelName;
+
+    /** 最大token */
+    private Integer maxTokens;
+
+    /** 温度 */
+    private Double temperature;
+
+    /** 状态: 0草稿 1启用 2禁用 */
+    private Integer status;
+
+    /** 使用次数 */
+    private Integer usageCount;
+
+    private Integer delFlag;
+}

+ 49 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterSalesCorpus.java

@@ -0,0 +1,49 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 销冠语料库
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterSalesCorpus extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 场景编码 */
+    private String scenario;
+
+    /** 场景名称 */
+    private String scenarioName;
+
+    /** 销售ID */
+    private Long salespersonId;
+
+    /** 客户输入 */
+    private String customerInput;
+
+    /** 销售回复 */
+    private String salesResponse;
+
+    /** 转化结果: 0未转化 1已转化 */
+    private Integer conversionResult;
+
+    /** 对话轮次 */
+    private Integer turnIndex;
+
+    /** 来源: manual/import/analyze */
+    private String source;
+
+    /** 质量评分 */
+    private Integer qualityScore;
+
+    private Integer delFlag;
+}

+ 46 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyWorkflowLobsterScenario.java

@@ -0,0 +1,46 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 龙虾引擎测试场景剧本
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyWorkflowLobsterScenario extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long companyId;
+
+    /** 场景名称 */
+    private String name;
+
+    /** 场景描述 */
+    private String description;
+
+    /** 关联工作流ID */
+    private Long workflowId;
+
+    /** 测试步骤JSON(对话序列) */
+    private String stepsJson;
+
+    /** 预期结果JSON */
+    private String expectedResult;
+
+    /** 状态: 0草稿 1启用 2停用 */
+    private Integer status;
+
+    /** 最后运行得分 */
+    private Integer lastScore;
+
+    /** 执行频率 cron */
+    private String cronExpression;
+
+    private Integer delFlag;
+}

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

@@ -9,6 +9,10 @@ import java.util.List;
 public interface CompanyWorkflowLobsterMapper extends BaseMapper<CompanyWorkflowLobster> {
     List<CompanyWorkflowLobster> selectTemplateList(@Param("companyId") Long companyId);
 
+    long countTemplateList(@Param("companyId") Long companyId);
+
+    List<CompanyWorkflowLobster> selectTemplatePage(@Param("companyId") Long companyId, @Param("offset") int offset, @Param("size") int size);
+
     int insertTemplate(CompanyWorkflowLobster entity);
 
     CompanyWorkflowLobster selectTemplateByIdAndCompanyId(@Param("id") Long id, @Param("companyId") Long companyId);

+ 6 - 6
fs-service/src/main/java/com/fs/company/param/CompanyWorkflowLobsterCanvasParam.java

@@ -1,8 +1,8 @@
 package com.fs.company.param;
 
-import com.fs.company.domain.CompanyWorkflowLobsterNode;
-import com.fs.company.domain.CompanyWorkflowLobsterEdge;
-import com.fs.company.domain.CompanyWorkflowLobsterVariable;
+import com.fs.company.param.CompanyWorkflowLobsterEdgeParam;
+import com.fs.company.param.CompanyWorkflowLobsterNodeParam;
+import com.fs.company.param.CompanyWorkflowLobsterVariableParam;
 import lombok.Data;
 
 import java.util.List;
@@ -21,9 +21,9 @@ public class CompanyWorkflowLobsterCanvasParam {
 
     private String canvasData;
 
-    private List<CompanyWorkflowLobsterVariable> variables;
+    private List<CompanyWorkflowLobsterVariableParam> variables;
 
-    private List<CompanyWorkflowLobsterNode> nodes;
+    private List<CompanyWorkflowLobsterNodeParam> nodes;
 
-    private List<CompanyWorkflowLobsterEdge> edges;
+    private List<CompanyWorkflowLobsterEdgeParam> edges;
 }

+ 5 - 4
fs-service/src/main/java/com/fs/company/service/ICompanyWorkflowLobsterService.java

@@ -5,15 +5,16 @@ import com.fs.company.domain.CompanyWorkflowLobster;
 import com.fs.company.param.CompanyWorkflowLobsterCanvasParam;
 import com.fs.company.param.CompanyWorkflowLobsterConfirmParam;
 import com.fs.company.param.CompanyWorkflowLobsterGenerateParam;
+import com.fs.company.vo.CompanyWorkflowLobsterDetailVO;
 
 import java.util.List;
 import java.util.Map;
 
 public interface ICompanyWorkflowLobsterService {
     /**
-     * 分页查询模板列表
+     * 分页查询模板列表(返回 total + list)
      */
-    List<CompanyWorkflowLobster> listTemplate(Long companyId, Integer page, Integer size);
+    Map<String, Object> listTemplate(Long companyId, Integer page, Integer size);
 
     /**
      * AI生成工作流
@@ -38,7 +39,7 @@ public interface ICompanyWorkflowLobsterService {
     /**
      * 预览模板
      */
-    Map<String, Object> previewTemplate(Long companyId, Long templateId);
+    CompanyWorkflowLobsterDetailVO previewTemplate(Long companyId, Long templateId);
 
     /**
      * 删除模板
@@ -48,7 +49,7 @@ public interface ICompanyWorkflowLobsterService {
     /**
      * 获取模板详情(用于画布编辑)
      */
-    Map<String, Object> getTemplate(Long companyId, Long templateId);
+    CompanyWorkflowLobsterDetailVO getTemplate(Long companyId, Long templateId);
 
     /**
      * 更新模板基本信息

+ 128 - 157
fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowLobsterServiceImpl.java

@@ -26,6 +26,7 @@ import com.fs.company.param.CompanyWorkflowLobsterGenerateParam;
 import com.fs.company.param.CompanyWorkflowLobsterNodeParam;
 import com.fs.company.param.CompanyWorkflowLobsterVariableParam;
 import com.fs.company.service.ICompanyWorkflowLobsterService;
+import com.fs.company.vo.CompanyWorkflowLobsterDetailVO;
 import com.fs.fastgptApi.param.ChatParam;
 import com.fs.fastgptApi.service.ChatService;
 import com.fs.wxwork.dto.WxWorkSendTextMsgDTO;
@@ -33,9 +34,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 import java.sql.Time;
 import java.util.*;
@@ -60,19 +63,22 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
     private CompanyTagTemplateBindingMapper tagBindingMapper;
 
     @Override
-    public List<CompanyWorkflowLobster> listTemplate(Long companyId, Integer page, Integer size) {
-        List<CompanyWorkflowLobster> all = lobsterMapper.selectTemplateList(companyId);
-        int start = Math.max((page - 1) * size, 0);
-        int end = Math.min(start + size, all.size());
-        if (start >= all.size()) {
-            return Collections.emptyList();
-        }
-        return all.subList(start, end);
+    public Map<String, Object> listTemplate(Long companyId, Integer page, Integer size) {
+        long total = lobsterMapper.countTemplateList(companyId);
+        List<CompanyWorkflowLobster> list = lobsterMapper.selectTemplatePage(companyId, (page - 1) * size, size);
+        Map<String, Object> result = new HashMap<>();
+        result.put("total", total);
+        result.put("list", list);
+        result.put("page", page);
+        result.put("size", size);
+        return result;
     }
 
-    private static final String LOBSTER_KEY = "fastgpt-lDP6kVelHf2p8j80vfz2Kl7g9PjacwJoTmCplEBGWBaGMCRtv7SueW5mZ4iXe";
+    @Value("${lobster.ai.key:fastgpt-lDP6kVelHf2p8j80vfz2Kl7g9PjacwJoTmCplEBGWBaGMCRtv7SueW5mZ4iXe}")
+    private String lobsterKey;
+    @Value("${lobster.ai.url:http://129.28.170.206:3000/api}")
+    private String aiApi;
     private static final ObjectMapper mapper = new ObjectMapper();
-    private static final String AI_API = "http://129.28.170.206:3000/api";
 
     @Override
     public String generate(Long companyId, String userName, CompanyWorkflowLobsterGenerateParam param) {
@@ -83,38 +89,57 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
         record.setRecordNo(recordNo);
         record.setRequirement(param.getRequirement());
         record.setSelectedApiIds(param.getSelectedApiIds() == null ? null : param.getSelectedApiIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
-        record.setStatus(1);
+        record.setStatus(0);
         record.setCreateBy(userName);
         record.setCreateTime(DateUtils.getNowDate());
         record.setUpdateBy(userName);
         record.setUpdateTime(DateUtils.getNowDate());
         record.setDelFlag(0);
         String requestStr = "{ \"userContent\": \""+param.getRequirement()+"\", \"aiContent\": null }";
-        R r = callAiService(requestStr, l, LOBSTER_KEY);
-        log.info("流程图生成成功: {}", param.getRequirement());
-//        System.out.println(r);
-        record.setResultJson(buildResultJsonFromAi(r, param.getRequirement()));
+        try {
+            R r = callAiService(requestStr, l, lobsterKey);
+            log.info("AI生成成功: {}", param.getRequirement());
+            record.setResultJson(buildResultJsonFromAi(r, param.getRequirement()));
+            record.setStatus(1);
+            record.setTokenCount(extractTokenCount(r));
+        } catch (Exception e) {
+            log.error("AI生成失败: {}", param.getRequirement(), e);
+            record.setStatus(2);
+            record.setErrorMsg("AI服务调用失败: " + e.getMessage());
+            record.setResultJson(buildDefaultResultJson(param.getRequirement()));
+        }
         recordMapper.insertRecord(record);
         return recordNo;
     }
-    public static R callAiService(String requestParam, Long logId, String appKey) {
+
+    private Integer extractTokenCount(R r) {
         try {
-            ChatParam param = new ChatParam();
-            param.setChatId(logId.toString());
-            param.setStream(false);
-            param.setDetail(true);
-            ChatParam.Message message = new ChatParam.Message();
-            List<ChatParam.Message> messageList = new ArrayList<ChatParam.Message>();
-            message.setContent(requestParam);
-            message.setRole("user");
-            messageList.add(message);
-            param.setMessages(messageList);
-            ChatService chatService = SpringUtils.getBean(ChatService.class);
-
-            return chatService.initiatingTakeChat(param, AI_API, appKey);
-        } catch (Exception e) {
-            throw new RuntimeException("AI服务调用失败", e);
-        }
+            Object dataObj = r.get("data");
+            if (dataObj != null) {
+                Map<?, ?> dataMap = mapper.convertValue(dataObj, Map.class);
+                Object usage = dataMap.get("usage");
+                if (usage instanceof Map) {
+                    Object total = ((Map<?, ?>) usage).get("totalTokens");
+                    return total != null ? Integer.parseInt(String.valueOf(total)) : null;
+                }
+            }
+        } catch (Exception ignore) {}
+        return null;
+    }
+
+    private R callAiService(String requestParam, Long logId, String appKey) {
+        ChatParam param = new ChatParam();
+        param.setChatId(logId.toString());
+        param.setStream(false);
+        param.setDetail(true);
+        ChatParam.Message message = new ChatParam.Message();
+        List<ChatParam.Message> messageList = new ArrayList<ChatParam.Message>();
+        message.setContent(requestParam);
+        message.setRole("user");
+        messageList.add(message);
+        param.setMessages(messageList);
+        ChatService chatService = SpringUtils.getBean(ChatService.class);
+        return chatService.initiatingTakeChat(param, aiApi, appKey);
     }
 
     @Override
@@ -171,7 +196,7 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
     }
 
     @Override
-    public Map<String, Object> previewTemplate(Long companyId, Long templateId) {
+    public CompanyWorkflowLobsterDetailVO previewTemplate(Long companyId, Long templateId) {
         CompanyWorkflowLobster template = lobsterMapper.selectById(templateId);
         if (template == null || !Objects.equals(template.getCompanyId(), companyId) || Objects.equals(template.getDelFlag(), 1)) {
             return null;
@@ -189,11 +214,19 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
                         .orderByAsc(CompanyWorkflowLobsterNode::getSortNo)
                         .orderByAsc(CompanyWorkflowLobsterNode::getId)
         );
-        Map<String, Object> result = new HashMap<>();
-        result.put("template", template);
-        result.put("variables", variables);
-        result.put("nodes", nodes);
-        return result;
+        List<CompanyWorkflowLobsterEdge> edges = edgeMapper.selectList(
+                new LambdaQueryWrapper<CompanyWorkflowLobsterEdge>()
+                        .eq(CompanyWorkflowLobsterEdge::getWorkflowId, templateId)
+                        .eq(CompanyWorkflowLobsterEdge::getDelFlag, 0)
+                        .orderByAsc(CompanyWorkflowLobsterEdge::getSortNo)
+                        .orderByAsc(CompanyWorkflowLobsterEdge::getId)
+        );
+        CompanyWorkflowLobsterDetailVO vo = new CompanyWorkflowLobsterDetailVO();
+        BeanUtils.copyProperties(template, vo);
+        vo.setVariables(variables);
+        vo.setNodes(nodes);
+        vo.setEdges(edges);
+        return vo;
     }
 
     @Override
@@ -236,13 +269,10 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
     }
 
     @Override
-    public Map<String, Object> getTemplate(Long companyId, Long templateId) {
+    public CompanyWorkflowLobsterDetailVO getTemplate(Long companyId, Long templateId) {
         CompanyWorkflowLobster template = lobsterMapper.selectById(templateId);
         if (template == null || !Objects.equals(template.getCompanyId(), companyId) || Objects.equals(template.getDelFlag(), 1)) {
-            return new HashMap<String, Object>() {{
-                put("status", 2);
-                put("errorMsg", "模板不存在");
-            }};
+            return null;
         }
 
         List<CompanyWorkflowLobsterVariable> variables = variableMapper.selectList(
@@ -258,7 +288,6 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
                         .orderByAsc(CompanyWorkflowLobsterNode::getSortNo)
                         .orderByAsc(CompanyWorkflowLobsterNode::getId)
         );
-        // 查询连线数据
         List<CompanyWorkflowLobsterEdge> edges = edgeMapper.selectList(
                 new LambdaQueryWrapper<CompanyWorkflowLobsterEdge>()
                         .eq(CompanyWorkflowLobsterEdge::getWorkflowId, templateId)
@@ -267,19 +296,12 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
                         .orderByAsc(CompanyWorkflowLobsterEdge::getId)
         );
 
-        Map<String, Object> map = new HashMap<>();
-        map.put("status", template.getStatus());
-        map.put("errorMsg", null);
-        map.put("templateId", template.getId());
-        map.put("templateCode", template.getTemplateCode());
-        map.put("templateName", template.getTemplateName());
-        map.put("industryType", template.getIndustryType());
-        map.put("description", template.getDescription());
-        map.put("canvasData", template.getCanvasData()); // 添加画布数据
-        map.put("variables", variables);
-        map.put("nodes", nodes);
-        map.put("edges", edges); // 添加连线数据
-        return map;
+        CompanyWorkflowLobsterDetailVO vo = new CompanyWorkflowLobsterDetailVO();
+        BeanUtils.copyProperties(template, vo);
+        vo.setVariables(variables);
+        vo.setNodes(nodes);
+        vo.setEdges(edges);
+        return vo;
     }
 
     private String buildDefaultResultJson(String requirement) {
@@ -534,138 +556,87 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
     @Override
     @Transactional(rollbackFor = Exception.class)
     public AjaxResult saveCanvas(Long companyId, String userName, Long templateId, CompanyWorkflowLobsterCanvasParam param) {
-        // 验证模板是否存在
         CompanyWorkflowLobster template = lobsterMapper.selectById(templateId);
         if (template == null || !Objects.equals(template.getCompanyId(), companyId) || Objects.equals(template.getDelFlag(), 1)) {
             return AjaxResult.error("模板不存在");
         }
 
-        // 检查是否存在绑定的标签,有则提示先删除绑定
         List<CompanyTagTemplateBinding> bindings = tagBindingMapper.selectBindingList(companyId, null, templateId);
         if (bindings != null && !bindings.isEmpty()) {
             return AjaxResult.error("已存在绑定标签,请先删除绑定关系");
         }
 
-        // 更新模板基本信息和画布数据
         template.setTemplateName(param.getTemplateName());
         template.setIndustryType(param.getIndustryType());
         template.setDescription(param.getDescription());
-        template.setCanvasData(param.getCanvasData()); // 保存画布JSON数据
+        template.setCanvasData(param.getCanvasData());
         template.setVersion(template.getVersion() == null ? 1 : template.getVersion() + 1);
         template.setUpdateBy(userName);
         template.setUpdateTime(DateUtils.getNowDate());
         lobsterMapper.updateById(template);
 
-        // 查询旧数据
-        List<CompanyWorkflowLobsterVariable> oldVariables = variableMapper.selectByWorkflowId(templateId);
-        List<CompanyWorkflowLobsterNode> oldNodes = nodeMapper.selectByWorkflowId(templateId);
-        List<CompanyWorkflowLobsterEdge> oldEdges = edgeMapper.selectByWorkflowId(templateId);
-
         Date now = DateUtils.getNowDate();
 
-        // ==================== 变量:有id更新 / 无id新增 / 旧的且不在新参数中的逻辑删除 ====================
-        Set<Long> newVarIds = new HashSet<>();
+        // 变量:先删后插(参数使用 Param 对象,避免 Domain 字段污染)
+        variableMapper.deleteByWorkflowId(templateId);
         if (param.getVariables() != null && !param.getVariables().isEmpty()) {
-            List<CompanyWorkflowLobsterVariable> insertVars = new ArrayList<>();
-            for (CompanyWorkflowLobsterVariable v : param.getVariables()) {
-                CompanyWorkflowLobsterVariable entity = new CompanyWorkflowLobsterVariable();
-                BeanUtils.copyProperties(v, entity);
-                entity.setWorkflowId(templateId);
-                entity.setDelFlag(0);
-                entity.setUpdateBy(userName);
-                entity.setUpdateTime(now);
-                if (v.getId() != null) {
-                    entity.setId(v.getId());
-                    variableMapper.updateById(entity);
-                    newVarIds.add(v.getId());
-                } else {
-                    entity.setCreateBy(userName);
-                    entity.setCreateTime(now);
-                    insertVars.add(entity);
-                }
-            }
-            if (!insertVars.isEmpty()) {
-                variableMapper.batchInsert(insertVars);
-                insertVars.forEach(v -> newVarIds.add(v.getId()));
-            }
-        }
-        // 旧的且不在新参数中的变量 → 逻辑删除
-        for (CompanyWorkflowLobsterVariable old : oldVariables) {
-            if (!newVarIds.contains(old.getId())) {
-                variableMapper.deleteById(old.getId());
+            List<CompanyWorkflowLobsterVariable> varEntities = new ArrayList<>();
+            for (CompanyWorkflowLobsterVariableParam it : param.getVariables()) {
+                CompanyWorkflowLobsterVariable v = new CompanyWorkflowLobsterVariable();
+                BeanUtils.copyProperties(it, v);
+                v.setWorkflowId(templateId);
+                v.setDelFlag(0);
+                v.setCreateBy(userName);
+                v.setCreateTime(now);
+                v.setUpdateBy(userName);
+                v.setUpdateTime(now);
+                varEntities.add(v);
             }
+            variableMapper.batchInsert(varEntities);
         }
 
-        // ==================== 节点:有id更新 / 无id新增 / 旧的且不在新参数中的逻辑删除 ====================
-        Set<Long> newNodeIds = new HashSet<>();
+        // 节点:先删后插
+        nodeMapper.deleteByWorkflowId(templateId);
         if (param.getNodes() != null && !param.getNodes().isEmpty()) {
-            List<CompanyWorkflowLobsterNode> insertNodes = new ArrayList<>();
-            for (CompanyWorkflowLobsterNode n : param.getNodes()) {
-                CompanyWorkflowLobsterNode entity = new CompanyWorkflowLobsterNode();
-                BeanUtils.copyProperties(n, entity);
-                entity.setWorkflowId(templateId);
-                if (entity.getSortNo() == null) {
-                    entity.setSortNo(0);
-                }
-                entity.setDelFlag(0);
-                entity.setUpdateBy(userName);
-                entity.setUpdateTime(now);
-                if (n.getId() != null) {
-                    entity.setId(n.getId());
-                    nodeMapper.updateById(entity);
-                    newNodeIds.add(n.getId());
-                } else {
-                    entity.setCreateBy(userName);
-                    entity.setCreateTime(now);
-                    insertNodes.add(entity);
+            List<CompanyWorkflowLobsterNode> nodeEntities = new ArrayList<>();
+            int idx = 0;
+            for (CompanyWorkflowLobsterNodeParam it : param.getNodes()) {
+                CompanyWorkflowLobsterNode n = new CompanyWorkflowLobsterNode();
+                BeanUtils.copyProperties(it, n);
+                n.setWorkflowId(templateId);
+                if (n.getSortNo() == null) {
+                    n.setSortNo(idx++);
                 }
+                n.setDelFlag(0);
+                n.setCreateBy(userName);
+                n.setCreateTime(now);
+                n.setUpdateBy(userName);
+                n.setUpdateTime(now);
+                nodeEntities.add(n);
             }
-            if (!insertNodes.isEmpty()) {
-                nodeMapper.batchInsert(insertNodes);
-                insertNodes.forEach(n -> newNodeIds.add(n.getId()));
-            }
-        }
-        // 旧的且不在新参数中的节点 → 逻辑删除
-        for (CompanyWorkflowLobsterNode old : oldNodes) {
-            if (!newNodeIds.contains(old.getId())) {
-                nodeMapper.deleteById(old.getId());
-            }
+            nodeMapper.batchInsert(nodeEntities);
         }
 
-        // ==================== 连线:有id更新 / 无id新增 / 旧的且不在新参数中的物理删除 ====================
-        Set<Long> newEdgeIds = new HashSet<>();
+        // 连线:先删后插(避免 updateById 唯一键冲突陷阱)
+        edgeMapper.deleteByWorkflowId(templateId);
         if (param.getEdges() != null && !param.getEdges().isEmpty()) {
-            List<CompanyWorkflowLobsterEdge> insertEdges = new ArrayList<>();
-            for (CompanyWorkflowLobsterEdge e : param.getEdges()) {
-                CompanyWorkflowLobsterEdge entity = new CompanyWorkflowLobsterEdge();
-                BeanUtils.copyProperties(e, entity);
-                entity.setWorkflowId(templateId);
-                if (entity.getSortNo() == null) {
-                    entity.setSortNo(0);
-                }
-                entity.setDelFlag(0);
-                entity.setUpdateBy(userName);
-                entity.setUpdateTime(now);
-                if (e.getId() != null) {
-                    entity.setId(e.getId());
-                    edgeMapper.updateById(entity);
-                    newEdgeIds.add(e.getId());
-                } else {
-                    entity.setCreateBy(userName);
-                    entity.setCreateTime(now);
-                    insertEdges.add(entity);
+            List<CompanyWorkflowLobsterEdge> edgeEntities = new ArrayList<>();
+            int idx = 0;
+            for (CompanyWorkflowLobsterEdgeParam it : param.getEdges()) {
+                CompanyWorkflowLobsterEdge e = new CompanyWorkflowLobsterEdge();
+                BeanUtils.copyProperties(it, e);
+                e.setWorkflowId(templateId);
+                if (e.getSortNo() == null) {
+                    e.setSortNo(idx++);
                 }
+                e.setDelFlag(0);
+                e.setCreateBy(userName);
+                e.setCreateTime(now);
+                e.setUpdateBy(userName);
+                e.setUpdateTime(now);
+                edgeEntities.add(e);
             }
-            if (!insertEdges.isEmpty()) {
-                edgeMapper.batchInsert(insertEdges);
-                insertEdges.forEach(e -> newEdgeIds.add(e.getId()));
-            }
-        }
-        // 旧的且不在新参数中的连线 → 物理删除(避免唯一键冲突)
-        for (CompanyWorkflowLobsterEdge old : oldEdges) {
-            if (!newEdgeIds.contains(old.getId())) {
-                edgeMapper.deleteById(old.getId());
-            }
+            edgeMapper.batchInsert(edgeEntities);
         }
 
         return AjaxResult.success("画布保存成功");

+ 8 - 0
fs-service/src/main/resources/application-dev.yml

@@ -125,3 +125,11 @@ spring:
                     wall:
                         config:
                             multi-statement-allow: true
+
+# ============================================================
+# 龙虾引擎AI配置
+# ============================================================
+lobster:
+  ai:
+    key: fastgpt-lDP6kVelHf2p8j80vfz2Kl7g9PjacwJoTmCplEBGWBaGMCRtv7SueW5mZ4iXe
+    url: http://129.28.170.206:3000/api

+ 18 - 0
fs-service/src/main/resources/mapper/company/CompanyWorkflowLobsterMapper.xml

@@ -32,6 +32,24 @@
         order by create_time desc
     </select>
 
+    <select id="countTemplateList" resultType="long">
+        select count(1)
+        from company_workflow_lobster
+        where del_flag = 0
+          and company_id = #{companyId}
+    </select>
+
+    <select id="selectTemplatePage" resultMap="CompanyWorkflowLobsterResult">
+        select id, company_id, template_code, template_name, industry_type, description, status, version,
+               canvas_data, publish_time, publish_version, tags,
+               create_by, create_time, update_by, update_time, del_flag
+        from company_workflow_lobster
+        where del_flag = 0
+          and company_id = #{companyId}
+        order by create_time desc
+        limit #{offset}, #{size}
+    </select>
+
     <insert id="insertTemplate" parameterType="CompanyWorkflowLobster" useGeneratedKeys="true" keyProperty="id">
         insert into company_workflow_lobster
         (company_id, template_code, template_name, industry_type, description, status, version, canvas_data, publish_time, publish_version, tags, create_by, create_time, update_by, update_time, del_flag)