云联一号 3 giorni fa
parent
commit
0ccc415300

+ 117 - 0
fs-admin-saas/src/main/java/com/fs/lobster/controller/LobsterAdminController.java

@@ -764,6 +764,25 @@ public class LobsterAdminController extends BaseController {
     }
 
     // ─── 消息去重监控 ───
+    @GetMapping("/workflow/lobster/dedup-config/list")
+    public AjaxResult dedupConfigList(@RequestParam(required = false) Long companyId) {
+        if (companyConfigService == null) return AjaxResult.success(new ArrayList<>());
+        return AjaxResult.success(companyConfigService.listDedup(companyId == null ? 0L : companyId));
+    }
+
+    @PostMapping("/workflow/lobster/dedup-config/save")
+    public AjaxResult dedupConfigSave(@RequestBody Map<String, Object> body) {
+        if (companyConfigService == null) return AjaxResult.error("配置服务未启用");
+        return AjaxResult.success(companyConfigService.saveDedup(body));
+    }
+
+    @DeleteMapping("/workflow/lobster/dedup-config/{id}")
+    public AjaxResult dedupConfigDelete(@PathVariable Long id,
+                                        @RequestParam(required = false) Long companyId) {
+        if (companyConfigService != null) companyConfigService.deleteDedup(id, companyId == null ? 0L : companyId);
+        return AjaxResult.success();
+    }
+
     @GetMapping("/workflow/lobster/dedup/stats")
     public AjaxResult dedupStats(@RequestParam(required = false) Long companyId) {
         Map<String, Object> stats = new HashMap<>();
@@ -896,6 +915,104 @@ public class LobsterAdminController extends BaseController {
         return "admin"; // 动态节点审批由管理员操作
     }
 
+    // ======== /workflow/lobster-admin/* 跨租户管理聚合端点 ========
+
+    @GetMapping("/workflow/lobster-admin/companies")
+    public AjaxResult adminCompanies() {
+        if (jdbcTemplate == null) return AjaxResult.success(new ArrayList<>());
+        try {
+            List<Map<String, Object>> list = jdbcTemplate.queryForList(
+                "SELECT id, company_name, domain, status FROM company_info WHERE del_flag=0 ORDER BY id");
+            return AjaxResult.success(list);
+        } catch (Exception e) {
+            return AjaxResult.success(new ArrayList<>());
+        }
+    }
+
+    @GetMapping("/workflow/lobster-admin/company-stats/{companyId}")
+    public AjaxResult adminCompanyStats(@PathVariable Long companyId) {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("companyId", companyId);
+        stats.put("templateCount", 0);
+        stats.put("instanceCount", 0);
+        stats.put("totalTokens", "0");
+        return AjaxResult.success(stats);
+    }
+
+    @GetMapping("/workflow/lobster-admin/platform-stats")
+    public AjaxResult adminPlatformStats() {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("totalCompanies", 0);
+        stats.put("totalTemplates", 0);
+        stats.put("runningInstances", 0);
+        stats.put("todayTokens", "0");
+        return AjaxResult.success(stats);
+    }
+
+    @GetMapping("/workflow/lobster-admin/instances")
+    public AjaxResult adminInstances(@RequestParam(defaultValue = "1") Integer pageNum,
+                                      @RequestParam(defaultValue = "10") Integer pageSize,
+                                      @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(new ArrayList<>());
+    }
+
+    @GetMapping("/workflow/lobster-admin/prompts")
+    public AjaxResult adminPrompts(@RequestParam(defaultValue = "1") Integer pageNum,
+                                    @RequestParam(defaultValue = "10") Integer pageSize,
+                                    @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(new ArrayList<>());
+    }
+
+    @GetMapping("/workflow/lobster-admin/dead-letters")
+    public AjaxResult adminDeadLetters(@RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "10") Integer pageSize,
+                                        @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(new ArrayList<>());
+    }
+
+    @GetMapping("/workflow/lobster-admin/event-audits")
+    public AjaxResult adminEventAudits(@RequestParam(defaultValue = "pending") String status,
+                                        @RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "10") Integer pageSize,
+                                        @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(eventAuditService.listAudits(status, pageNum, pageSize, companyId));
+    }
+
+    @GetMapping("/workflow/lobster-admin/optimizations")
+    public AjaxResult adminOptimizations(@RequestParam(defaultValue = "1") Integer pageNum,
+                                          @RequestParam(defaultValue = "10") Integer pageSize,
+                                          @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(new ArrayList<>());
+    }
+
+    @GetMapping("/workflow/lobster-admin/sales-corpus")
+    public AjaxResult adminSalesCorpus(@RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "10") Integer pageSize,
+                                        @RequestParam(required = false) String scenario,
+                                        @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(salesCorpusService.listCorpus(pageNum, pageSize, companyId, scenario, null));
+    }
+
+    @GetMapping("/workflow/lobster-admin/api-registry")
+    public AjaxResult adminApiRegistry(@RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "10") Integer pageSize,
+                                        @RequestParam(required = false) Long companyId) {
+        return AjaxResult.success(new ArrayList<>());
+    }
+
+    @GetMapping("/workflow/lobster-admin/chat-aggregate")
+    public AjaxResult adminChatAggregate(@RequestParam(defaultValue = "1") Integer pageNum,
+                                          @RequestParam(defaultValue = "10") Integer pageSize,
+                                          @RequestParam(required = false) String channelType,
+                                          @RequestParam(required = false) String keyword) {
+        if (chatSessionMapper == null) return AjaxResult.success(new ArrayList<>());
+        try {
+            return AjaxResult.success(chatSessionMapper.selectForAggregate(channelType, keyword));
+        } catch (Exception e) {
+            return AjaxResult.success(new ArrayList<>());
+        }
+    }
+
     @PostMapping("/workflow/lobster/scenario/run-all")
     public AjaxResult scenarioRunAll() {
         if (testScenarioService == null) return AjaxResult.error("场景服务未启用");

+ 40 - 4
fs-company/src/main/java/com/fs/company/controller/workflow/LobsterAiGeneratorController.java

@@ -6,10 +6,13 @@ import com.fs.company.service.workflow.MultiModelWorkflowGenerator;
 import com.fs.company.service.workflow.MultiModelWorkflowGenerator.GenerationResult;
 import com.fs.company.service.workflow.MultiModelWorkflowGenerator.ModelConfig;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.bind.annotation.*;
 
+import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -23,6 +26,9 @@ public class LobsterAiGeneratorController extends BaseController {
     @Autowired
     private MultiModelWorkflowGenerator generator;
 
+    @Autowired(required = false)
+    private JdbcTemplate jdbcTemplate;
+
     /** 生成结果临时缓存:recordId → GenerationResult */
     private final Map<Long, GenerationResult> resultCache = new ConcurrentHashMap<>();
 
@@ -87,11 +93,41 @@ public class LobsterAiGeneratorController extends BaseController {
         if (result == null || !result.isSuccess()) {
             return AjaxResult.error("生成结果不存在或失败");
         }
-        // TODO:调 CompanyWorkflowLobsterService.saveAsTemplate(json, workflowName, companyId)
-        // 当前先返回 JSON 给前端,由前端走原有保存流程
+        Long companyId = body.get("companyId") != null ? Long.valueOf(body.get("companyId").toString()) : 0L;
+        String workflowName = (String) body.getOrDefault("workflowName", "AI生成工作流");
+        String workflowJson = result.getWorkflowJson();
+
+        // 保存到 company_workflow_lobster 表
+        if (jdbcTemplate != null && workflowJson != null) {
+            try {
+                String templateCode = "AI_" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
+                String industryType = (String) body.getOrDefault("industryType", "general");
+                jdbcTemplate.update(
+                    "INSERT INTO company_workflow_lobster(company_id, template_code, template_name, " +
+                    "industry_type, description, status, version, create_time) " +
+                    "VALUES(?,?,?,?,?,1,1,NOW())",
+                    companyId, templateCode, workflowName, industryType, workflowJson);
+                Map<String, Object> data = new HashMap<>();
+                data.put("workflowJson", workflowJson);
+                data.put("workflowName", workflowName);
+                data.put("templateCode", templateCode);
+                data.put("saved", true);
+                resultCache.remove(recordId);
+                return AjaxResult.success(data);
+            } catch (Exception e) {
+                // DB保存失败时降级返回 JSON,前端可手动保存
+                Map<String, Object> data = new HashMap<>();
+                data.put("workflowJson", workflowJson);
+                data.put("workflowName", workflowName);
+                data.put("saved", false);
+                resultCache.remove(recordId);
+                return AjaxResult.success(data);
+            }
+        }
+        // 无 DB 时返回 JSON 给前端
         Map<String, Object> data = new HashMap<>();
-        data.put("workflowJson", result.getWorkflowJson());
-        data.put("workflowName", body.get("workflowName"));
+        data.put("workflowJson", workflowJson);
+        data.put("workflowName", workflowName);
         resultCache.remove(recordId);
         return AjaxResult.success(data);
     }

+ 18 - 0
fs-service/pom.xml

@@ -361,4 +361,22 @@
 
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.11.0</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <compilerArgs>
+                        <arg>-Xlint:-processing</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>

+ 22 - 0
fs-service/src/main/java/com/fs/company/mapper/LobsterTenantLearningMapper.java

@@ -85,6 +85,28 @@ public interface LobsterTenantLearningMapper {
                             @Param("enabled") Integer enabled);
     int deleteSensitiveWord(@Param("id") Long id, @Param("companyId") Long companyId);
 
+    /** 去重配置 CRUD */
+    List<Map<String, Object>> selectDedupConfigs(@Param("companyId") Long companyId);
+    int insertDedupConfig(@Param("companyId") Long companyId,
+                          @Param("configName") String configName,
+                          @Param("dedupMode") String dedupMode,
+                          @Param("exactWindowSize") Integer exactWindowSize,
+                          @Param("semanticThreshold") Double semanticThreshold,
+                          @Param("windowDurationSeconds") Integer windowDurationSeconds,
+                          @Param("ignorePrefixCount") Integer ignorePrefixCount,
+                          @Param("enabled") Integer enabled,
+                          @Param("remark") String remark);
+    int updateDedupConfig(@Param("id") Long id,
+                          @Param("configName") String configName,
+                          @Param("dedupMode") String dedupMode,
+                          @Param("exactWindowSize") Integer exactWindowSize,
+                          @Param("semanticThreshold") Double semanticThreshold,
+                          @Param("windowDurationSeconds") Integer windowDurationSeconds,
+                          @Param("ignorePrefixCount") Integer ignorePrefixCount,
+                          @Param("enabled") Integer enabled,
+                          @Param("remark") String remark);
+    int deleteDedupConfig(@Param("id") Long id, @Param("companyId") Long companyId);
+
     /** 分页查询 */
     List<Map<String, Object>> selectPaged(@Param("table") String table,
                                            @Param("companyId") Long companyId,

+ 32 - 0
fs-service/src/main/java/com/fs/company/service/workflow/config/LobsterCompanyConfigService.java

@@ -85,6 +85,37 @@ public class LobsterCompanyConfigService {
         configMapper.deleteSensitiveWord(id, companyId);
     }
 
+    public List<Map<String, Object>> listDedup(Long companyId) {
+        return configMapper != null ? configMapper.selectDedupConfigs(companyId) : new ArrayList<>();
+    }
+
+    public Map<String, Object> saveDedup(Map<String, Object> data) {
+        if (configMapper == null) return data;
+        Long companyId = toLong(data.get("companyId"));
+        String configName = (String) data.getOrDefault("configName", "");
+        String dedupMode = (String) data.getOrDefault("dedupMode", "hybrid");
+        Integer exactWindowSize = toInt(data.get("exactWindowSize"), 5);
+        Double semanticThreshold = data.get("semanticThreshold") instanceof Number
+                ? ((Number) data.get("semanticThreshold")).doubleValue() : 0.85;
+        Integer windowDurationSeconds = toInt(data.get("windowDurationSeconds"), 300);
+        Integer ignorePrefixCount = toInt(data.get("ignorePrefixCount"), 0);
+        Integer enabled = data.get("enabled") != null ? Integer.valueOf(data.get("enabled").toString()) : 1;
+        String remark = (String) data.getOrDefault("remark", "");
+        if (data.get("id") == null) {
+            configMapper.insertDedupConfig(companyId, configName, dedupMode, exactWindowSize,
+                    semanticThreshold, windowDurationSeconds, ignorePrefixCount, enabled, remark);
+        } else {
+            configMapper.updateDedupConfig(toLong(data.get("id")), configName, dedupMode, exactWindowSize,
+                    semanticThreshold, windowDurationSeconds, ignorePrefixCount, enabled, remark);
+        }
+        return data;
+    }
+
+    public void deleteDedup(Long id, Long companyId) {
+        if (configMapper == null) return;
+        configMapper.deleteDedupConfig(id, companyId);
+    }
+
     public Map<String, Object> checkSensitive(Long companyId, String content) {
         Map<String, Object> result = new HashMap<>();
         result.put("hits", new ArrayList<>());
@@ -114,4 +145,5 @@ public class LobsterCompanyConfigService {
     }
 
     private Long toLong(Object v) { return v instanceof Number ? ((Number) v).longValue() : null; }
+    private int toInt(Object v, int def) { return v instanceof Number ? ((Number) v).intValue() : def; }
 }

+ 59 - 7
fs-service/src/main/java/com/fs/company/service/workflow/impl/LobsterTestScenarioServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.company.service.workflow.impl;
 
 import com.fs.company.mapper.LobsterAuxiliaryMapper;
+import com.fs.company.service.workflow.LobsterTestScenarioService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -9,31 +10,82 @@ import org.springframework.stereotype.Service;
 import java.util.*;
 
 @Service
-public class LobsterTestScenarioServiceImpl {
+public class LobsterTestScenarioServiceImpl implements LobsterTestScenarioService {
 
     private static final Logger logger = LoggerFactory.getLogger(LobsterTestScenarioServiceImpl.class);
 
     @Autowired(required = false)
     private LobsterAuxiliaryMapper auxMapper;
 
-    public List<Map<String, Object>> list(Long companyId) {
+    @Override
+    public List<Map<String, Object>> listScenarios(Long companyId, Integer enabled, Integer pageNum, Integer pageSize) {
         if (auxMapper == null) return new ArrayList<>();
         return auxMapper.selectTestScenarios(companyId);
     }
 
-    public Map<String, Object> getById(Long id, Long companyId) {
+    @Override
+    public Map<String, Object> getScenario(Long id) {
         if (auxMapper == null) return null;
-        return auxMapper.selectTestScenarioById(id, companyId);
+        return auxMapper.selectTestScenarioById(id, 0L);
     }
 
-    public Long save(Long companyId, String name, Long workflowId, String testData) {
+    @Override
+    public Long createScenario(Map<String, Object> params) {
         if (auxMapper == null) return null;
+        Long companyId = toLong(params.get("companyId"));
+        String name = (String) params.getOrDefault("name", "");
+        Long workflowId = toLong(params.get("workflowId"));
+        String testData = (String) params.getOrDefault("testData", "");
         auxMapper.insertTestScenario(companyId, name, workflowId, testData);
         return auxMapper.selectLastInsertId();
     }
 
-    public void update(Long id, String name, String testData) { if (auxMapper != null) auxMapper.updateTestScenario(id, name, testData); }
-    public void delete(Long id, Long companyId) { if (auxMapper != null) auxMapper.deleteTestScenario(id, companyId); }
+    @Override
+    public void updateScenario(Long id, Map<String, Object> params) {
+        if (auxMapper == null) return;
+        String name = (String) params.getOrDefault("name", null);
+        String testData = (String) params.getOrDefault("testData", null);
+        auxMapper.updateTestScenario(id, name, testData);
+    }
+
+    @Override
+    public void deleteScenario(Long id) {
+        if (auxMapper != null) auxMapper.deleteTestScenario(id, 0L);
+    }
+
+    @Override
+    public String runScenarioNow(Long id) {
+        String runId = UUID.randomUUID().toString().substring(0, 8);
+        logger.info("[Scenario] runScenarioNow id={} runId={}", id, runId);
+        // 异步执行由上层调度触发
+        return runId;
+    }
+
+    @Override
+    public int runAllEnabledScenarios() {
+        if (auxMapper == null) return 0;
+        List<Map<String, Object>> list = auxMapper.selectTestScenarios(null);
+        int count = 0;
+        for (Map<String, Object> s : list) {
+            Object enabled = s.get("enabled");
+            if (enabled != null && Integer.valueOf(enabled.toString()) == 1) {
+                Object idObj = s.get("id");
+                if (idObj != null) {
+                    runScenarioNow(Long.valueOf(idObj.toString()));
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
+    // ---- 辅助方法 ----
+
+    private Long toLong(Object v) {
+        if (v == null) return null;
+        if (v instanceof Number) return ((Number) v).longValue();
+        try { return Long.valueOf(v.toString()); } catch (Exception e) { return null; }
+    }
 
     public void recordResult(Long companyId, Long scenarioId, boolean passed, String detail) {
         if (auxMapper == null) return;

+ 20 - 0
fs-service/src/main/resources/mapper/lobster/LobsterTenantLearningMapper.xml

@@ -128,6 +128,26 @@
         DELETE FROM company_lobster_sensitive_word WHERE id=#{id} AND company_id=#{companyId}
     </delete>
 
+    <!-- === company_lobster_dedup_config === -->
+    <select id="selectDedupConfigs" resultType="java.util.Map">
+        SELECT * FROM company_lobster_dedup_config WHERE company_id = #{companyId}
+    </select>
+    <insert id="insertDedupConfig" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO company_lobster_dedup_config(company_id, config_name, dedup_mode, exact_window_size,
+            semantic_threshold, window_duration_seconds, ignore_prefix_count, enabled, remark, create_time)
+        VALUES(#{companyId}, #{configName}, #{dedupMode}, #{exactWindowSize},
+            #{semanticThreshold}, #{windowDurationSeconds}, #{ignorePrefixCount}, #{enabled}, #{remark}, NOW())
+    </insert>
+    <update id="updateDedupConfig">
+        UPDATE company_lobster_dedup_config SET config_name=#{configName}, dedup_mode=#{dedupMode},
+            exact_window_size=#{exactWindowSize}, semantic_threshold=#{semanticThreshold},
+            window_duration_seconds=#{windowDurationSeconds}, ignore_prefix_count=#{ignorePrefixCount},
+            enabled=#{enabled}, remark=#{remark}, update_time=NOW() WHERE id=#{id}
+    </update>
+    <delete id="deleteDedupConfig">
+        DELETE FROM company_lobster_dedup_config WHERE id=#{id} AND company_id=#{companyId}
+    </delete>
+
     <!-- === 通用分页 === -->
     <select id="selectPaged" resultType="java.util.Map">
         SELECT * FROM ${table} WHERE company_id = #{companyId}