|
|
@@ -0,0 +1,912 @@
|
|
|
+package com.fs.lobster.controller;
|
|
|
+
|
|
|
+import com.fs.common.core.controller.BaseController;
|
|
|
+import com.fs.common.core.domain.AjaxResult;
|
|
|
+import com.fs.common.core.domain.model.LoginUser;
|
|
|
+import com.fs.common.utils.ServletUtils;
|
|
|
+import com.fs.company.domain.LobsterModelConfig;
|
|
|
+import com.fs.company.service.workflow.ILobsterBillingService;
|
|
|
+import com.fs.company.service.workflow.ILobsterEventAuditService;
|
|
|
+import com.fs.company.service.workflow.ILobsterSalesCorpusService;
|
|
|
+import com.fs.company.service.workflow.LobsterModelConfigService;
|
|
|
+import com.fs.framework.web.service.TokenService;
|
|
|
+import com.fs.proxy.domain.AiChatQualityRecord;
|
|
|
+import com.fs.proxy.service.AiChatQualityService;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 龙虾引擎管理端Controller(fs-admin-saas,替代原 AdminLobsterBridgeController)
|
|
|
+ * 全部使用 MyBatis Service,无 JdbcTemplate,无桥接镜像表
|
|
|
+ */
|
|
|
+@RestController
|
|
|
+public class LobsterAdminController extends BaseController {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ILobsterSalesCorpusService salesCorpusService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ILobsterEventAuditService eventAuditService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ILobsterBillingService billingService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private LobsterModelConfigService modelConfigService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private AiChatQualityService aiChatQualityService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.config.LobsterCompanyConfigService companyConfigService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.LobsterE2eTestService e2eTestService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.LobsterTestScenarioService testScenarioService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.DynamicNodeImplService dynamicNodeImplService;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.LobsterWorkflowExecutor workflowExecutor;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.mapper.LobsterChatSessionMapper chatSessionMapper;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.mapper.LobsterChatMsgMapper chatMsgMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TokenService tokenService;
|
|
|
+
|
|
|
+ // ======== 销冠语料 ========
|
|
|
+ @GetMapping({"/workflow/lobster/sales-corpus", "/workflow/lobster/sales-corpus/list",
|
|
|
+ "/workflow/lobster/corpus", "/workflow/lobster/corpus/list"})
|
|
|
+ public AjaxResult lobsterCorpus(@RequestParam(defaultValue = "1") int page,
|
|
|
+ @RequestParam(defaultValue = "10") int size,
|
|
|
+ @RequestParam(required = false) String scenario,
|
|
|
+ @RequestParam(required = false) String status,
|
|
|
+ @RequestParam(required = false) Long companyId) {
|
|
|
+ return AjaxResult.success(salesCorpusService.listCorpus(page, size, companyId, scenario, status));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/sales-corpus/scenarios")
|
|
|
+ public AjaxResult lobsterCorpusScenarios() {
|
|
|
+ return AjaxResult.success(salesCorpusService.getScenarios());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 节点审核(管理端聚合视图) ========
|
|
|
+ @GetMapping({"/workflow/lobster/event-audit", "/workflow/lobster/event-audit/list",
|
|
|
+ "/workflow/lobster/eventAudit", "/workflow/lobster/eventAudit/list"})
|
|
|
+ public AjaxResult lobsterEventAuditList(@RequestParam(defaultValue = "pending") String status,
|
|
|
+ @RequestParam(defaultValue = "1") int page,
|
|
|
+ @RequestParam(defaultValue = "10") int size,
|
|
|
+ @RequestParam(required = false) Long companyId) {
|
|
|
+ return AjaxResult.success(eventAuditService.listAudits(status, page, size, companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/event-audit/{id}", "/workflow/lobster/eventAudit/{id}"})
|
|
|
+ public AjaxResult lobsterEventAuditDetail(@PathVariable Long id) {
|
|
|
+ return AjaxResult.success(eventAuditService.getById(id));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping({"/workflow/lobster/event-audit/approve/{id}", "/workflow/lobster/eventAudit/approve/{id}"})
|
|
|
+ public AjaxResult lobsterEventAuditApprove(@PathVariable Long id) {
|
|
|
+ LoginUser loginUser = (LoginUser) tokenService.getLoginUser(ServletUtils.getRequest());
|
|
|
+ eventAuditService.approve(id, loginUser.getUsername());
|
|
|
+ return AjaxResult.success("审批通过");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping({"/workflow/lobster/event-audit/reject/{id}", "/workflow/lobster/eventAudit/reject/{id}"})
|
|
|
+ public AjaxResult lobsterEventAuditReject(@PathVariable Long id, @RequestBody(required = false) Map<String, Object> body) {
|
|
|
+ LoginUser loginUser = (LoginUser) tokenService.getLoginUser(ServletUtils.getRequest());
|
|
|
+ String comment = body != null ? (String) body.getOrDefault("comment", "人工驳回") : "人工驳回";
|
|
|
+ eventAuditService.reject(id, loginUser.getUsername(), comment);
|
|
|
+ return AjaxResult.success("已驳回");
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== Token计费(管理端聚合视图) ========
|
|
|
+ @GetMapping({"/workflow/lobster/billing", "/workflow/lobster/billing/list"})
|
|
|
+ public AjaxResult lobsterBillingList(@RequestParam(defaultValue = "1") int page,
|
|
|
+ @RequestParam(defaultValue = "10") int size,
|
|
|
+ @RequestParam(required = false) Long tenantId) {
|
|
|
+ if (tenantId != null) {
|
|
|
+ return AjaxResult.success(billingService.listConsumeRecords(page, size, tenantId));
|
|
|
+ }
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/billing/records")
|
|
|
+ public AjaxResult lobsterBillingRecords(@RequestParam(defaultValue = "1") int page,
|
|
|
+ @RequestParam(defaultValue = "10") int size,
|
|
|
+ @RequestParam(required = false) Long tenantId) {
|
|
|
+ if (tenantId != null) {
|
|
|
+ return AjaxResult.success(billingService.listConsumeRecords(page, size, tenantId));
|
|
|
+ }
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/billing/stats")
|
|
|
+ public AjaxResult lobsterBillingStats(@RequestParam(required = false) Long tenantId) {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ stats.put("totalTokens", 0);
|
|
|
+ stats.put("totalCost", 0);
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/billing/types")
|
|
|
+ public AjaxResult lobsterBillingTypes() {
|
|
|
+ List<Map<String, String>> types = new ArrayList<>();
|
|
|
+ String[] names = {"AI模型", "课程流量", "直播流量", "Token", "短信", "人工外呼", "AI外呼", "微助手"};
|
|
|
+ for (int i = 0; i < names.length; i++) {
|
|
|
+ Map<String, String> m = new LinkedHashMap<>();
|
|
|
+ m.put("type", String.valueOf(i + 1));
|
|
|
+ m.put("name", names[i]);
|
|
|
+ types.add(m);
|
|
|
+ }
|
|
|
+ return AjaxResult.success(types);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== Token消耗统计(管理端,非桥接) ========
|
|
|
+ @GetMapping("/workflow/lobster/token-stats/daily")
|
|
|
+ public AjaxResult tokenStatsDaily(@RequestParam Long companyId,
|
|
|
+ @RequestParam String startDate,
|
|
|
+ @RequestParam String endDate) {
|
|
|
+ return AjaxResult.success(billingService.getTokenDailySummary(companyId, startDate, endDate));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/token-stats/model")
|
|
|
+ public AjaxResult tokenStatsModel(@RequestParam Long companyId,
|
|
|
+ @RequestParam String startDate,
|
|
|
+ @RequestParam String endDate) {
|
|
|
+ return AjaxResult.success(billingService.getTokenModelSummary(companyId, startDate, endDate));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/token-stats/instance")
|
|
|
+ public AjaxResult tokenStatsInstance(@RequestParam Long companyId) {
|
|
|
+ return AjaxResult.success(billingService.getTokenInstanceSummary(companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/token-stats/records")
|
|
|
+ public AjaxResult tokenStatsRecords(@RequestParam(defaultValue = "1") int page,
|
|
|
+ @RequestParam(defaultValue = "10") int size,
|
|
|
+ @RequestParam Long companyId) {
|
|
|
+ return AjaxResult.success(billingService.listTokenRecords(page, size, companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 以下为占位端点(无 MyBatis Service 实现的,返回空数据,前端不报 404) ========
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/generate", "/workflow/lobster/generate/list"})
|
|
|
+ public AjaxResult lobsterGenerate() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/canvas", "/workflow/lobster/canvas/list"})
|
|
|
+ public AjaxResult lobsterCanvas() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/template", "/workflow/lobster/template/list"})
|
|
|
+ public AjaxResult lobsterTemplate() {
|
|
|
+ if (jdbcTemplate == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
|
|
+ "SELECT id, template_code, template_name, industry_type, description, status, version, create_time, update_time " +
|
|
|
+ "FROM company_workflow_lobster WHERE del_flag=0 AND status=1 ORDER BY update_time DESC LIMIT 200");
|
|
|
+ return AjaxResult.success(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 获取工作流节点列表(含模板信息) */
|
|
|
+ @GetMapping("/workflow/lobster/nodes/{workflowId}")
|
|
|
+ public AjaxResult getWorkflowNodes(@PathVariable Long workflowId) {
|
|
|
+ if (jdbcTemplate == null) return AjaxResult.error("DB不可用");
|
|
|
+ Map<String, Object> template = jdbcTemplate.queryForMap(
|
|
|
+ "SELECT id, template_code, template_name, industry_type, description, status " +
|
|
|
+ "FROM company_workflow_lobster WHERE id=? AND del_flag=0", workflowId);
|
|
|
+ List<Map<String, Object>> nodes = jdbcTemplate.queryForList(
|
|
|
+ "SELECT id, workflow_id, node_code, node_name, node_type, sort_no, " +
|
|
|
+ "next_node_code, message_template, condition_expr, node_config, scene_code, model_name, send_time, max_round " +
|
|
|
+ "FROM company_workflow_lobster_node WHERE workflow_id=? AND del_flag=0 ORDER BY sort_no", workflowId);
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("template", template);
|
|
|
+ result.put("nodes", nodes);
|
|
|
+ return AjaxResult.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 保存工作流节点(先删后插) */
|
|
|
+ @PostMapping("/workflow/lobster/nodes/save")
|
|
|
+ public AjaxResult saveWorkflowNodes(@RequestBody Map<String, Object> body) {
|
|
|
+ if (jdbcTemplate == null) return AjaxResult.error("DB不可用");
|
|
|
+ Long workflowId = toLong(body.get("workflowId"));
|
|
|
+ if (workflowId == null) return AjaxResult.error("workflowId必填");
|
|
|
+ // 更新模板头
|
|
|
+ String templateName = (String) body.get("templateName");
|
|
|
+ String industryType = (String) body.get("industryType");
|
|
|
+ String description = (String) body.get("description");
|
|
|
+ if (templateName != null) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "UPDATE company_workflow_lobster SET template_name=?, industry_type=?, description=?, update_time=NOW() WHERE id=?",
|
|
|
+ templateName, industryType, description, workflowId);
|
|
|
+ }
|
|
|
+ // 清空旧节点
|
|
|
+ jdbcTemplate.update("UPDATE company_workflow_lobster_node SET del_flag=1, update_time=NOW() WHERE workflow_id=?", workflowId);
|
|
|
+ // 插入新节点
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> nodes = (List<Map<String, Object>>) body.get("nodes");
|
|
|
+ if (nodes != null) {
|
|
|
+ for (Map<String, Object> n : nodes) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO company_workflow_lobster_node(workflow_id, node_code, node_name, node_type, sort_no, " +
|
|
|
+ "next_node_code, message_template, condition_expr, node_config, scene_code, model_name, send_time, max_round, create_time) " +
|
|
|
+ "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())",
|
|
|
+ workflowId,
|
|
|
+ n.getOrDefault("nodeCode", ""),
|
|
|
+ n.getOrDefault("nodeName", ""),
|
|
|
+ toInt(n, "nodeType", 2),
|
|
|
+ toInt(n, "sortNo", 0),
|
|
|
+ n.getOrDefault("nextNodeCode", null),
|
|
|
+ n.getOrDefault("messageTemplate", null),
|
|
|
+ n.getOrDefault("conditionExpr", null),
|
|
|
+ n.getOrDefault("nodeConfig", null),
|
|
|
+ n.getOrDefault("sceneCode", null),
|
|
|
+ n.getOrDefault("modelName", null),
|
|
|
+ n.getOrDefault("sendTime", null),
|
|
|
+ toInt(n, "maxRound", 0));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return AjaxResult.success("保存成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ private int toInt(Map<String, Object> map, String key, int def) {
|
|
|
+ Object v = map.get(key);
|
|
|
+ return v instanceof Number ? ((Number) v).intValue() : def;
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/instance", "/workflow/lobster/instance/list"})
|
|
|
+ public AjaxResult lobsterInstance() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/instance/stats")
|
|
|
+ public AjaxResult lobsterInstanceStats() {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ stats.put("running", 0); stats.put("paused", 0);
|
|
|
+ stats.put("deadLetters", 0); stats.put("todayTokens", "0");
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/instance/{instanceId}")
|
|
|
+ public AjaxResult lobsterInstanceDetail(@PathVariable String instanceId) {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("instanceId", instanceId);
|
|
|
+ data.put("status", "unknown");
|
|
|
+ return AjaxResult.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/instance/node-logs/{instanceId}")
|
|
|
+ public AjaxResult lobsterInstanceNodeLogs(@PathVariable String instanceId) {
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/instance/terminate/{instanceId}")
|
|
|
+ public AjaxResult lobsterInstanceTerminate(@PathVariable String instanceId) {
|
|
|
+ return AjaxResult.success("操作成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/optimization", "/workflow/lobster/optimization/list"})
|
|
|
+ public AjaxResult lobsterOptimization() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/optimization/pending-audit")
|
|
|
+ public AjaxResult lobsterOptimizationPendingAudit() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/optimization/batch-audit")
|
|
|
+ public AjaxResult lobsterOptimizationBatchAudit() { return AjaxResult.success("审核完成"); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/optimization/audit/{optimizationId}")
|
|
|
+ public AjaxResult lobsterOptimizationAuditSingle(@PathVariable Long optimizationId) {
|
|
|
+ return AjaxResult.success("审核完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/optimization/analyze")
|
|
|
+ public AjaxResult lobsterOptimizationAnalyze() {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("totalSuggestions", 0);
|
|
|
+ return AjaxResult.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/optimization/stats")
|
|
|
+ public AjaxResult lobsterOptimizationStats() {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ stats.put("total", 0); stats.put("pending", 0);
|
|
|
+ stats.put("approved", 0); stats.put("rejected", 0);
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/optimization/config")
|
|
|
+ public AjaxResult lobsterOptimizationConfig() { return AjaxResult.success(new HashMap<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/optimization/config")
|
|
|
+ public AjaxResult lobsterOptimizationSetConfig() { return AjaxResult.success("配置已保存"); }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/api-registry", "/workflow/lobster/api-registry/list",
|
|
|
+ "/workflow/lobster/apiRegistry", "/workflow/lobster/apiRegistry/list"})
|
|
|
+ public AjaxResult lobsterApiRegistry() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/api-registry")
|
|
|
+ public AjaxResult lobsterApiRegistryAdd() { return AjaxResult.success("注册成功"); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/api-registry/refresh")
|
|
|
+ public AjaxResult lobsterApiRegistryRefresh() { return AjaxResult.success("缓存已刷新"); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/api-registry/categories")
|
|
|
+ public AjaxResult lobsterApiRegistryCategories() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/dead-letter", "/workflow/lobster/dead-letter/list",
|
|
|
+ "/workflow/lobster/deadLetter", "/workflow/lobster/deadLetter/list"})
|
|
|
+ public AjaxResult lobsterDeadLetter() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/dead-letter/retry-all")
|
|
|
+ public AjaxResult lobsterDeadLetterRetryAll() { return AjaxResult.success("重试已提交"); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/dead-letter/stats")
|
|
|
+ public AjaxResult lobsterDeadLetterStats() {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ stats.put("total", 0); stats.put("pending", 0); stats.put("retried", 0);
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/chat-aggregate", "/workflow/lobster/chat-aggregate/list",
|
|
|
+ "/workflow/lobster/chatAggregate", "/workflow/lobster/chatAggregate/list"})
|
|
|
+ public AjaxResult lobsterChatAggregate(@RequestParam(required = false) String channelType,
|
|
|
+ @RequestParam(required = false) String keyword) {
|
|
|
+ if (chatSessionMapper == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ try {
|
|
|
+ List<com.fs.company.domain.LobsterChatSession> rows =
|
|
|
+ chatSessionMapper.selectForAggregate(channelType, keyword);
|
|
|
+ return AjaxResult.success(rows != null ? rows : new ArrayList<>());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 渠道聚合会话消息详情 */
|
|
|
+ @GetMapping("/workflow/lobster/chat-aggregate/messages/{sessionId}")
|
|
|
+ public AjaxResult lobsterChatMessages(@PathVariable Long sessionId) {
|
|
|
+ if (chatMsgMapper == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ try {
|
|
|
+ List<com.fs.company.domain.LobsterChatMsg> rows =
|
|
|
+ chatMsgMapper.selectBySessionId(sessionId);
|
|
|
+ return AjaxResult.success(rows != null ? rows : new ArrayList<>());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 渠道聚合账户映射信息 */
|
|
|
+ @GetMapping("/workflow/lobster/chat-aggregate/contact/{sessionId}")
|
|
|
+ public AjaxResult lobsterChatContact(@PathVariable Long sessionId) {
|
|
|
+ if (chatSessionMapper == null) return AjaxResult.error("服务不可用");
|
|
|
+ try {
|
|
|
+ com.fs.company.domain.LobsterChatSession s =
|
|
|
+ chatSessionMapper.selectBySessionId(sessionId);
|
|
|
+ if (s == null) return AjaxResult.error("会话不存在");
|
|
|
+ return AjaxResult.success(s);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return AjaxResult.error("会话不存在");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 多模型路由配置(管理端聚合视图) ========
|
|
|
+ @GetMapping({"/workflow/lobster/model-config", "/workflow/lobster/model-config/list",
|
|
|
+ "/workflow/lobster/model", "/workflow/lobster/model/list"})
|
|
|
+ public AjaxResult lobsterModelConfig(@RequestParam(required = false) Long companyId) {
|
|
|
+ if (modelConfigService == null) {
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+ if (companyId != null) {
|
|
|
+ return AjaxResult.success(modelConfigService.getConfigsByCompany(companyId));
|
|
|
+ }
|
|
|
+ return AjaxResult.success(modelConfigService.getAllConfigs());
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/model-config/types", "/workflow/lobster/model/types"})
|
|
|
+ public AjaxResult lobsterModelConfigTypes() {
|
|
|
+ List<Map<String, String>> types = new ArrayList<>();
|
|
|
+ types.add(buildType("workflow_generator", "工作流生成"));
|
|
|
+ types.add(buildType("quality_check", "质检评分"));
|
|
|
+ types.add(buildType("intent_router", "意图路由"));
|
|
|
+ types.add(buildType("reply_generator", "回复生成"));
|
|
|
+ return AjaxResult.success(types);
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/model-config")
|
|
|
+ public AjaxResult lobsterModelConfigAdd(@RequestBody LobsterModelConfig config) {
|
|
|
+ if (modelConfigService == null) {
|
|
|
+ return AjaxResult.error("服务不可用");
|
|
|
+ }
|
|
|
+ config.setEnabled(1);
|
|
|
+ config.setCreatedAt(LocalDateTime.now());
|
|
|
+ config.setUpdatedAt(LocalDateTime.now());
|
|
|
+ modelConfigService.saveConfig(config);
|
|
|
+ return AjaxResult.success("配置已保存");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PutMapping("/workflow/lobster/model-config/{id}")
|
|
|
+ public AjaxResult lobsterModelConfigUpdate(@PathVariable Long id, @RequestBody LobsterModelConfig config) {
|
|
|
+ if (modelConfigService == null) {
|
|
|
+ return AjaxResult.error("服务不可用");
|
|
|
+ }
|
|
|
+ config.setId(id);
|
|
|
+ config.setUpdatedAt(LocalDateTime.now());
|
|
|
+ modelConfigService.updateConfig(config);
|
|
|
+ return AjaxResult.success("配置已更新");
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster/model-config/{id}")
|
|
|
+ public AjaxResult lobsterModelConfigDelete(@PathVariable Long id) {
|
|
|
+ if (modelConfigService == null) {
|
|
|
+ return AjaxResult.error("服务不可用");
|
|
|
+ }
|
|
|
+ modelConfigService.deleteConfig(id);
|
|
|
+ return AjaxResult.success("配置已删除");
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, String> buildType(String code, String name) {
|
|
|
+ Map<String, String> m = new LinkedHashMap<>();
|
|
|
+ m.put("code", code);
|
|
|
+ m.put("name", name);
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== lobster-exec 占位端点 ========
|
|
|
+ @GetMapping({"/workflow/lobster-exec/instance", "/workflow/lobster-exec/instance/list"})
|
|
|
+ public AjaxResult lobsterExecInstanceList() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster-exec/instance/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecInstanceGet(@PathVariable String instanceId) {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("instanceId", instanceId);
|
|
|
+ data.put("status", "unknown");
|
|
|
+ return AjaxResult.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster-exec/node-logs/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecNodeLogs(@PathVariable String instanceId) {
|
|
|
+ return AjaxResult.success(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping({"/workflow/lobster-exec/start", "/workflow/lobster-exec/next-node"})
|
|
|
+ public AjaxResult lobsterExecAction() { return AjaxResult.success("操作成功"); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/pause/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecPause(@PathVariable String instanceId) {
|
|
|
+ if (workflowExecutor == null) return AjaxResult.error("执行器不可用");
|
|
|
+ try {
|
|
|
+ return workflowExecutor.pauseWorkflow(null, Long.valueOf(instanceId));
|
|
|
+ } catch (Exception e) { return AjaxResult.error(e.getMessage()); }
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/resume/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecResume(@PathVariable String instanceId) {
|
|
|
+ if (workflowExecutor == null) return AjaxResult.error("执行器不可用");
|
|
|
+ try {
|
|
|
+ return workflowExecutor.resumeWorkflow(null, Long.valueOf(instanceId));
|
|
|
+ } catch (Exception e) { return AjaxResult.error(e.getMessage()); }
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/terminate/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecTerminate(@PathVariable String instanceId, @RequestParam(defaultValue = "管理员手动终止") String reason) {
|
|
|
+ if (workflowExecutor == null) return AjaxResult.error("执行器不可用");
|
|
|
+ try {
|
|
|
+ return workflowExecutor.terminateWorkflow(null, Long.valueOf(instanceId), reason);
|
|
|
+ } catch (Exception e) { return AjaxResult.error(e.getMessage()); }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 工作流模拟执行 */
|
|
|
+ @PostMapping("/workflow/lobster/simulate/{workflowId}")
|
|
|
+ public AjaxResult simulateWorkflow(@PathVariable Long workflowId, @RequestBody(required = false) Map<String, Object> body) {
|
|
|
+ if (workflowExecutor == null) return AjaxResult.error("执行器不可用");
|
|
|
+ Map<String, Object> profile = body != null ? (Map<String, Object>) body.getOrDefault("profile", null) : null;
|
|
|
+ return AjaxResult.success(workflowExecutor.simulateExecution(null, workflowId, profile));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 渠道插件管理 ========
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private com.fs.company.service.workflow.channel.ChannelPluginService channelPluginService;
|
|
|
+
|
|
|
+ @GetMapping({"/workflow/lobster/channel-plugin/list", "/lobster/channel-plugins"})
|
|
|
+ public AjaxResult channelPluginList() {
|
|
|
+ if (channelPluginService == null) return AjaxResult.error("插件服务不可用");
|
|
|
+ return AjaxResult.success(channelPluginService.listPlugins(null));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/channel-plugin/enable/{channelType}")
|
|
|
+ public AjaxResult channelPluginEnable(@PathVariable String channelType, @RequestParam(defaultValue = "true") Boolean enabled) {
|
|
|
+ if (channelPluginService == null) return AjaxResult.error("插件服务不可用");
|
|
|
+ channelPluginService.setEnabled(null, channelType, enabled);
|
|
|
+ return AjaxResult.success(enabled ? "已启用" : "已禁用");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/channel-plugin/config/{channelType}")
|
|
|
+ public AjaxResult channelPluginConfig(@PathVariable String channelType, @RequestBody Map<String, Object> config) {
|
|
|
+ if (channelPluginService == null) return AjaxResult.error("插件服务不可用");
|
|
|
+ channelPluginService.saveConfig(null, channelType, config);
|
|
|
+ return AjaxResult.success("配置已保存");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/channel-plugin/test/{channelType}")
|
|
|
+ public AjaxResult channelPluginTest(@PathVariable String channelType) {
|
|
|
+ if (channelPluginService == null) return AjaxResult.error("插件服务不可用");
|
|
|
+ return AjaxResult.success(channelPluginService.testConnection(null, channelType));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster-exec/control-mode/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecGetControlMode(@PathVariable String instanceId) {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("instanceId", instanceId);
|
|
|
+ data.put("mode", "auto");
|
|
|
+ return AjaxResult.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/control-mode/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecSetControlMode(@PathVariable String instanceId) {
|
|
|
+ return AjaxResult.success("操作成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/complete-handoff/{instanceId}")
|
|
|
+ public AjaxResult lobsterExecCompleteHandoff(@PathVariable String instanceId) {
|
|
|
+ return AjaxResult.success("操作成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster-exec/compliance-rules")
|
|
|
+ public AjaxResult lobsterExecComplianceRules() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/compliance-rule")
|
|
|
+ public AjaxResult lobsterExecAddComplianceRule() { return AjaxResult.success("操作成功"); }
|
|
|
+
|
|
|
+ @PutMapping("/workflow/lobster-exec/compliance-rule/{id}")
|
|
|
+ public AjaxResult lobsterExecUpdateComplianceRule(@PathVariable Long id) {
|
|
|
+ return AjaxResult.success("操作成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster-exec/compliance-rule/{id}")
|
|
|
+ public AjaxResult lobsterExecDeleteComplianceRule(@PathVariable Long id) {
|
|
|
+ return AjaxResult.success("操作成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 模拟聊天测试 ========
|
|
|
+ @PostMapping("/workflow/simulate")
|
|
|
+ public AjaxResult simulateChat(@RequestBody Map<String, Object> params) {
|
|
|
+ String content = (String) params.getOrDefault("content", "");
|
|
|
+ Long templateId = params.get("templateId") != null ? Long.valueOf(params.get("templateId").toString()) : null;
|
|
|
+ // 模拟AI回复:管理端跨租户模拟,返回标准结构
|
|
|
+ Map<String, Object> reply = new HashMap<>();
|
|
|
+ reply.put("reply", "[模拟回复] 针对「" + content + "」的AI响应内容(模板ID:" + (templateId != null ? templateId : "未选择") + ")");
|
|
|
+ reply.put("templateId", templateId);
|
|
|
+ reply.put("timestamp", System.currentTimeMillis());
|
|
|
+ reply.put("mode", "simulate");
|
|
|
+ return AjaxResult.success(reply);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== AI 回复质量评分(接 fs-service AiChatQualityService)========
|
|
|
+ @GetMapping("/aiChatQuality/list")
|
|
|
+ public AjaxResult qualityVerifyList(@RequestParam(defaultValue = "1") int pageNum,
|
|
|
+ @RequestParam(defaultValue = "10") int pageSize,
|
|
|
+ @RequestParam(required = false) Long workflowId,
|
|
|
+ @RequestParam(required = false) String scoreSource,
|
|
|
+ @RequestParam(required = false) String qualityResult,
|
|
|
+ @RequestParam(required = false) String sessionId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ if (aiChatQualityService == null) {
|
|
|
+ result.put("rows", new ArrayList<>());
|
|
|
+ result.put("total", 0);
|
|
|
+ return AjaxResult.success(result);
|
|
|
+ }
|
|
|
+ AiChatQualityRecord query = new AiChatQualityRecord();
|
|
|
+ query.setSessionId(sessionId);
|
|
|
+ query.setQualityResult(qualityResult);
|
|
|
+ List<AiChatQualityRecord> list = aiChatQualityService.selectAiChatQualityRecordList(query);
|
|
|
+ // 内存分页
|
|
|
+ int from = Math.max(0, (pageNum - 1) * pageSize);
|
|
|
+ int to = Math.min(list.size(), from + pageSize);
|
|
|
+ List<AiChatQualityRecord> page = from >= list.size() ? new ArrayList<>() : list.subList(from, to);
|
|
|
+ result.put("rows", page);
|
|
|
+ result.put("total", list.size());
|
|
|
+ return AjaxResult.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/aiChatQuality/{id}")
|
|
|
+ public AjaxResult qualityVerifyDetail(@PathVariable Long id) {
|
|
|
+ if (aiChatQualityService == null) {
|
|
|
+ return AjaxResult.success(new HashMap<>());
|
|
|
+ }
|
|
|
+ return AjaxResult.success(aiChatQualityService.selectAiChatQualityRecordById(id));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/aiChatQuality")
|
|
|
+ public AjaxResult qualityVerifyAdd(@RequestBody AiChatQualityRecord record) {
|
|
|
+ if (aiChatQualityService == null) return AjaxResult.error("评分服务不可用");
|
|
|
+ return AjaxResult.success(aiChatQualityService.insertAiChatQualityRecord(record));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PutMapping("/aiChatQuality")
|
|
|
+ public AjaxResult qualityVerifyUpdate(@RequestBody AiChatQualityRecord record) {
|
|
|
+ if (aiChatQualityService == null) return AjaxResult.error("评分服务不可用");
|
|
|
+ return AjaxResult.success(aiChatQualityService.updateAiChatQualityRecord(record));
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/aiChatQuality/{id}")
|
|
|
+ public AjaxResult qualityVerifyDelete(@PathVariable Long id) {
|
|
|
+ if (aiChatQualityService == null) return AjaxResult.error("评分服务不可用");
|
|
|
+ return AjaxResult.success(aiChatQualityService.deleteAiChatQualityRecordById(id));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/aiChatQuality/stats")
|
|
|
+ public AjaxResult qualityVerifyStats(@RequestParam(required = false) Long workflowId) {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ if (aiChatQualityService == null) {
|
|
|
+ stats.put("totalCount", 0);
|
|
|
+ stats.put("passCount", 0);
|
|
|
+ stats.put("failCount", 0);
|
|
|
+ stats.put("pendingCount", 0);
|
|
|
+ stats.put("avgScore", 0);
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+ AiChatQualityRecord query = new AiChatQualityRecord();
|
|
|
+ List<AiChatQualityRecord> all = aiChatQualityService.selectAiChatQualityRecordList(query);
|
|
|
+ long pass = all.stream().filter(r -> "pass".equalsIgnoreCase(r.getQualityResult())).count();
|
|
|
+ long fail = all.stream().filter(r -> "fail".equalsIgnoreCase(r.getQualityResult())).count();
|
|
|
+ long pending = all.stream().filter(r -> r.getQualityResult() == null || r.getQualityResult().isEmpty()).count();
|
|
|
+ stats.put("totalCount", all.size());
|
|
|
+ stats.put("passCount", pass);
|
|
|
+ stats.put("failCount", fail);
|
|
|
+ stats.put("pendingCount", pending);
|
|
|
+ stats.put("avgScore", all.isEmpty() ? 0 : Math.round(pass * 100.0 / Math.max(1, all.size())));
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 引擎核心占位端点 ========
|
|
|
+ @GetMapping("/workflow/lobster/engine/evolution/metrics")
|
|
|
+ public AjaxResult lobsterEngineEvolutionMetrics() {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("totalEvolutions", 0); data.put("appliedCount", 0); data.put("pendingCount", 0);
|
|
|
+ return AjaxResult.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/engine/evolution/analyze")
|
|
|
+ public AjaxResult lobsterEngineEvolutionAnalyze() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/engine/evolution/apply")
|
|
|
+ public AjaxResult lobsterEngineEvolutionApply() { return AjaxResult.success("操作成功"); }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/engine/heartbeat/status")
|
|
|
+ public AjaxResult lobsterEngineHeartbeat() {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("status", "healthy");
|
|
|
+ return AjaxResult.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/engine/channels")
|
|
|
+ public AjaxResult lobsterEngineChannels() { return AjaxResult.success(new ArrayList<>()); }
|
|
|
+
|
|
|
+ // ════════════════════════════════════════════════════════════════
|
|
|
+ // 画像配置 / 摘要配置 / 敏感词 / 消息去重 — 走真实 LobsterCompanyConfigService
|
|
|
+ // ════════════════════════════════════════════════════════════════
|
|
|
+
|
|
|
+ // ─── 画像配置 ───
|
|
|
+ @GetMapping("/workflow/lobster/profile-config/list")
|
|
|
+ public AjaxResult profileConfigList(@RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(companyConfigService.listProfile(companyId == null ? 0L : companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/profile-config/save")
|
|
|
+ public AjaxResult profileConfigSave(@RequestBody Map<String, Object> body) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.error("配置服务未启用");
|
|
|
+ return AjaxResult.success(companyConfigService.saveProfile(body));
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster/profile-config/{id}")
|
|
|
+ public AjaxResult profileConfigDelete(@PathVariable Long id,
|
|
|
+ @RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService != null) companyConfigService.deleteProfile(id, companyId == null ? 0L : companyId);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ─── 摘要配置 ───
|
|
|
+ @GetMapping("/workflow/lobster/summary-config/list")
|
|
|
+ public AjaxResult summaryConfigList(@RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(companyConfigService.listSummary(companyId == null ? 0L : companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/summary-config/save")
|
|
|
+ public AjaxResult summaryConfigSave(@RequestBody Map<String, Object> body) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.error("配置服务未启用");
|
|
|
+ return AjaxResult.success(companyConfigService.saveSummary(body));
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster/summary-config/{id}")
|
|
|
+ public AjaxResult summaryConfigDelete(@PathVariable Long id,
|
|
|
+ @RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService != null) companyConfigService.deleteSummary(id, companyId == null ? 0L : companyId);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ─── 敏感词 ───
|
|
|
+ @GetMapping("/workflow/lobster/sensitive-words/list")
|
|
|
+ public AjaxResult sensitiveWordsList(@RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(companyConfigService.listSensitive(companyId == null ? 0L : companyId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/sensitive-words/save")
|
|
|
+ public AjaxResult sensitiveWordsSave(@RequestBody Map<String, Object> body) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.error("配置服务未启用");
|
|
|
+ return AjaxResult.success(companyConfigService.saveSensitive(body));
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster/sensitive-words/{id}")
|
|
|
+ public AjaxResult sensitiveWordsDelete(@PathVariable Long id,
|
|
|
+ @RequestParam(required = false) Long companyId) {
|
|
|
+ if (companyConfigService != null) companyConfigService.deleteSensitive(id, companyId == null ? 0L : companyId);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/sensitive-words/check")
|
|
|
+ public AjaxResult sensitiveWordsCheck(@RequestBody Map<String, Object> body) {
|
|
|
+ if (companyConfigService == null) return AjaxResult.error("配置服务未启用");
|
|
|
+ Long companyId = body.get("companyId") == null ? 0L : Long.valueOf(body.get("companyId").toString());
|
|
|
+ String content = (String) body.get("content");
|
|
|
+ return AjaxResult.success(companyConfigService.checkSensitive(companyId, content));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ─── 消息去重监控 ───
|
|
|
+ @GetMapping("/workflow/lobster/dedup/stats")
|
|
|
+ public AjaxResult dedupStats(@RequestParam(required = false) Long companyId) {
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
+ stats.put("exactHits", 0);
|
|
|
+ stats.put("semanticHits", 0);
|
|
|
+ stats.put("totalChecked", 0);
|
|
|
+ stats.put("threshold", 0.70);
|
|
|
+ stats.put("note", "实时统计由 MessageDedupService 在内存维护,可后续接 Redis");
|
|
|
+ return AjaxResult.success(stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== E2E 端到端测试 ========
|
|
|
+ @PostMapping("/workflow/lobster/e2e/run")
|
|
|
+ public AjaxResult e2eRun(@RequestBody Map<String, Object> body) {
|
|
|
+ if (e2eTestService == null) return AjaxResult.error("E2E 测试服务未启用");
|
|
|
+ com.fs.company.service.workflow.LobsterE2eTestService.E2eRequest req =
|
|
|
+ new com.fs.company.service.workflow.LobsterE2eTestService.E2eRequest();
|
|
|
+ req.setCompanyId(toLong(body.get("companyId")));
|
|
|
+ req.setScenarioId(toLong(body.get("scenarioId")));
|
|
|
+ req.setTemplateId(toLong(body.get("templateId")));
|
|
|
+ req.setBusinessDesc((String) body.get("businessDesc"));
|
|
|
+ req.setIndustryType((String) body.get("industryType"));
|
|
|
+ req.setTestContactId(toLong(body.get("testContactId")));
|
|
|
+ Object ui = body.get("userInputs");
|
|
|
+ if (ui instanceof List) {
|
|
|
+ List<String> in = new ArrayList<>();
|
|
|
+ for (Object o : (List<?>) ui) if (o != null) in.add(o.toString());
|
|
|
+ req.setUserInputs(in);
|
|
|
+ }
|
|
|
+ return AjaxResult.success(e2eTestService.runE2e(req));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/e2e/report/{runId}")
|
|
|
+ public AjaxResult e2eReport(@PathVariable String runId) {
|
|
|
+ if (e2eTestService == null) return AjaxResult.error("E2E 测试服务未启用");
|
|
|
+ return AjaxResult.success(e2eTestService.getReport(runId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/e2e/list")
|
|
|
+ public AjaxResult e2eList(@RequestParam(required = false) Long companyId,
|
|
|
+ @RequestParam(defaultValue = "1") Integer pageNum,
|
|
|
+ @RequestParam(defaultValue = "20") Integer pageSize) {
|
|
|
+ if (e2eTestService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(e2eTestService.listRuns(companyId, pageNum, pageSize));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster-exec/step-next/{instanceId}")
|
|
|
+ public AjaxResult stepNext(@PathVariable Long instanceId, @RequestBody Map<String, Object> body) {
|
|
|
+ if (e2eTestService == null) return AjaxResult.error("E2E 测试服务未启用");
|
|
|
+ return AjaxResult.success(e2eTestService.stepNext(
|
|
|
+ toLong(body.get("companyId")), instanceId, (String) body.get("userInput")));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/chat/multi-turn")
|
|
|
+ public AjaxResult multiTurn(@RequestBody Map<String, Object> body) {
|
|
|
+ if (e2eTestService == null) return AjaxResult.error("E2E 测试服务未启用");
|
|
|
+ Object ui = body.get("userInputs");
|
|
|
+ List<String> in = new ArrayList<>();
|
|
|
+ if (ui instanceof List) for (Object o : (List<?>) ui) if (o != null) in.add(o.toString());
|
|
|
+ return AjaxResult.success(e2eTestService.multiTurn(
|
|
|
+ toLong(body.get("companyId")), toLong(body.get("instanceId")),
|
|
|
+ (String) body.get("nodeCode"), in));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ======== 测试场景剧本 ========
|
|
|
+ @GetMapping("/workflow/lobster/scenario/list")
|
|
|
+ public AjaxResult scenarioList(@RequestParam(required = false) Long companyId,
|
|
|
+ @RequestParam(required = false) Integer enabled,
|
|
|
+ @RequestParam(defaultValue = "1") Integer pageNum,
|
|
|
+ @RequestParam(defaultValue = "20") Integer pageSize) {
|
|
|
+ if (testScenarioService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(testScenarioService.listScenarios(companyId, enabled, pageNum, pageSize));
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/scenario/{id}")
|
|
|
+ public AjaxResult scenarioGet(@PathVariable Long id) {
|
|
|
+ if (testScenarioService == null) return AjaxResult.error("场景服务未启用");
|
|
|
+ return AjaxResult.success(testScenarioService.getScenario(id));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/scenario/save")
|
|
|
+ public AjaxResult scenarioSave(@RequestBody Map<String, Object> body) {
|
|
|
+ if (testScenarioService == null) return AjaxResult.error("场景服务未启用");
|
|
|
+ Object idObj = body.get("id");
|
|
|
+ if (idObj == null) {
|
|
|
+ return AjaxResult.success(testScenarioService.createScenario(body));
|
|
|
+ }
|
|
|
+ testScenarioService.updateScenario(toLong(idObj), body);
|
|
|
+ return AjaxResult.success(idObj);
|
|
|
+ }
|
|
|
+
|
|
|
+ @DeleteMapping("/workflow/lobster/scenario/{id}")
|
|
|
+ public AjaxResult scenarioDelete(@PathVariable Long id) {
|
|
|
+ if (testScenarioService != null) testScenarioService.deleteScenario(id);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/scenario/{id}/run")
|
|
|
+ public AjaxResult scenarioRunNow(@PathVariable Long id) {
|
|
|
+ if (testScenarioService == null) return AjaxResult.error("场景服务未启用");
|
|
|
+ String runId = testScenarioService.runScenarioNow(id);
|
|
|
+ Map<String, Object> r = new HashMap<>();
|
|
|
+ r.put("runId", runId);
|
|
|
+ return AjaxResult.success(r);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 动态节点学习产物审批 ==========
|
|
|
+
|
|
|
+ @GetMapping("/workflow/lobster/dynamic-impl/list")
|
|
|
+ public AjaxResult dynamicImplList(@RequestParam(required = false) String status) {
|
|
|
+ if (dynamicNodeImplService == null) return AjaxResult.success(new ArrayList<>());
|
|
|
+ return AjaxResult.success(dynamicNodeImplService.listByStatus(status, null));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/dynamic-impl/{id}/approve")
|
|
|
+ public AjaxResult dynamicImplApprove(@PathVariable Long id) {
|
|
|
+ if (dynamicNodeImplService == null) return AjaxResult.error("服务未启用");
|
|
|
+ dynamicNodeImplService.approve(id, getLoginUsername());
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/dynamic-impl/{id}/reject")
|
|
|
+ public AjaxResult dynamicImplReject(@PathVariable Long id, @RequestParam String reason) {
|
|
|
+ if (dynamicNodeImplService == null) return AjaxResult.error("服务未启用");
|
|
|
+ dynamicNodeImplService.reject(id, getLoginUsername(), reason);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getLoginUsername() {
|
|
|
+ return "admin"; // 动态节点审批由管理员操作
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostMapping("/workflow/lobster/scenario/run-all")
|
|
|
+ public AjaxResult scenarioRunAll() {
|
|
|
+ if (testScenarioService == null) return AjaxResult.error("场景服务未启用");
|
|
|
+ Map<String, Object> r = new HashMap<>();
|
|
|
+ r.put("triggered", testScenarioService.runAllEnabledScenarios());
|
|
|
+ return AjaxResult.success(r);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Long toLong(Object o) {
|
|
|
+ if (o == null) return null;
|
|
|
+ if (o instanceof Number) return ((Number) o).longValue();
|
|
|
+ try { return Long.valueOf(o.toString()); } catch (Exception e) { return null; }
|
|
|
+ }
|
|
|
+}
|