From ec7fb14f77fc210a6eee157891b9480bacbcce0c Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期三, 18 三月 2026 10:58:05 +0800
Subject: [PATCH] #删除旧AI代码
---
/dev/null | 1194 ---------------------------------------------------
rsf-admin/src/config/authProvider.js | 21
version/db/init.sql | 41 -
rsf-server/src/main/resources/application-prod.yml | 2
rsf-server/src/main/java/com/vincent/rsf/server/common/security/SecurityConfig.java | 1
rsf-server/src/main/resources/application.yml | 20
rsf-admin/src/i18n/zh.js | 21
rsf-admin/src/i18n/en.js | 21
rsf-admin/src/layout/AppBarToolbar.jsx | 16
rsf-admin/src/page/ResourceContent.js | 24 -
pom.xml | 1
rsf-server/src/main/resources/application-dev.yml | 3
12 files changed, 22 insertions(+), 1,343 deletions(-)
diff --git a/docs/AI_DEVELOPMENT_GUIDE.md b/docs/AI_DEVELOPMENT_GUIDE.md
deleted file mode 100644
index f37a006..0000000
--- a/docs/AI_DEVELOPMENT_GUIDE.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# WMS AI鍔熻兘寮�鍙戞枃妗�
-
-## 1. 鐩爣璇存槑
-
-鏈枃妗e熀浜� `zy-wcs-master` 鐨� AI 宸℃鎬濊矾锛屽褰撳墠 `wms-master` 椤圭洰杩涜浜嗗姛鑳芥槧灏勫拰瀹炵幇璇存槑銆�
-鏈瀹炵幇鐨勭洰鏍囦笉鏄収鎼� Spring AI + MCP 鐨勫畬鏁翠綋绯伙紝鑰屾槸澶嶇敤褰撳墠椤圭洰宸叉湁鐨� `rsf-server + rsf-ai-gateway + rsf-admin` 鏋舵瀯锛屼互鏈�灏忔敼鍔ㄨˉ榻愪互涓嬭兘鍔涳細
-
-- 閫氱敤鑱婂ぉ鍦烘櫙涓庣郴缁熻瘖鏂満鏅苟瀛�
-- 涓�閿瘖鏂叆鍙�
-- 鍩轰簬搴撳瓨銆佷换鍔°�佽澶囩珯鐐圭殑瀹炴椂璇婃柇涓婁笅鏂�
-- AI浼氳瘽涓庢秷鎭寔涔呭寲
-- 淇濇寔鐜版湁妯″瀷閰嶇疆涓績鍜岀綉鍏宠皟鐢ㄩ摼涓嶅彉
-
-## 2. 涓� `zy-wcs-master` 鐨勬槧灏勫叧绯�
-
-| `zy-wcs-master` 鑳藉姏 | 褰撳墠椤圭洰鏄犲皠鏂规 |
-| --- | --- |
-| 涓�閿贰妫� / 杩炵画杩介棶 | `AiController` 鏂板璇婃柇鍦烘櫙锛屽墠绔鍔犫�滀竴閿瘖鏂�� |
-| Prompt 鍦烘櫙涓績 | 浣跨敤 `sceneCode + AiProperties + AiPromptContextProvider` 缁勫悎瀹炵幇 |
-| MCP 宸ュ叿鑱氬悎 | 浣跨敤鐜版湁涓婁笅鏂囨彁渚涘櫒鐩存帴鏌ヨ涓氬姟琛紝鍏堝疄鐜拌交閲忕骇鏁版嵁宸ュ叿鑳藉姏 |
-| 浼氳瘽钀藉簱 | 鏂板 `sys_ai_chat_session`銆乣sys_ai_chat_message` |
-| LLM 缃戝叧 | 缁х画澶嶇敤 `rsf-ai-gateway` |
-
-璇存槑锛氬綋鍓嶇増鏈病鏈夌洿鎺ュ紩鍏� `zy-wcs-master` 鐨� Prompt 绠$悊鍚庡彴銆丮CP 鎸傝浇涓績銆佹ā鍨嬭矾鐢卞鐏句腑蹇冦�傝繖浜涜兘鍔涗繚鐣欎负涓嬩竴闃舵澧炲己椤广��
-
-## 3. 褰撳墠瀹炵幇鏋舵瀯
-
-### 3.1 妯″潡鍒嗗伐
-
-- `rsf-admin`
- - AI 瀵硅瘽鎶藉眽缁勪欢
- - 鏂板涓�閿瘖鏂寜閽�
-- `rsf-server`
- - AI鎺у埗鍣ㄣ�佸満鏅矾鐢便�佷笂涓嬫枃鎷艰銆佷細璇濆瓨鍌�
- - 鐩存帴浠� WMS 涓氬姟琛ㄨ鍙栬瘖鏂憳瑕�
-- `rsf-ai-gateway`
- - 缁熶竴灏佽 OpenAI 鍏煎娴佸紡鎺ュ彛
-
-### 3.2 璋冪敤閾捐矾
-
-```mermaid
-flowchart LR
- A["rsf-admin AI瀵硅瘽缁勪欢"] --> B["rsf-server /ai/chat/stream"]
- A --> C["rsf-server /ai/diagnose/stream"]
- B --> D["AiPromptContextService"]
- C --> D
- D --> E["搴撳瓨鎽樿"]
- D --> F["浠诲姟鎽樿"]
- D --> G["璁惧绔欑偣鎽樿"]
- B --> H["AiSessionService"]
- C --> H
- B --> I["rsf-ai-gateway"]
- C --> I
- I --> J["OpenAI鍏煎妯″瀷鎺ュ彛"]
-```
-
-## 4. 鍚庣瀹炵幇璇存槑
-
-### 4.1 鍦烘櫙瀹氫箟
-
-鏂板鍦烘櫙缂栫爜锛�
-
-- `general_chat`锛氭櫘閫氬璇�
-- `system_diagnose`锛氱郴缁熷贰妫�璇婃柇
-
-鐩稿叧浣嶇疆锛�
-
-- `rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiSceneCode.java`
-- `rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiChatStreamRequest.java`
-- `rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiPromptContext.java`
-
-### 4.2 鎺у埗鍣ㄨ兘鍔�
-
-`AiController` 褰撳墠鎻愪緵涓ゆ潯 SSE 鑳藉姏锛�
-
-- `/ai/chat/stream`
- - 閫氱敤鑱婂ぉ
- - 鏀寔鏄惧紡浼犲叆 `sceneCode`
-- `/ai/diagnose/stream`
- - 璇婃柇蹇嵎鍏ュ彛
- - 鑷姩浣跨敤 `system_diagnose`
- - 濡傛灉鍓嶇鏈紶娑堟伅锛屽垯浣跨敤榛樿宸℃鎻愮ず璇�
-
-### 4.3 Prompt 涓庝笂涓嬫枃绛栫暐
-
-褰撳墠瀹炵幇閲囩敤鈥滃熀纭�鎻愮ず璇� + 鍦烘櫙鎻愮ず璇� + 鏁版嵁鎽樿鎻愮ず璇嶁�濈殑缁勫悎鏂瑰紡锛�
-
-1. 鍩虹鎻愮ず璇嶆潵鑷ā鍨嬮厤缃垨 `application.yml`
-2. 璇婃柇鍦烘櫙杩藉姞 `diagnosis-system-prompt`
-3. `AiPromptContextService` 鑷姩鎷兼帴涓婁笅鏂囨彁渚涘櫒鍐呭
-
-涓婁笅鏂囨彁渚涘櫒濡備笅锛�
-
-- `AiDiagnosisPromptProvider`
- - 杈撳嚭璇婃柇娴佺▼鍜屽洖绛旂粨鏋勭害鏉�
-- `AiWarehouseSummaryService`
- - 姹囨�� `man_loc`銆乣man_loc_item`
-- `AiTaskSummaryService`
- - 姹囨�� `man_task`
-- `AiDeviceSiteSummaryService`
- - 姹囨�� `man_device_site`
-
-### 4.4 璇婃柇鏁版嵁鏉ユ簮
-
-鏈瀹炵幇浼樺厛鎺ュ叆瀹炴椂涓氬姟琛紝涓嶉澶栧紩鍏ョ嫭绔嬬煡璇嗗簱锛�
-
-- 搴撳瓨涓庡簱浣嶏細`man_loc`銆乣man_loc_item`
-- 浠诲姟锛歚man_task`
-- 璁惧绔欑偣锛歚man_device_site`
-
-杩欎娇寰� AI 鏇村亸鍚戔�滆繍琛屾�佽瘖鏂姪鎵嬧�濓紝鑰屼笉鏄�氱敤鐭ヨ瘑闂瓟鏈哄櫒浜恒��
-
-## 5. 浼氳瘽鎸佷箙鍖栬璁�
-
-### 5.1 琛ㄧ粨鏋�
-
-鏂板鑴氭湰锛歚version/db/20260316_ai_chat_storage.sql`
-
-娑夊強涓ゅ紶琛細
-
-- `sys_ai_chat_session`
-- `sys_ai_chat_message`
-
-### 5.2 瀛樺偍绛栫暐
-
-`AiSessionServiceImpl` 閲囩敤鈥滄暟鎹簱浼樺厛锛屽唴瀛樺厹搴曗�濇ā寮忥細
-
-- 濡傛灉妫�娴嬪埌涓ゅ紶琛ㄥ瓨鍦紝鍒欒嚜鍔ㄥ惎鐢ㄦ暟鎹簱鎸佷箙鍖�
-- 濡傛灉鏈墽琛岃縼绉昏剼鏈紝鍒欑户缁娇鐢ㄥ師鏈夊唴瀛樼紦瀛橀�昏緫
-
-杩欐牱鍙互闄嶄綆鍙戝竷椋庨櫓锛岄�傚悎鍒嗛樁娈典笂绾裤��
-
-### 5.3 涓婄嚎寤鸿
-
-寤鸿鍦ㄦ寮忓惎鐢ㄥ墠鍏堟墽琛屼互涓嬭剼鏈細
-
-- `version/db/20260311_ai_param.sql`
-- `version/db/20260316_ai_chat_storage.sql`
-
-鑴氭湰鎵ц瀹屾垚鍚庨噸鍚� `rsf-server`锛屼娇鍏跺湪鍚姩闃舵妫�娴嬪埌浼氳瘽琛ㄥ苟鍒囨崲鍒版寔涔呭寲妯″紡銆�
-
-## 6. 鍓嶇浜や簰璇存槑
-
-`rsf-admin/src/ai/AiChatWidget.jsx` 宸插鍔犫�滀竴閿瘖鏂�濆叆鍙o紝鍏ュ彛浣嶇疆鍖呮嫭锛�
-
-- 浼氳瘽椤堕儴宸ュ叿鍖�
-- 绌虹櫧鎬佸紩瀵煎尯
-- 搴曢儴杈撳叆鍖虹姸鎬佹爮
-
-鐐瑰嚮鍚庝細鍚� `/ai/diagnose/stream` 鍙戣捣娴佸紡璇锋眰锛屽苟鑷姩甯︿笂锛�
-
-- `sessionId`
-- `modelCode`
-- `sceneCode=system_diagnose`
-
-## 7. 閰嶇疆椤硅鏄�
-
-`rsf-server/src/main/resources/application.yml`
-
-鍏抽敭閰嶇疆锛�
-
-- `ai.system-prompt`
-- `ai.diagnosis-system-prompt`
-- `ai.default-model-code`
-- `ai.max-context-messages`
-
-妯″瀷鎺ュ叆閰嶇疆浠嶇劧浼樺厛浠� AI 鍙傛暟绠$悊璇诲彇锛宍application.yml` 涓昏鎵挎媴榛樿鍏滃簳閰嶇疆銆�
-
-## 8. 涓� `zy-wcs-master` 鐨勫樊璺�
-
-褰撳墠鐗堟湰宸茬粡鍏峰鈥滃彲鐢ㄢ�濈殑璇婃柇鑳藉姏锛屼絾涓� `zy-wcs-master` 鐩告瘮浠嶆湁浠ヤ笅宸窛锛�
-
-- 灏氭湭瀹炵幇鐙珛鐨� Prompt 閰嶇疆鍚庡彴
-- 灏氭湭瀹炵幇鏍囧噯 Spring AI MCP Server / MCP Mount 鑱氬悎
-- 灏氭湭瀹炵幇澶氭ā鍨嬭矾鐢便�佸け璐ュ垏鎹€�佽皟鐢ㄦ棩蹇椾腑蹇�
-- 褰撳墠璇婃柇渚濊禆鏁版嵁搴撴憳瑕侊紝灏氭湭绾冲叆鏃ュ織娴併�佽澶囧疄鏃剁嚎绋嬬姸鎬佺瓑鏇存繁灞傛暟鎹�
-
-## 9. 涓嬩竴闃舵寤鸿
-
-濡傛灉缁х画鍚� `zy-wcs-master` 闈犺繎锛屽缓璁寜浠ヤ笅椤哄簭婕旇繘锛�
-
-1. 鎶借薄缁熶竴鐨勨�淎I鏁版嵁宸ュ叿灞傗�濓紝鏇夸唬闆舵暎鐨勪笂涓嬫枃鎻愪緵鍣�
-2. 鏂板 Prompt 妯℃澘琛ㄥ拰鍙戝竷鏈哄埗
-3. 缁� `rsf-ai-gateway` 澧炲姞璋冪敤鏃ュ織銆佸け璐ュ垏鎹€�佹ā鍨嬭矾鐢�
-4. 灏嗘棩蹇椼�佷换鍔″紓甯搞�佹帴鍙eけ璐ョ巼绾冲叆璇婃柇涓婁笅鏂�
-5. 澧炲姞鐙珛鐨� AI 绠$悊椤碉紝鐢ㄤ簬璇婃柇璁板綍銆丳rompt 璋冧紭鍜屾ā鍨嬪垏鎹�
diff --git a/pom.xml b/pom.xml
index a8ab56d..f6f4fa1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,6 @@
<module>rsf-framework</module>
<module>rsf-server</module>
<module>rsf-open-api</module>
- <module>rsf-ai-gateway</module>
</modules>
<properties>
diff --git a/rsf-admin/src/ai/AiChatWidget.jsx b/rsf-admin/src/ai/AiChatWidget.jsx
deleted file mode 100644
index 9a5bcf0..0000000
--- a/rsf-admin/src/ai/AiChatWidget.jsx
+++ /dev/null
@@ -1,800 +0,0 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react';
-import {
- Alert,
- Avatar,
- Box,
- Button,
- Chip,
- CircularProgress,
- Divider,
- Drawer,
- Fab,
- IconButton,
- List,
- ListItemButton,
- ListItemText,
- MenuItem,
- Select,
- Stack,
- TextField,
- Tooltip,
- Typography
-} from '@mui/material';
-import AddIcon from '@mui/icons-material/Add';
-import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline';
-import CloseIcon from '@mui/icons-material/Close';
-import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
-import SendIcon from '@mui/icons-material/Send';
-import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined';
-import { createAiSession, getAiMessages, getAiModels, getAiSessions, removeAiSession, stopAiChat } from '@/api/ai';
-import { PREFIX_BASE_URL, TOKEN_HEADER_NAME } from '@/config/setting';
-import { getToken } from '@/utils/token-util';
-
-const DRAWER_WIDTH = 720;
-const SESSION_WIDTH = 220;
-const DIAGNOSIS_MESSAGE = '璇峰褰撳墠WMS绯荤粺杩涜涓�娆″贰妫�璇婃柇锛岀粨鍚堝簱瀛樸�佷换鍔°�佽澶囩珯鐐规暟鎹瘑鍒紓甯稿苟缁欏嚭澶勭悊寤鸿銆�';
-
-const parseSseChunk = (chunk, onEvent) => {
- const blocks = chunk.split('\n\n');
- const remain = blocks.pop();
- blocks.forEach((block) => {
- if (!block.trim()) {
- return;
- }
- let eventName = 'message';
- const dataLines = [];
- block.split('\n').forEach((line) => {
- if (line.startsWith('event:')) {
- eventName = line.substring(6).trim();
- }
- if (line.startsWith('data:')) {
- dataLines.push(line.substring(5).trim());
- }
- });
- if (!dataLines.length) {
- return;
- }
- try {
- onEvent(eventName, JSON.parse(dataLines.join('\n')));
- } catch (error) {
- console.error(error);
- }
- });
- return remain || '';
-};
-
-const buildAssistantMessage = (sessionId, modelCode) => ({
- id: `assistant-${Date.now()}`,
- sessionId,
- role: 'assistant',
- content: '',
- modelCode,
- createTime: new Date().toISOString()
-});
-
-const buildFallbackAnswer = (modelCode, question) => `褰撳墠涓烘紨绀烘ā寮忥紝妯″瀷[${modelCode}]宸叉敹鍒颁綘鐨勯棶棰橈細${question}`;
-
-const shouldUseMockFallback = (models, modelCode) => {
- const model = (models || []).find((item) => item.code === modelCode);
- return model ? model.provider === 'mock' : String(modelCode || '').startsWith('mock');
-};
-
-const formatTime = (value) => {
- if (!value) {
- return '';
- }
- const date = new Date(value);
- if (Number.isNaN(date.getTime())) {
- return '';
- }
- return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
-};
-
-const formatPreview = (value) => {
- if (!value) {
- return '寮�濮嬩竴涓柊鐨勫璇�';
- }
- const normalized = String(value).replace(/\s+/g, ' ').trim();
- return normalized.length > 22 ? `${normalized.slice(0, 22)}...` : normalized;
-};
-
-export const AiChatWidget = ({
- trigger = 'fab',
- buttonText = 'AI 瀵硅瘽',
- buttonVariant = 'contained',
- buttonSx = {}
-}) => {
- const [open, setOpen] = useState(false);
- const [loading, setLoading] = useState(false);
- const [sending, setSending] = useState(false);
- const [models, setModels] = useState([]);
- const [sessions, setSessions] = useState([]);
- const [activeSessionId, setActiveSessionId] = useState('');
- const [messagesBySession, setMessagesBySession] = useState({});
- const [draft, setDraft] = useState('');
- const [error, setError] = useState('');
- const streamControllerRef = useRef(null);
- const messagesEndRef = useRef(null);
-
- const scrollToBottom = (behavior = 'auto') => {
- window.setTimeout(() => {
- window.requestAnimationFrame(() => {
- messagesEndRef.current?.scrollIntoView({ behavior, block: 'end' });
- });
- }, 0);
- };
-
- const activeSession = useMemo(
- () => sessions.find((item) => item.id === activeSessionId) || null,
- [sessions, activeSessionId]
- );
-
- const activeMessages = messagesBySession[activeSessionId] || [];
-
- useEffect(() => {
- if (open) {
- bootstrap();
- }
- }, [open]);
-
- useEffect(() => {
- if (!open) {
- return undefined;
- }
- const timer = window.setTimeout(() => {
- scrollToBottom('auto');
- }, 180);
- return () => window.clearTimeout(timer);
- }, [open, activeSessionId]);
-
- useEffect(() => {
- if (open && activeSessionId) {
- loadMessages(activeSessionId);
- }
- }, [open, activeSessionId]);
-
- useEffect(() => {
- if (open && messagesEndRef.current) {
- scrollToBottom('smooth');
- }
- }, [open, activeMessages]);
-
- const bootstrap = async () => {
- setLoading(true);
- setError('');
- try {
- const [modelList, sessionList] = await Promise.all([getAiModels(), getAiSessions()]);
- setModels(modelList);
- if (sessionList.length > 0) {
- setSessions(sessionList);
- setActiveSessionId((prev) => prev || sessionList[0].id);
- } else {
- const created = await createAiSession({ modelCode: modelList[0]?.code });
- setSessions(created ? [created] : []);
- setActiveSessionId(created?.id || '');
- if (created?.id) {
- setMessagesBySession((prev) => ({ ...prev, [created.id]: [] }));
- }
- }
- } catch (bootstrapError) {
- setError(bootstrapError.message || 'AI鍔╂墜鍒濆鍖栧け璐�');
- } finally {
- setLoading(false);
- }
- };
-
- const refreshSessions = async (preferSessionId) => {
- try {
- const sessionList = await getAiSessions();
- if (sessionList.length > 0) {
- setSessions(sessionList);
- setActiveSessionId(preferSessionId || sessionList[0].id);
- }
- } catch (refreshError) {
- setError(refreshError.message || '鍒锋柊浼氳瘽澶辫触');
- }
- };
-
- const loadMessages = async (sessionId) => {
- if (!sessionId || messagesBySession[sessionId]) {
- return;
- }
- try {
- const messageList = await getAiMessages(sessionId);
- setMessagesBySession((prev) => {
- const current = prev[sessionId];
- if (current && current.length > 0) {
- return prev;
- }
- return { ...prev, [sessionId]: messageList };
- });
- scrollToBottom('auto');
- } catch (messageError) {
- setError(messageError.message || '鍔犺浇娑堟伅澶辫触');
- }
- };
-
- const handleCreateSession = async () => {
- try {
- const created = await createAiSession({ modelCode: activeSession?.modelCode || models[0]?.code });
- setSessions((prev) => [created, ...prev]);
- setActiveSessionId(created.id);
- setMessagesBySession((prev) => ({ ...prev, [created.id]: [] }));
- } catch (createError) {
- setError(createError.message || '鍒涘缓浼氳瘽澶辫触');
- }
- };
-
- const handleDeleteSession = async (sessionId) => {
- try {
- await removeAiSession(sessionId);
- const nextSessions = sessions.filter((item) => item.id !== sessionId);
- setSessions(nextSessions);
- setMessagesBySession((prev) => {
- const next = { ...prev };
- delete next[sessionId];
- return next;
- });
- if (nextSessions.length > 0) {
- setActiveSessionId(nextSessions[0].id);
- } else {
- const created = await createAiSession({ modelCode: models[0]?.code });
- if (created?.id) {
- setSessions([created]);
- setActiveSessionId(created.id);
- setMessagesBySession({ [created.id]: [] });
- }
- }
- } catch (deleteError) {
- setError(deleteError.message || '鍒犻櫎浼氳瘽澶辫触');
- }
- };
-
- const handleModelChange = (modelCode) => {
- setSessions((prev) => prev.map((item) => item.id === activeSessionId ? { ...item, modelCode } : item));
- };
-
- const handleStop = async () => {
- if (!activeSessionId) {
- return;
- }
- if (streamControllerRef.current) {
- streamControllerRef.current.abort();
- }
- try {
- await stopAiChat({ sessionId: activeSessionId });
- } catch (stopError) {
- console.error(stopError);
- } finally {
- setSending(false);
- }
- };
-
- const streamChat = async ({ userContent, endpoint, sceneCode }) => {
- if (!userContent || !activeSessionId || sending) {
- return;
- }
- const sessionId = activeSessionId;
- const modelCode = activeSession?.modelCode || models[0]?.code;
- setError('');
- setSending(true);
- setMessagesBySession((prev) => {
- const current = prev[sessionId] || [];
- return {
- ...prev,
- [sessionId]: [
- ...current,
- {
- id: `user-${Date.now()}`,
- sessionId,
- role: 'user',
- content: userContent,
- modelCode,
- createTime: new Date().toISOString()
- },
- buildAssistantMessage(sessionId, modelCode)
- ]
- };
- });
-
- const controller = new AbortController();
- streamControllerRef.current = controller;
- let buffer = '';
- let receivedDelta = false;
-
- try {
- const response = await fetch(`${PREFIX_BASE_URL}${endpoint}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'text/event-stream',
- [TOKEN_HEADER_NAME]: getToken()
- },
- body: JSON.stringify({
- sessionId,
- message: userContent,
- modelCode,
- sceneCode
- }),
- signal: controller.signal
- });
-
- if (!response.ok || !response.body) {
- throw new Error('AI娴佸紡璇锋眰澶辫触');
- }
-
- const reader = response.body.getReader();
- const decoder = new TextDecoder('utf-8');
-
- while (true) {
- const { value, done } = await reader.read();
- if (done) {
- break;
- }
- buffer += decoder.decode(value, { stream: true });
- buffer = parseSseChunk(buffer, (eventName, payload) => {
- if (eventName === 'session' && payload.sessionId) {
- setSessions((prev) => prev.map((item) => item.id === sessionId ? { ...item, modelCode: payload.modelCode || modelCode } : item));
- }
- if (eventName === 'delta') {
- receivedDelta = true;
- setMessagesBySession((prev) => {
- const current = [...(prev[sessionId] || [])];
- if (!current.length) {
- return prev;
- }
- const last = current[current.length - 1];
- current[current.length - 1] = { ...last, content: `${last.content || ''}${payload.content || ''}` };
- return { ...prev, [sessionId]: current };
- });
- }
- if (eventName === 'error') {
- setError(payload.message || 'AI鏈嶅姟寮傚父');
- }
- });
- }
-
- const latestMessages = await getAiMessages(sessionId);
- setMessagesBySession((prev) => {
- const current = prev[sessionId] || [];
- if (!receivedDelta && current.length > 0 && shouldUseMockFallback(models, modelCode)) {
- const fallbackAnswer = buildFallbackAnswer(modelCode, userContent);
- const next = [...current];
- const last = next[next.length - 1];
- if (last && last.role === 'assistant' && !last.content) {
- next[next.length - 1] = { ...last, content: fallbackAnswer };
- return { ...prev, [sessionId]: next };
- }
- }
- if (latestMessages.length === 0 && current.length > 0) {
- return prev;
- }
- return { ...prev, [sessionId]: latestMessages };
- });
- setSessions((prev) => prev.map((item) => {
- if (item.id !== sessionId) {
- return item;
- }
- const lastMessage = assistantReplyText(latestMessages) || item.lastMessage || userContent;
- return {
- ...item,
- lastMessage,
- lastMessageAt: new Date().toISOString(),
- updateTime: new Date().toISOString()
- };
- }));
- await refreshSessions(sessionId);
- } catch (sendError) {
- if (sendError.name !== 'AbortError') {
- setError(sendError.message || '鍙戦�佹秷鎭け璐�');
- }
- } finally {
- setSending(false);
- streamControllerRef.current = null;
- }
- };
-
- const handleSend = async () => {
- if (!draft.trim() || !activeSessionId || sending) {
- return;
- }
- const userContent = draft.trim();
- setDraft('');
- await streamChat({
- userContent,
- endpoint: 'ai/chat/stream',
- sceneCode: 'general_chat'
- });
- };
-
- const handleDiagnose = async () => {
- if (!activeSessionId || sending) {
- return;
- }
- await streamChat({
- userContent: DIAGNOSIS_MESSAGE,
- endpoint: 'ai/diagnose/stream',
- sceneCode: 'system_diagnose'
- });
- };
-
- const assistantReplyText = (messageList) => {
- const assistantMessages = (messageList || []).filter((item) => item.role === 'assistant');
- if (!assistantMessages.length) {
- return '';
- }
- return assistantMessages[assistantMessages.length - 1].content || '';
- };
-
- return (
- <>
- {trigger === 'fab' ? (
- <Fab
- color="secondary"
- onClick={() => setOpen(true)}
- sx={{ position: 'fixed', right: 24, bottom: 24, zIndex: 1301 }}
- >
- <ChatBubbleOutlineIcon />
- </Fab>
- ) : (
- <Button
- variant={buttonVariant}
- startIcon={<ChatBubbleOutlineIcon />}
- onClick={() => setOpen(true)}
- sx={buttonSx}
- >
- {buttonText}
- </Button>
- )}
- <Drawer
- anchor="right"
- open={open}
- onClose={() => setOpen(false)}
- PaperProps={{
- sx: {
- width: DRAWER_WIDTH,
- maxWidth: '100vw',
- backgroundColor: '#f5f7fa'
- }
- }}
- >
- <Stack sx={{ height: '100%' }}>
- <Stack
- direction="row"
- alignItems="center"
- spacing={1.5}
- sx={{
- px: 2,
- py: 1.5,
- backgroundColor: '#fff',
- color: 'text.primary',
- borderBottom: '1px solid rgba(0, 0, 0, 0.08)'
- }}
- >
- <Avatar
- sx={{
- width: 38,
- height: 38,
- bgcolor: 'rgba(25, 118, 210, 0.12)',
- color: 'primary.main',
- fontSize: 16,
- fontWeight: 700
- }}
- >
- AI
- </Avatar>
- <Box sx={{ flex: 1, minWidth: 0 }}>
- <Typography variant="h6" sx={{ fontWeight: 700, lineHeight: 1.2 }}>
- 鏅鸿兘瀵硅瘽
- </Typography>
- <Typography variant="caption" color="text.secondary">
- 澶氫細璇濄�佸妯″瀷銆佹祦寮忓搷搴�
- </Typography>
- </Box>
- <Chip
- size="small"
- label={sending ? '鐢熸垚涓�' : '鍦ㄧ嚎'}
- sx={{
- bgcolor: sending ? 'rgba(255, 167, 38, 0.14)' : 'rgba(76, 175, 80, 0.12)',
- color: sending ? '#ad6800' : '#2e7d32',
- border: '1px solid rgba(0, 0, 0, 0.06)'
- }}
- />
- <Tooltip title="鏂板缓浼氳瘽">
- <IconButton color="primary" onClick={handleCreateSession}>
- <AddIcon />
- </IconButton>
- </Tooltip>
- <IconButton color="default" onClick={() => setOpen(false)}>
- <CloseIcon />
- </IconButton>
- </Stack>
- <Divider />
- {error ? <Alert severity="error" sx={{ m: 1.5, borderRadius: 2 }}>{error}</Alert> : null}
- <Stack direction="row" sx={{ minHeight: 0, flex: 1 }}>
- <Box sx={{
- width: SESSION_WIDTH,
- borderRight: '1px solid',
- borderColor: 'rgba(0, 0, 0, 0.08)',
- display: 'flex',
- flexDirection: 'column',
- backgroundColor: '#f7f9fc'
- }}>
- <Box sx={{ px: 1.5, py: 1.25 }}>
- <Typography variant="caption" color="text.secondary" sx={{ fontWeight: 700 }}>
- 浼氳瘽鍒楄〃
- </Typography>
- </Box>
- <List sx={{ flex: 1, overflowY: 'auto', py: 0, px: 1 }}>
- {sessions.map((item) => (
- <ListItemButton
- key={item.id}
- selected={item.id === activeSessionId}
- onClick={() => setActiveSessionId(item.id)}
- sx={{
- alignItems: 'flex-start',
- pr: 1,
- mb: 0.75,
- borderRadius: 2,
- border: '1px solid',
- borderColor: item.id === activeSessionId ? 'rgba(25, 118, 210, 0.28)' : 'rgba(0, 0, 0, 0.06)',
- backgroundColor: item.id === activeSessionId ? '#eaf3fe' : '#fff',
- boxShadow: item.id === activeSessionId ? '0 6px 16px rgba(25, 118, 210, 0.08)' : 'none'
- }}
- >
- <ListItemText
- primary={item.title || '鏂板璇�'}
- secondary={`${formatPreview(item.lastMessage || item.modelCode)}${formatTime(item.updateTime || item.lastMessageAt) ? ` 路 ${formatTime(item.updateTime || item.lastMessageAt)}` : ''}`}
- primaryTypographyProps={{ noWrap: true, fontSize: 13, fontWeight: 700 }}
- secondaryTypographyProps={{ noWrap: true, fontSize: 11.5, color: 'text.secondary' }}
- />
- <IconButton
- size="small"
- edge="end"
- sx={{ mt: 0.25 }}
- onClick={(event) => {
- event.stopPropagation();
- handleDeleteSession(item.id);
- }}
- >
- <DeleteOutlineIcon fontSize="inherit" />
- </IconButton>
- </ListItemButton>
- ))}
- </List>
- </Box>
- <Stack sx={{ flex: 1, minWidth: 0 }}>
- <Stack
- direction="row"
- alignItems="center"
- spacing={1}
- sx={{
- px: 2,
- py: 1.5,
- backgroundColor: '#fff'
- }}
- >
- <Box sx={{ flex: 1, minWidth: 0 }}>
- <Typography variant="body2" color="text.secondary">
- 褰撳墠妯″瀷
- </Typography>
- <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
- {activeSession?.title || '鏈懡鍚嶄細璇�'}
- </Typography>
- </Box>
- <Select
- size="small"
- value={activeSession?.modelCode || models[0]?.code || ''}
- onChange={(event) => handleModelChange(event.target.value)}
- sx={{
- minWidth: 176,
- borderRadius: 2,
- backgroundColor: '#fff',
- boxShadow: '0 1px 2px rgba(15, 23, 42, 0.06)'
- }}
- >
- {models.map((model) => (
- <MenuItem key={model.code} value={model.code}>
- {model.name}
- </MenuItem>
- ))}
- </Select>
- <Button
- variant="outlined"
- size="small"
- disabled={sending || !activeSessionId}
- onClick={handleDiagnose}
- sx={{ borderRadius: 2, whiteSpace: 'nowrap' }}
- >
- 涓�閿瘖鏂�
- </Button>
- </Stack>
- <Divider />
- <Box
- sx={{
- flex: 1,
- overflowY: 'auto',
- px: 2.5,
- py: 2,
- backgroundColor: '#f5f7fa'
- }}
- >
- {loading ? (
- <Stack alignItems="center" justifyContent="center" sx={{ height: '100%' }}>
- <CircularProgress size={28} />
- </Stack>
- ) : activeMessages.length === 0 ? (
- <Stack
- alignItems="center"
- justifyContent="center"
- spacing={1.25}
- sx={{
- height: '100%',
- textAlign: 'center',
- color: 'text.secondary',
- px: 3
- }}
- >
- <Avatar sx={{ width: 56, height: 56, bgcolor: 'rgba(25,118,210,0.1)', color: 'primary.main' }}>
- AI
- </Avatar>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: 'text.primary' }}>
- 寮�濮嬫柊鐨勬櫤鑳藉璇�
- </Typography>
- <Typography variant="body2">
- 鍙互鐩存帴鎻愰棶浠撳偍涓氬姟闂锛屾垨鐐瑰嚮涓�閿瘖鏂揩閫熷贰妫�褰撳墠WMS鐘舵�併��
- </Typography>
- <Button variant="outlined" onClick={handleDiagnose} disabled={sending || !activeSessionId}>
- 涓�閿瘖鏂�
- </Button>
- </Stack>
- ) : (
- <Stack spacing={2}>
- {activeMessages.map((message) => (
- <Box
- key={message.id}
- sx={{
- alignSelf: message.role === 'user' ? 'flex-end' : 'flex-start',
- maxWidth: '88%'
- }}
- >
- <Stack
- direction={message.role === 'user' ? 'row-reverse' : 'row'}
- spacing={1}
- alignItems="flex-start"
- >
- <Avatar
- sx={{
- width: 30,
- height: 30,
- mt: 0.25,
- bgcolor: message.role === 'user' ? '#1976d2' : '#eaf3fe',
- color: message.role === 'user' ? '#fff' : '#1976d2',
- fontSize: 12,
- fontWeight: 700
- }}
- >
- {message.role === 'user' ? '鎴�' : 'AI'}
- </Avatar>
- <Box
- sx={{
- maxWidth: 'calc(100% - 42px)',
- display: 'flex',
- flexDirection: 'column',
- alignItems: message.role === 'user' ? 'flex-end' : 'flex-start'
- }}
- >
- <Stack
- direction="row"
- spacing={0.75}
- alignItems="center"
- justifyContent={message.role === 'user' ? 'flex-end' : 'flex-start'}
- sx={{ px: 0.5, mb: 0.5 }}
- >
- <Typography variant="caption" color="text.secondary" sx={{ fontWeight: 700 }}>
- {message.role === 'assistant' ? message.modelCode || activeSession?.modelCode : '鎴�'}
- </Typography>
- <Typography variant="caption" color="text.disabled">
- {formatTime(message.createTime)}
- </Typography>
- </Stack>
- <Box
- sx={{
- px: 1.8,
- py: 1.35,
- minWidth: message.role === 'user' ? 96 : 'auto',
- maxWidth: message.role === 'user' ? '82%' : '100%',
- borderRadius: message.role === 'user' ? '20px 20px 8px 20px' : '20px 20px 20px 8px',
- background: '#fff',
- color: 'text.primary',
- boxShadow: '0 8px 18px rgba(15, 23, 42, 0.05)',
- border: '1px solid rgba(25, 118, 210, 0.08)',
- display: 'inline-block',
- whiteSpace: 'pre-wrap',
- wordBreak: 'normal',
- overflowWrap: 'anywhere',
- lineHeight: 1.7,
- fontSize: 14
- }}
- >
- {message.content || (message.role === 'assistant' && sending ? '姝e湪鐢熸垚鍥炲...' : '')}
- </Box>
- </Box>
- </Stack>
- </Box>
- ))}
- <div ref={messagesEndRef} />
- </Stack>
- )}
- </Box>
- <Divider />
- <Box
- sx={{
- p: 1.5,
- backgroundColor: '#fff'
- }}
- >
- <Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>
- <Chip
- size="small"
- label={activeSession?.modelCode || '鏈�夋嫨妯″瀷'}
- sx={{ bgcolor: 'rgba(25,118,210,0.08)', color: 'primary.main' }}
- />
- <Button
- variant="text"
- size="small"
- disabled={sending || !activeSessionId}
- onClick={handleDiagnose}
- sx={{ minWidth: 'auto', px: 1 }}
- >
- 涓�閿瘖鏂�
- </Button>
- <Typography variant="caption" color="text.secondary">
- `Enter` 鍙戦�侊紝`Shift + Enter` 鎹㈣
- </Typography>
- </Stack>
- <TextField
- fullWidth
- multiline
- minRows={3}
- maxRows={6}
- value={draft}
- onChange={(event) => setDraft(event.target.value)}
- placeholder="杈撳叆闂锛屾敮鎸佸浼氳瘽銆佸妯″瀷鍜屼竴閿瘖鏂�"
- onKeyDown={(event) => {
- if (event.key === 'Enter' && !event.shiftKey) {
- event.preventDefault();
- handleSend();
- }
- }}
- sx={{
- '& .MuiOutlinedInput-root': {
- borderRadius: 3,
- backgroundColor: '#fff'
- }
- }}
- />
- <Stack direction="row" spacing={1} justifyContent="flex-end" sx={{ mt: 1.2 }}>
- <Tooltip title="鍋滄鐢熸垚">
- <span>
- <IconButton color="warning" disabled={!sending} onClick={handleStop}>
- <StopCircleOutlinedIcon />
- </IconButton>
- </span>
- </Tooltip>
- <Tooltip title="鍙戦��">
- <span>
- <IconButton color="primary" disabled={sending || !draft.trim()} onClick={handleSend}>
- <SendIcon />
- </IconButton>
- </span>
- </Tooltip>
- </Stack>
- </Box>
- </Stack>
- </Stack>
- </Stack>
- </Drawer>
- </>
- );
-};
diff --git a/rsf-admin/src/api/ai/index.js b/rsf-admin/src/api/ai/index.js
deleted file mode 100644
index 4bb5fab..0000000
--- a/rsf-admin/src/api/ai/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import request from '../../utils/request';
-
-export async function getAiModels() {
- const res = await request.get('/ai/model/list');
- if (res.data.code === 200) {
- return res.data.data || [];
- }
- return Promise.reject(new Error(res.data.msg || 'Load models failed'));
-}
-
-export async function getAiSessions() {
- const res = await request.get('/ai/session/list');
- if (res.data.code === 200) {
- return res.data.data || [];
- }
- return Promise.reject(new Error(res.data.msg || 'Load sessions failed'));
-}
-
-export async function createAiSession(payload = {}) {
- const res = await request.post('/ai/session/create', payload);
- if (res.data.code === 200) {
- return res.data.data;
- }
- return Promise.reject(new Error(res.data.msg || 'Create session failed'));
-}
-
-export async function removeAiSession(sessionId) {
- const res = await request.post(`/ai/session/remove/${sessionId}`);
- return res.data;
-}
-
-export async function getAiMessages(sessionId) {
- const res = await request.get(`/ai/session/${sessionId}/messages`);
- if (res.data.code === 200) {
- return res.data.data || [];
- }
- return Promise.reject(new Error(res.data.msg || 'Load messages failed'));
-}
-
-export async function stopAiChat(payload) {
- const res = await request.post('/ai/chat/stop', payload);
- return res.data;
-}
diff --git a/rsf-admin/src/config/authProvider.js b/rsf-admin/src/config/authProvider.js
index 97820b0..cb8083e 100644
--- a/rsf-admin/src/config/authProvider.js
+++ b/rsf-admin/src/config/authProvider.js
@@ -4,6 +4,25 @@
import avatar from '/avatar.jpg'
+const AI_COMPONENTS = new Set([
+ 'aiParam',
+ 'aiPrompt',
+ 'aiDiagnosis',
+ 'aiDiagnosisPlan',
+ 'aiCallLog',
+ 'aiRoute',
+ 'aiToolConfig',
+ 'aiMcpMount',
+]);
+
+const filterAiMenus = (items = []) =>
+ items
+ .filter((item) => !AI_COMPONENTS.has(item.component))
+ .map((item) => ({
+ ...item,
+ children: item.children ? filterAiMenus(item.children) : item.children,
+ }));
+
const AuthProvider = {
// login
login: async ({ username, password, tenantId }) => {
@@ -58,7 +77,7 @@
const res = await menus();
const { code, msg, data } = res.data;
if (code === 200) {
- return Promise.resolve(data);
+ return Promise.resolve(filterAiMenus(data));
} else if (code === 401) {
localStorage.removeItem("user");
return Promise.reject();
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index ab6ed0e..00fd50a 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -150,14 +150,6 @@
token: 'Token',
operation: 'Operation',
config: 'Config',
- aiParam: 'AI Params',
- aiPrompt: 'AI Prompt',
- aiDiagnosis: 'AI Diagnosis',
- aiDiagnosisPlan: 'AI Diagnosis Plan',
- aiCallLog: 'AI Call Log',
- aiRoute: 'AI Route',
- aiToolConfig: 'AI Diagnostic Tool',
- aiMcpMount: 'AI MCP Mount',
tenant: 'Tenant',
userLogin: 'Token',
customer: 'Customer',
@@ -408,19 +400,6 @@
val: "val",
content: "content",
type: "type",
- },
- aiParam: {
- uuid: "uuid",
- name: "name",
- modelCode: "model code",
- provider: "provider",
- chatUrl: "chat url",
- apiKey: "api key",
- modelName: "model name",
- systemPrompt: "system prompt",
- maxContextMessages: "max context",
- defaultFlag: "default",
- sort: "sort",
},
tenant: {
name: "name",
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index ddf6a87..f54083c 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -151,14 +151,6 @@
token: '鐧诲綍鏃ュ織',
operation: '鎿嶄綔鏃ュ織',
config: '閰嶇疆鍙傛暟',
- aiParam: 'AI鍙傛暟',
- aiPrompt: 'AI鎻愮ず璇�',
- aiDiagnosis: 'AI璇婃柇璁板綍',
- aiDiagnosisPlan: 'AI宸℃璁″垝',
- aiCallLog: 'AI璋冪敤鏃ュ織',
- aiRoute: 'AI妯″瀷璺敱',
- aiToolConfig: 'AI璇婃柇宸ュ叿',
- aiMcpMount: 'AI MCP鎸傝浇',
tenant: '绉熸埛绠$悊',
userLogin: '鐧诲綍鏃ュ織',
customer: '瀹㈡埛琛�',
@@ -437,19 +429,6 @@
val: "閰嶇疆鍊�",
content: "閰嶇疆鍐呭",
type: "鏁版嵁绫诲瀷",
- },
- aiParam: {
- uuid: "缂栧彿",
- name: "鍚嶇О",
- modelCode: "妯″瀷缂栫爜",
- provider: "渚涘簲鍟�",
- chatUrl: "鑱婂ぉ鍦板潃",
- apiKey: "API瀵嗛挜",
- modelName: "妯″瀷鍚嶇О",
- systemPrompt: "绯荤粺鎻愮ず璇�",
- maxContextMessages: "涓婁笅鏂囪疆鏁�",
- defaultFlag: "榛樿妯″瀷",
- sort: "鎺掑簭",
},
tenant: {
name: "绉熸埛鍚�",
diff --git a/rsf-admin/src/layout/AppBarToolbar.jsx b/rsf-admin/src/layout/AppBarToolbar.jsx
index ff7cd2b..45cf9db 100644
--- a/rsf-admin/src/layout/AppBarToolbar.jsx
+++ b/rsf-admin/src/layout/AppBarToolbar.jsx
@@ -1,28 +1,12 @@
import { LoadingIndicator, LocalesMenuButton } from 'react-admin';
import { ThemeSwapper } from '../themes/ThemeSwapper';
import { TenantTip } from './TenantTip';
-import { AiChatWidget } from '@/ai/AiChatWidget';
export const AppBarToolbar = () => (
<>
<LocalesMenuButton />
<ThemeSwapper />
<LoadingIndicator />
- <AiChatWidget
- trigger="button"
- buttonText="AI 瀵硅瘽"
- buttonVariant="text"
- buttonSx={{
- minWidth: 'auto',
- px: 1.25,
- color: '#fff',
- borderRadius: 2,
- whiteSpace: 'nowrap',
- '&:hover': {
- backgroundColor: 'rgba(255,255,255,0.12)'
- }
- }}
- />
<TenantTip />
</>
);
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 9f7c34d..22fdb9a 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -6,14 +6,6 @@
import host from "./system/host";
import config from "./system/config";
-import aiParam from "./system/aiParam";
-import aiPrompt from "./system/aiPrompt";
-import aiDiagnosis from "./system/aiDiagnosis";
-import aiDiagnosisPlan from "./system/aiDiagnosisPlan";
-import aiCallLog from "./system/aiCallLog";
-import aiRoute from "./system/aiRoute";
-import aiToolConfig from "./system/aiToolConfig";
-import aiMcpMount from "./system/aiMcpMount";
import tenant from "./system/tenant";
import role from "./system/role";
import userLogin from "./system/userLogin";
@@ -85,22 +77,6 @@
return host;
case "config":
return config;
- case "aiParam":
- return aiParam;
- case "aiPrompt":
- return aiPrompt;
- case "aiDiagnosis":
- return aiDiagnosis;
- case "aiDiagnosisPlan":
- return aiDiagnosisPlan;
- case "aiCallLog":
- return aiCallLog;
- case "aiRoute":
- return aiRoute;
- case "aiToolConfig":
- return aiToolConfig;
- case "aiMcpMount":
- return aiMcpMount;
case "tenant":
return tenant;
case "role":
diff --git a/rsf-admin/src/page/components/AiConsoleLayout.jsx b/rsf-admin/src/page/components/AiConsoleLayout.jsx
deleted file mode 100644
index 0393683..0000000
--- a/rsf-admin/src/page/components/AiConsoleLayout.jsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import React from "react";
-import { alpha } from '@mui/material/styles';
-import { Box, Grid, Paper, Stack, Typography } from '@mui/material';
-
-const pageShellSx = {
- mt: 0.5,
- width: '100%',
-};
-
-export const AiConsoleLayout = ({ title, subtitle, actions, stats, children }) => (
- <Box sx={pageShellSx}>
- <Paper
- elevation={0}
- sx={{
- borderRadius: 3,
- px: { xs: 2, md: 2.5 },
- py: 2,
- overflow: 'hidden',
- backgroundColor: '#fff',
- border: '1px solid #e6ebf2',
- boxShadow: '0 1px 3px rgba(15, 23, 42, 0.06)',
- }}
- >
- <Stack direction={{ xs: 'column', md: 'row' }} justifyContent="space-between" spacing={2}>
- <Box>
- <Typography variant="h6" sx={{ fontWeight: 700, letterSpacing: 0.1 }}>
- {title}
- </Typography>
- {subtitle ? (
- <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5, maxWidth: 760 }}>
- {subtitle}
- </Typography>
- ) : null}
- </Box>
- {actions ? (
- <Stack direction="row" spacing={1} flexWrap="wrap" justifyContent="flex-end">
- {actions}
- </Stack>
- ) : null}
- </Stack>
- {stats?.length ? (
- <Grid container spacing={1.5} sx={{ mt: 1.5 }}>
- {stats.map((item) => (
- <Grid item xs={12} sm={6} md={12 / Math.min(stats.length, 4)} key={item.label}>
- <Box
- sx={{
- minHeight: 76,
- px: 1.5,
- py: 1.25,
- borderRadius: 2,
- border: '1px solid #e7ecf3',
- backgroundColor: '#fafbfd',
- }}
- >
- <Typography variant="caption" color="text.secondary">
- {item.label}
- </Typography>
- <Typography variant="h5" sx={{ mt: 0.5, fontWeight: 700, lineHeight: 1.1 }}>
- {item.value}
- </Typography>
- {item.helper ? (
- <Typography variant="caption" color="text.secondary">
- {item.helper}
- </Typography>
- ) : null}
- </Box>
- </Grid>
- ))}
- </Grid>
- ) : null}
- </Paper>
- <Box sx={{ mt: 1.5 }}>
- {children}
- </Box>
- </Box>
-);
-
-export const AiConsolePanel = ({ title, subtitle, action, children, minHeight }) => (
- <Paper
- elevation={0}
- sx={{
- height: '100%',
- minHeight: minHeight || 240,
- borderRadius: 3,
- border: '1px solid #e6ebf2',
- backgroundColor: '#fff',
- boxShadow: '0 1px 3px rgba(15, 23, 42, 0.06)',
- overflow: 'hidden',
- }}
- >
- <Stack
- direction="row"
- justifyContent="space-between"
- alignItems="flex-start"
- spacing={1}
- sx={{
- px: 2,
- py: 1.5,
- borderBottom: '1px solid #e6edf5',
- backgroundColor: alpha('#fafbfc', 1),
- }}
- >
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
- {title}
- </Typography>
- {subtitle ? (
- <Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.5 }}>
- {subtitle}
- </Typography>
- ) : null}
- </Box>
- {action}
- </Stack>
- <Box sx={{ p: 1.5 }}>
- {children}
- </Box>
- </Paper>
-);
-
-export const aiCardSx = (active) => ({
- p: 1.5,
- borderRadius: 2.5,
- border: active ? '1px solid #d7e2ef' : '1px solid #e5eaf0',
- backgroundColor: '#fff',
- boxShadow: active
- ? '0 2px 8px rgba(15, 23, 42, 0.08)'
- : '0 1px 4px rgba(15, 23, 42, 0.04)',
- transition: 'box-shadow 0.18s ease, border-color 0.18s ease',
- '&:hover': {
- boxShadow: '0 3px 10px rgba(15, 23, 42, 0.08)',
- },
-});
diff --git a/rsf-admin/src/page/system/aiCallLog/AiCallLogEdit.jsx b/rsf-admin/src/page/system/aiCallLog/AiCallLogEdit.jsx
deleted file mode 100644
index dacb4b4..0000000
--- a/rsf-admin/src/page/system/aiCallLog/AiCallLogEdit.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-
-const AiCallLogEdit = () => (
- <Edit actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm toolbar={false}>
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12}>
- <Typography variant="h6" gutterBottom>璋冪敤璇︽儏</Typography>
- <Stack direction='row' gap={2}>
- <TextInput source="routeCode" label="璺敱缂栫爜" disabled />
- <TextInput source="modelCode" label="妯″瀷缂栫爜" disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput source="attemptNo" label="灏濊瘯搴忓彿" disabled />
- <TextInput source="result$" label="缁撴灉" disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput source="spendTime" label="鑰楁椂(ms)" disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="err" label="閿欒淇℃伅" fullWidth multiline minRows={4} disabled />
- </Stack>
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-);
-
-export default AiCallLogEdit;
diff --git a/rsf-admin/src/page/system/aiCallLog/AiCallLogList.jsx b/rsf-admin/src/page/system/aiCallLog/AiCallLogList.jsx
deleted file mode 100644
index 4bf7195..0000000
--- a/rsf-admin/src/page/system/aiCallLog/AiCallLogList.jsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import React, { useEffect, useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useListContext,
- Pagination,
-} from 'react-admin';
-import { Box, Chip, Grid, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-import MyExportButton from '@/page/components/MyExportButton';
-import { DEFAULT_PAGE_SIZE } from '@/config/setting';
-import request from "@/utils/request";
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <TextInput source="modelCode" label="妯″瀷缂栫爜" />,
- <TextInput source="routeCode" label="璺敱缂栫爜" />,
- <SelectInput source="result" label="缁撴灉" choices={[
- { id: '1', name: '鎴愬姛' },
- { id: '0', name: '澶辫触' },
- ]} />,
-];
-
-const resultColor = (result) => Number(result) === 1 ? 'success' : 'error';
-
-const CallLogBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const [stats, setStats] = useState({});
-
- useEffect(() => {
- let mounted = true;
- const fetchStats = async () => {
- try {
- const { data: res } = await request.get('/ai/call-log/stats');
- if (mounted && res?.code === 200) {
- setStats(res.data || {});
- }
- } catch (error) {
- }
- };
- fetchStats();
- return () => {
- mounted = false;
- };
- }, []);
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI璋冪敤鏃ュ織"
- subtitle="鎸夊綋鍓嶇郴缁熷悗鍙伴鏍煎睍绀烘ā鍨嬭皟鐢ㄨ娴嬶紝绐佸嚭鎴愬姛鐜囥�佹渶杩� 24 灏忔椂璋冪敤閲忓拰骞冲潎鑰楁椂锛屼究浜庡揩閫熸帓鏌ヨ矾鐢变笌涓婃父妯″瀷闂銆�"
- stats={[
- { label: '璋冪敤鎬绘暟', value: stats.total || 0 },
- { label: '鎴愬姛鐜�', value: `${Number(stats.successRate || 0).toFixed(1)}%` },
- { label: '骞冲潎鑰楁椂', value: `${stats.avgSpendTime || 0} ms` },
- { label: '杩�24灏忔椂', value: stats.last24hCount || 0 },
- ]}
- >
- <AiConsolePanel
- title="璋冪敤鏄庣粏"
- subtitle={`宸茶娴嬫ā鍨� ${stats.modelCount || 0} 涓紝璺敱 ${stats.routeCount || 0} 鏉°�俙}
- minHeight={420}
- >
- <Grid container spacing={2}>
- {records.map((record) => (
- <Grid item xs={12} md={6} xl={4} key={record.id}>
- <Box sx={aiCardSx(record.result === 1)}>
- <Box display="flex" justifyContent="space-between" gap={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.modelCode || '鏈褰曟ā鍨�'}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- {record.routeCode || '鏈褰曡矾鐢�'} 路 绗� {record.attemptNo || 1} 娆�
- </Typography>
- </Box>
- <Chip size="small" color={resultColor(record.result)} label={record.result$ || '鏈煡'} />
- </Box>
- <Box sx={{ mt: 1.25, display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 1.5 }}>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>鑰楁椂</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.spendTime || 0} ms</Typography>
- </Box>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>璇锋眰鏃堕棿</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.requestTime || '-'}</Typography>
- </Box>
- </Box>
- <Box sx={{ mt: 1.25 }}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>閿欒淇℃伅</Typography>
- <Typography variant="body2" sx={{ mt: 0.5, minHeight: 72, color: '#31465d' }}>
- {record.err || '鏃�'}
- </Typography>
- </Box>
- <Box sx={{ mt: 1.5 }}>
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- </Box>
- </Box>
- </Grid>
- ))}
- </Grid>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiCallLogList = () => (
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiCallLog"}
- filters={filters}
- sort={{ field: "createTime", order: "desc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <SelectColumnsButton preferenceKey='aiCallLog' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <CallLogBoard />
- </List>
-);
-
-export default AiCallLogList;
diff --git a/rsf-admin/src/page/system/aiCallLog/index.jsx b/rsf-admin/src/page/system/aiCallLog/index.jsx
deleted file mode 100644
index 59ebe86..0000000
--- a/rsf-admin/src/page/system/aiCallLog/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiCallLogList from "./AiCallLogList";
-import AiCallLogEdit from "./AiCallLogEdit";
-
-export default {
- list: AiCallLogList,
- edit: AiCallLogEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.modelCode || record.id || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisEdit.jsx b/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisEdit.jsx
deleted file mode 100644
index 8a4a61d..0000000
--- a/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisEdit.jsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
- useNotify,
- useRecordContext,
-} from 'react-admin';
-import { Box, Button, Chip, Grid, Paper, Stack, Typography } from '@mui/material';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import request from "@/utils/request";
-
-const ReportExportButton = () => {
- const record = useRecordContext();
- const notify = useNotify();
-
- if (!record?.id) {
- return null;
- }
-
- const handleExport = async () => {
- try {
- const res = await request.get(`/ai/diagnosis/report/export/${record.id}`, {
- responseType: 'blob',
- });
- const blob = new Blob([res.data], { type: 'text/markdown;charset=utf-8' });
- const link = document.createElement('a');
- link.href = window.URL.createObjectURL(blob);
- link.setAttribute('download', `${record.diagnosisNo || 'ai-diagnosis-report'}.md`);
- document.body.appendChild(link);
- link.click();
- link.remove();
- } catch (error) {
- notify(error.message || '瀵煎嚭澶辫触', { type: 'error' });
- }
- };
-
- return <Button variant="outlined" onClick={handleExport}>瀵煎嚭鎶ュ憡</Button>;
-};
-
-const ToolTracePanel = () => {
- const record = useRecordContext();
-
- if (!record?.toolSummary) {
- return (
- <Typography variant="body2" color="text.secondary">
- 鏆傛棤宸ュ叿杞ㄨ抗鏁版嵁
- </Typography>
- );
- }
-
- let tools = [];
- try {
- const parsed = JSON.parse(record.toolSummary);
- if (Array.isArray(parsed)) {
- tools = parsed;
- }
- } catch (error) {
- }
-
- if (!tools.length) {
- return (
- <TextInput source="toolSummary" label="宸ュ叿鎽樿(JSON)" fullWidth multiline minRows={6} disabled />
- );
- }
-
- return (
- <Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 2 }}>
- {tools.map((item, index) => (
- <Paper key={`${item.toolCode || 'tool'}-${index}`} variant="outlined" sx={{ p: 2, borderRadius: 2 }}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
- {item.toolName || item.toolCode || `宸ュ叿${index + 1}`}
- </Typography>
- <Typography variant="caption" color="text.secondary">
- {(item.mcpToolName || item.toolCode || '-') + (item.mountCode ? ` 路 ${item.mountCode}` : '')}
- </Typography>
- </Box>
- <Chip size="small" label={item.severity || 'INFO'} color={item.severity === 'ERROR' ? 'error' : (item.severity === 'WARN' ? 'warning' : 'primary')} />
- </Stack>
- <Typography variant="body2" sx={{ mt: 1.25, whiteSpace: 'pre-wrap' }}>
- {item.summaryText || '鏃犳憳瑕�'}
- </Typography>
- </Paper>
- ))}
- </Box>
- );
-};
-
-const AiDiagnosisEdit = () => (
- <Edit actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm toolbar={false}>
- <Grid container width={{ xs: '100%', xl: '85%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12}>
- <Typography variant="h6" gutterBottom>璇婃柇姒傝</Typography>
- <Stack direction='row' gap={2} flexWrap="wrap">
- <TextInput source="diagnosisNo" label="璇婃柇缂栧彿" disabled />
- <TextInput source="sceneCode$" label="鍦烘櫙" disabled />
- <TextInput source="modelCode" label="妯″瀷缂栫爜" disabled />
- <TextInput source="result$" label="缁撴灉" disabled />
- <NumberInput source="spendTime" label="鑰楁椂(ms)" disabled />
- </Stack>
- <Stack direction='row' gap={2} sx={{ mt: 1 }}>
- <ReportExportButton />
- </Stack>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="h6" gutterBottom>鎶ュ憡鎽樿</Typography>
- <Stack direction='row' gap={2}>
- <TextInput source="reportTitle" label="鎶ュ憡鏍囬" fullWidth disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="question" label="璇婃柇闂" fullWidth multiline minRows={3} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="executiveSummary" label="闂姒傝堪" fullWidth multiline minRows={4} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="evidenceSummary" label="鍏抽敭璇佹嵁" fullWidth multiline minRows={4} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="actionSummary" label="寤鸿鍔ㄤ綔" fullWidth multiline minRows={4} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="riskSummary" label="椋庨櫓璇勪及" fullWidth multiline minRows={4} disabled />
- </Stack>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="h6" gutterBottom>鍘熷鏁版嵁</Typography>
- <Stack direction='row' gap={2}>
- <TextInput source="conclusion" label="AI缁撹" fullWidth multiline minRows={6} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="reportMarkdown" label="鎶ュ憡Markdown" fullWidth multiline minRows={8} disabled />
- </Stack>
- <Box sx={{ mt: 2 }}>
- <Typography variant="subtitle1" gutterBottom>宸ュ叿鎵ц杞ㄨ抗</Typography>
- <ToolTracePanel />
- </Box>
- <Stack direction='row' gap={2}>
- <TextInput source="err" label="閿欒淇℃伅" fullWidth multiline minRows={3} disabled />
- </Stack>
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-);
-
-export default AiDiagnosisEdit;
diff --git a/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisList.jsx b/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisList.jsx
deleted file mode 100644
index 40845df..0000000
--- a/rsf-admin/src/page/system/aiDiagnosis/AiDiagnosisList.jsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import React from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useListContext,
- Pagination,
-} from 'react-admin';
-import { Box, Chip, Grid, Stack, Typography } from '@mui/material';
-import MyExportButton from '@/page/components/MyExportButton';
-import { DEFAULT_PAGE_SIZE } from '@/config/setting';
-import EmptyData from "@/page/components/EmptyData";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <TextInput source="diagnosisNo" label="璇婃柇缂栧彿" />,
- <TextInput source="modelCode" label="妯″瀷缂栫爜" />,
- <SelectInput source="result" label="缁撴灉" choices={[
- { id: '2', name: '杩愯涓�' },
- { id: '1', name: '鎴愬姛' },
- { id: '0', name: '澶辫触' },
- ]} />,
-];
-
-const resultColor = (result) => {
- if (result === 1) {
- return 'success';
- }
- if (result === 0) {
- return 'error';
- }
- return 'warning';
-};
-
-const DiagnosisBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const successCount = records.filter((item) => item.result === 1).length;
- const failedCount = records.filter((item) => item.result === 0).length;
- const runningCount = records.filter((item) => item.result === 2).length;
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI璇婃柇鎶ュ憡"
- subtitle="璇婃柇鍒楄〃鏀规垚鎶ュ憡鍗$墖瑙嗗浘锛岀獊鍑虹粨璁烘爣棰樸�佸鐞嗙粨鏋滃拰鑰楁椂锛涜鎯呴〉缁х画鎵挎帴瀹屾暣鎶ュ憡涓� Markdown 瀵煎嚭銆�"
- stats={[
- { label: '鎶ュ憡鎬绘暟', value: records.length },
- { label: '鎴愬姛', value: successCount },
- { label: '澶辫触', value: failedCount },
- { label: '杩愯涓�', value: runningCount },
- ]}
- >
- <AiConsolePanel
- title="鎶ュ憡鍒楄〃"
- subtitle="鐐瑰嚮缂栬緫杩涘叆鎶ュ憡璇︽儏椤碉紝鍙煡鐪嬬粨鏋勫寲鎶ュ憡銆佸師濮嬬粨璁哄拰瀵煎嚭 Markdown銆�"
- minHeight={460}
- >
- <Grid container spacing={2}>
- {records.map((record) => (
- <Grid item xs={12} md={6} xl={4} key={record.id}>
- <Box sx={aiCardSx(record.result === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.reportTitle || record.diagnosisNo}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- {record.sceneCode$} 路 {record.modelCode || '鏈褰曟ā鍨�'}
- </Typography>
- </Box>
- <Chip size="small" color={resultColor(record.result)} label={record.result$ || '鏈煡'} />
- </Stack>
- <Typography variant="body2" sx={{ mt: 1.25, minHeight: 72, color: '#31465d' }}>
- {record.executiveSummary || record.question || '鏆傛棤璇婃柇鎽樿'}
- </Typography>
- <Stack direction="row" spacing={2} sx={{ mt: 1.25 }}>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>鑰楁椂</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.spendTime || 0} ms</Typography>
- </Box>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>寮�濮嬫椂闂�</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.startTime || '-'}</Typography>
- </Box>
- </Stack>
- <Box sx={{ mt: 1.5 }}>
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- </Box>
- </Box>
- </Grid>
- ))}
- </Grid>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiDiagnosisList = () => (
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiDiagnosis"}
- filters={filters}
- sort={{ field: "createTime", order: "desc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <SelectColumnsButton preferenceKey='aiDiagnosis' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <DiagnosisBoard />
- </List>
-);
-
-export default AiDiagnosisList;
diff --git a/rsf-admin/src/page/system/aiDiagnosis/index.jsx b/rsf-admin/src/page/system/aiDiagnosis/index.jsx
deleted file mode 100644
index d045a36..0000000
--- a/rsf-admin/src/page/system/aiDiagnosis/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiDiagnosisList from "./AiDiagnosisList";
-import AiDiagnosisEdit from "./AiDiagnosisEdit";
-
-export default {
- list: AiDiagnosisList,
- edit: AiDiagnosisEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.diagnosisNo || record.id || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanCreate.jsx b/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanCreate.jsx
deleted file mode 100644
index 668d67d..0000000
--- a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanCreate.jsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const sceneChoices = [
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
-];
-
-const AiDiagnosisPlanCreate = (props) => {
- const { open, setOpen } = props;
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{
- sceneCode: 'system_diagnose',
- cronExpr: '0 0/30 * * * ?',
- status: 1,
- }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog open={open} onClose={handleClose} fullWidth disableRestoreFocus maxWidth="md">
- <Form>
- <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6}><TextInput source="planName" label="璁″垝鍚嶇О" fullWidth /></Grid>
- <Grid item xs={6}><SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="cronExpr" label="Cron琛ㄨ揪寮�" fullWidth helperText="渚嬪: 0 0/30 * * * ?" /></Grid>
- <Grid item xs={6}><TextInput source="preferredModelCode" label="浼樺厛妯″瀷缂栫爜" fullWidth /></Grid>
- <Grid item xs={12}><TextInput source="prompt" label="宸℃鎻愮ず璇�" fullWidth multiline minRows={5} /></Grid>
- <Grid item xs={6}><StatusSelectInput fullWidth /></Grid>
- <Grid item xs={12}><MemoInput /></Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}>
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiDiagnosisPlanCreate;
diff --git a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanEdit.jsx b/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanEdit.jsx
deleted file mode 100644
index 534c29f..0000000
--- a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanEdit.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const sceneChoices = [
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
-];
-
-const FormToolbar = () => (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
-);
-
-const AiDiagnosisPlanEdit = () => (
- <Edit redirect="list" mutationMode={EDIT_MODE} actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm shouldUnregister warnWhenUnsavedChanges toolbar={<FormToolbar />} mode="onTouched">
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>涓昏</Typography>
- <Stack direction='row' gap={2}>
- <TextInput source="planName" label="璁″垝鍚嶇О" fullWidth />
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="cronExpr" label="Cron琛ㄨ揪寮�" fullWidth />
- <TextInput source="preferredModelCode" label="浼樺厛妯″瀷缂栫爜" fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="nextRunTime$" label="涓嬫杩愯鏃堕棿" disabled fullWidth />
- <TextInput source="lastRunTime$" label="涓婃杩愯鏃堕棿" disabled fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="lastResult$" label="鏈�杩戠粨鏋�" disabled fullWidth />
- <TextInput source="lastDiagnosisId" label="鏈�杩戣瘖鏂璉D" disabled fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="lastMessage" label="鏈�杩戞秷鎭�" fullWidth multiline minRows={3} disabled />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="prompt" label="宸℃鎻愮ず璇�" fullWidth multiline minRows={6} />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>閫氱敤</Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-)
-
-export default AiDiagnosisPlanEdit;
diff --git a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanList.jsx b/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanList.jsx
deleted file mode 100644
index 0a685bb..0000000
--- a/rsf-admin/src/page/system/aiDiagnosisPlan/AiDiagnosisPlanList.jsx
+++ /dev/null
@@ -1,175 +0,0 @@
-import React, { useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useListContext,
- Pagination,
- useNotify,
- useRefresh,
- DeleteButton,
-} from 'react-admin';
-import { Box, Button, Chip, Grid, Stack, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { DEFAULT_PAGE_SIZE, OPERATE_MODE } from '@/config/setting';
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-import AiDiagnosisPlanCreate from "./AiDiagnosisPlanCreate";
-import request from "@/utils/request";
-
-const sceneChoices = [
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
-];
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} />,
- <TextInput source="planName" label="璁″垝鍚嶇О" />,
- <SelectInput source="status" label="鐘舵��" choices={[
- { id: '1', name: '鍚敤' },
- { id: '0', name: '鍋滅敤' },
- ]} />,
-];
-
-const RunPlanButton = ({ record }) => {
- const notify = useNotify();
- const refresh = useRefresh();
-
- const handleRun = async () => {
- try {
- const { data } = await request.post('/ai/diagnosis-plan/run', { id: record.id });
- if (data?.code !== 200) {
- throw new Error(data?.msg || '鎵ц澶辫触');
- }
- notify('宸叉彁浜ゆ墽琛�');
- refresh();
- } catch (error) {
- notify(error.message || '鎵ц澶辫触', { type: 'error' });
- }
- };
-
- return (
- <Button size="small" variant="outlined" onClick={handleRun} disabled={record.runningFlag === 1}>
- 绔嬪嵆鎵ц
- </Button>
- );
-};
-
-const PlanBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const enabledCount = records.filter((item) => item.status === 1).length;
- const runningCount = records.filter((item) => item.runningFlag === 1).length;
- const successCount = records.filter((item) => item.lastResult === 1).length;
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI宸℃璁″垝"
- subtitle="鎶婁竴閿瘖鏂墿鎴愬彲璁″垝鎵ц鐨勫贰妫�浠诲姟锛屾寜 Cron 瀹氭椂瑙﹀彂锛岃嚜鍔ㄧ敓鎴愭柊鐨勮瘖鏂褰曞拰鎶ュ憡銆�"
- stats={[
- { label: '璁″垝鎬绘暟', value: records.length },
- { label: '鍚敤', value: enabledCount },
- { label: '杩愯涓�', value: runningCount },
- { label: '鏈�杩戞垚鍔�', value: successCount },
- ]}
- >
- <AiConsolePanel
- title="璁″垝鍒楄〃"
- subtitle="璁″垝鎵ц鏃朵細鑷姩鍐欏叆璇婃柇璁板綍鍜岃皟鐢ㄦ棩蹇楋紱鍋滅敤鍚庝笉浼氱户缁Е鍙戯紝浣嗕粛鍙墜鍔ㄦ墽琛屻��"
- minHeight={420}
- >
- <Grid container spacing={2}>
- {records.map((record) => (
- <Grid item xs={12} md={6} xl={4} key={record.id}>
- <Box sx={aiCardSx(record.status === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.planName || '鏈懡鍚嶈鍒�'}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- {record.sceneCode$} 路 {record.cronExpr}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={record.status === 1 ? 'success' : 'default'} label={record.status === 1 ? '鍚敤' : '鍋滅敤'} />
- <Chip size="small" color={record.lastResult === 1 ? 'success' : (record.lastResult === 0 ? 'error' : 'warning')} label={record.lastResult$} />
- </Stack>
- </Stack>
- <Stack direction="row" spacing={2} sx={{ mt: 1.25 }}>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>浼樺厛妯″瀷</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.preferredModelCode || '鑷姩璺敱'}</Typography>
- </Box>
- <Box>
- <Typography variant="caption" sx={{ color: '#70839a' }}>涓嬫杩愯</Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>{record.nextRunTime$ || '-'}</Typography>
- </Box>
- </Stack>
- <Box sx={{ mt: 1.25 }}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>鏈�杩戞秷鎭�</Typography>
- <Typography variant="body2" sx={{ mt: 0.5, minHeight: 72, color: '#31465d' }}>
- {record.lastMessage || record.prompt || '鏆傛棤鎵ц璁板綍'}
- </Typography>
- </Box>
- <Stack direction="row" spacing={1} sx={{ mt: 1.5 }} flexWrap="wrap">
- <RunPlanButton record={record} />
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- <DeleteButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} mutationMode={OPERATE_MODE} />
- </Stack>
- </Box>
- </Grid>
- ))}
- </Grid>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiDiagnosisPlanList = () => {
- const [createDialog, setCreateDialog] = useState(false);
-
- return (
- <Box display="flex" sx={{ width: '100%' }}>
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiDiagnosisPlan"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
- filters={filters}
- sort={{ field: "nextRunTime", order: "asc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiDiagnosisPlan' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <PlanBoard />
- </List>
- <AiDiagnosisPlanCreate open={createDialog} setOpen={setCreateDialog} />
- </Box>
- )
-}
-
-export default AiDiagnosisPlanList;
diff --git a/rsf-admin/src/page/system/aiDiagnosisPlan/index.jsx b/rsf-admin/src/page/system/aiDiagnosisPlan/index.jsx
deleted file mode 100644
index 1ede8e8..0000000
--- a/rsf-admin/src/page/system/aiDiagnosisPlan/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiDiagnosisPlanList from "./AiDiagnosisPlanList";
-import AiDiagnosisPlanEdit from "./AiDiagnosisPlanEdit";
-
-export default {
- list: AiDiagnosisPlanList,
- edit: AiDiagnosisPlanEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.planName || record.cronExpr || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountCreate.jsx b/rsf-admin/src/page/system/aiMcpMount/AiMcpMountCreate.jsx
deleted file mode 100644
index e00dc17..0000000
--- a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountCreate.jsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const transportChoices = [
- { id: 'INTERNAL', name: '鍐呴儴宸ュ叿闆�' },
- { id: 'HTTP', name: 'Streamable HTTP' },
- { id: 'SSE', name: 'SSE MCP' },
-];
-
-const enabledChoices = [
- { id: 1, name: '鍚敤' },
- { id: 0, name: '鍋滅敤' },
-];
-
-const AiMcpMountCreate = (props) => {
- const { open, setOpen } = props;
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{ transportType: 'INTERNAL', enabledFlag: 1, timeoutMs: 10000, status: 1 }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog open={open} onClose={handleClose} fullWidth disableRestoreFocus maxWidth="md">
- <Form>
- <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6}><TextInput source="name" label="鎸傝浇鍚嶇О" fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="mountCode" label="鎸傝浇缂栫爜" fullWidth /></Grid>
- <Grid item xs={6}><SelectInput source="transportType" label="浼犺緭绫诲瀷" choices={transportChoices} fullWidth /></Grid>
- <Grid item xs={6}><SelectInput source="enabledFlag" label="鍚敤" choices={enabledChoices} fullWidth /></Grid>
- <Grid item xs={6}><NumberInput source="timeoutMs" label="瓒呮椂姣" fullWidth /></Grid>
- <Grid item xs={6}><StatusSelectInput fullWidth /></Grid>
- <Grid item xs={12}><TextInput source="url" label="鍦板潃" fullWidth helperText="鍐呴儴宸ュ叿闆嗕細鑷姩鍐欏叆 /ai/mcp锛涘閮ㄦ寕杞藉彲濉啓杩滅▼ Streamable HTTP 鎴� SSE MCP 鍦板潃" /></Grid>
- <Grid item xs={12}><MemoInput /></Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}>
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiMcpMountCreate;
diff --git a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountEdit.jsx b/rsf-admin/src/page/system/aiMcpMount/AiMcpMountEdit.jsx
deleted file mode 100644
index f9337b6..0000000
--- a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountEdit.jsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const transportChoices = [
- { id: 'INTERNAL', name: '鍐呴儴宸ュ叿闆�' },
- { id: 'HTTP', name: 'Streamable HTTP' },
- { id: 'SSE', name: 'SSE MCP' },
-];
-
-const enabledChoices = [
- { id: 1, name: '鍚敤' },
- { id: 0, name: '鍋滅敤' },
-];
-
-const FormToolbar = () => (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
-);
-
-const AiMcpMountEdit = () => (
- <Edit redirect="list" mutationMode={EDIT_MODE} actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm shouldUnregister warnWhenUnsavedChanges toolbar={<FormToolbar />} mode="onTouched">
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>涓昏</Typography>
- <Stack direction='row' gap={2}>
- <TextInput source="name" label="鎸傝浇鍚嶇О" fullWidth />
- <TextInput source="mountCode" label="鎸傝浇缂栫爜" fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <SelectInput source="transportType" label="浼犺緭绫诲瀷" choices={transportChoices} fullWidth />
- <SelectInput source="enabledFlag" label="鍚敤" choices={enabledChoices} fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="url" label="鍦板潃" fullWidth />
- <NumberInput source="timeoutMs" label="瓒呮椂姣" fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="lastTestResult$" label="鏈�杩戞祴璇曠粨鏋�" disabled fullWidth />
- <TextInput source="lastTestTime$" label="鏈�杩戞祴璇曟椂闂�" disabled fullWidth />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="lastToolCount" label="鏈�杩戝伐鍏锋暟" disabled fullWidth />
- <TextInput source="lastTestMessage" label="鏈�杩戞祴璇曟秷鎭�" disabled fullWidth multiline minRows={2} />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>閫氱敤</Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-)
-
-export default AiMcpMountEdit;
diff --git a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountList.jsx b/rsf-admin/src/page/system/aiMcpMount/AiMcpMountList.jsx
deleted file mode 100644
index aa55328..0000000
--- a/rsf-admin/src/page/system/aiMcpMount/AiMcpMountList.jsx
+++ /dev/null
@@ -1,618 +0,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import {
- Accordion,
- AccordionDetails,
- AccordionSummary,
- Alert,
- Box,
- Button,
- Chip,
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- FormControl,
- Grid,
- InputLabel,
- MenuItem,
- Paper,
- Select,
- Stack,
- Switch,
- TextField,
- Typography,
-} from "@mui/material";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import AddIcon from "@mui/icons-material/Add";
-import EditIcon from "@mui/icons-material/Edit";
-import StorageIcon from "@mui/icons-material/Storage";
-import HubIcon from "@mui/icons-material/Hub";
-import BuildIcon from "@mui/icons-material/Build";
-import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
-import { useNotify } from "react-admin";
-import request from "@/utils/request";
-import EmptyData from "@/page/components/EmptyData";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const usageChoices = [
- { id: "DIAGNOSE_ONLY", name: "浠呰瘖鏂娇鐢�" },
- { id: "CHAT_AND_DIAGNOSE", name: "鑱婂ぉ涓庤瘖鏂兘鍙敤" },
- { id: "DISABLED", name: "绂佺敤" },
-];
-
-const transportChoices = [
- { id: "AUTO", name: "鑷姩璇嗗埆" },
- { id: "HTTP", name: "Streamable HTTP" },
- { id: "SSE", name: "SSE" },
-];
-
-const authChoices = [
- { id: "NONE", name: "鏃犺璇�" },
- { id: "BEARER", name: "Bearer Token" },
- { id: "API_KEY", name: "X-API-Key" },
-];
-
-const defaultServiceForm = {
- id: null,
- name: "",
- url: "",
- transportType: "AUTO",
- authType: "NONE",
- authValue: "",
- usageScope: "DIAGNOSE_ONLY",
- timeoutMs: 10000,
- enabledFlag: 1,
- memo: "",
-};
-
-const usageLabel = (usageScope) => {
- const matched = usageChoices.find((item) => item.id === usageScope);
- return matched ? matched.name : "浠呰瘖鏂娇鐢�";
-};
-
-const transportLabel = (transportType) => {
- const matched = transportChoices.find((item) => item.id === transportType);
- return matched ? matched.name : transportType || "鑷姩璇嗗埆";
-};
-
-const BuiltInToolCard = ({ tool, onSave }) => {
- const notify = useNotify();
- const [usageScope, setUsageScope] = useState(tool.usageScope || "DIAGNOSE_ONLY");
- const [priority, setPriority] = useState(tool.priority || 10);
- const [toolPrompt, setToolPrompt] = useState(tool.toolPrompt || "");
- const [saving, setSaving] = useState(false);
-
- useEffect(() => {
- setUsageScope(tool.usageScope || "DIAGNOSE_ONLY");
- setPriority(tool.priority || 10);
- setToolPrompt(tool.toolPrompt || "");
- }, [tool]);
-
- const handleSave = async () => {
- try {
- setSaving(true);
- const { data: res } = await request.post("/ai/mcp/console/builtin-tool/save", {
- toolCode: tool.toolCode,
- toolName: tool.toolName,
- priority: priority || 10,
- toolPrompt,
- usageScope,
- });
- if (res?.code !== 200) {
- throw new Error(res?.msg || "淇濆瓨澶辫触");
- }
- notify("鍐呯疆宸ュ叿绛栫暐宸叉洿鏂�");
- onSave?.(res?.data || []);
- } catch (error) {
- notify(error.message || "淇濆瓨澶辫触", { type: "error" });
- } finally {
- setSaving(false);
- }
- };
-
- const enabled = usageScope !== "DISABLED";
-
- return (
- <Box sx={aiCardSx(enabled)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: "#284059" }}>
- {tool.toolName}
- </Typography>
- <Typography variant="caption" sx={{ color: "#8093a8" }}>
- {tool.toolCode}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={enabled ? "success" : "default"} label={enabled ? "鍚敤" : "鍋滅敤"} />
- <Chip size="small" color="primary" label={usageLabel(usageScope)} />
- </Stack>
- </Stack>
- <Typography variant="body2" sx={{ mt: 1.5, minHeight: 44, color: "#31465d" }}>
- {tool.description || "绯荤粺鍐呯疆宸ュ叿锛岄粯璁ょ敱骞冲彴鎵樼銆�"}
- </Typography>
- <FormControl size="small" fullWidth sx={{ mt: 1.5 }}>
- <InputLabel>鐢ㄩ�旈璁�</InputLabel>
- <Select value={usageScope} label="鐢ㄩ�旈璁�" onChange={(event) => setUsageScope(event.target.value)}>
- {usageChoices.map((item) => (
- <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
- <Accordion elevation={0} disableGutters sx={{ mt: 1.5, borderRadius: 2, border: "1px solid #dbe5f1" }}>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography variant="body2" sx={{ fontWeight: 600 }}>楂樼骇璁剧疆</Typography>
- </AccordionSummary>
- <AccordionDetails>
- <Stack spacing={1.5}>
- <TextField
- label="鎵ц浼樺厛绾�"
- size="small"
- type="number"
- value={priority}
- onChange={(event) => setPriority(Number(event.target.value || 10))}
- />
- <TextField
- label="闄勫姞瑙勫垯"
- size="small"
- value={toolPrompt}
- multiline
- minRows={3}
- onChange={(event) => setToolPrompt(event.target.value)}
- helperText="杩欓噷鍙湪闇�瑕佽鐩栭粯璁ゅ伐鍏疯鏄庢椂濉啓銆�"
- />
- </Stack>
- </AccordionDetails>
- </Accordion>
- <Stack direction="row" justifyContent="flex-end" sx={{ mt: 1.5 }}>
- <Button variant="contained" size="small" onClick={handleSave} disabled={saving}>
- {saving ? "淇濆瓨涓�..." : "淇濆瓨"}
- </Button>
- </Stack>
- </Box>
- );
-};
-
-const ServiceDialog = ({ open, record, onClose, onSaved }) => {
- const notify = useNotify();
- const [form, setForm] = useState(defaultServiceForm);
- const [saving, setSaving] = useState(false);
-
- useEffect(() => {
- if (open) {
- setForm(record ? {
- ...defaultServiceForm,
- ...record,
- authType: record.authType || "NONE",
- transportType: record.transportType || "AUTO",
- usageScope: record.usageScope || "DIAGNOSE_ONLY",
- timeoutMs: record.timeoutMs || 10000,
- } : defaultServiceForm);
- }
- }, [open, record]);
-
- const handleChange = (field, value) => {
- setForm((prev) => ({ ...prev, [field]: value }));
- };
-
- const handleSave = async () => {
- try {
- setSaving(true);
- const payload = { ...form };
- if (payload.authType === "NONE") {
- payload.authValue = "";
- }
- const { data: res } = await request.post("/ai/mcp/console/service/save", payload);
- if (res?.code !== 200) {
- throw new Error(res?.msg || "淇濆瓨澶辫触");
- }
- notify("澶栭儴 MCP 鏈嶅姟宸蹭繚瀛�");
- onSaved?.(res?.data);
- } catch (error) {
- notify(error.message || "淇濆瓨澶辫触", { type: "error" });
- } finally {
- setSaving(false);
- }
- };
-
- return (
- <Dialog open={open} onClose={onClose} fullWidth maxWidth="md">
- <DialogTitle>{form.id ? "缂栬緫澶栭儴 MCP 鏈嶅姟" : "鏂板澶栭儴 MCP 鏈嶅姟"}</DialogTitle>
- <DialogContent dividers>
- <Grid container spacing={2} sx={{ mt: 0.25 }}>
- <Grid item xs={12} md={6}>
- <TextField label="鏈嶅姟鍚嶇О" fullWidth size="small" value={form.name} onChange={(event) => handleChange("name", event.target.value)} />
- </Grid>
- <Grid item xs={12} md={6}>
- <FormControl fullWidth size="small">
- <InputLabel>杩炴帴鏂瑰紡</InputLabel>
- <Select value={form.transportType} label="杩炴帴鏂瑰紡" onChange={(event) => handleChange("transportType", event.target.value)}>
- {transportChoices.map((item) => (
- <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
- </Grid>
- <Grid item xs={12}>
- <TextField
- label="鏈嶅姟鍦板潃"
- fullWidth
- size="small"
- value={form.url}
- onChange={(event) => handleChange("url", event.target.value)}
- helperText="鐩存帴濉啓杩滅▼ MCP 鏈嶅姟鍦板潃锛涚郴缁熶細浼樺厛鎸夎嚜鍔ㄨ瘑鍒崗璁繛鎺ャ��"
- />
- </Grid>
- <Grid item xs={12} md={4}>
- <FormControl fullWidth size="small">
- <InputLabel>璁よ瘉鏂瑰紡</InputLabel>
- <Select value={form.authType} label="璁よ瘉鏂瑰紡" onChange={(event) => handleChange("authType", event.target.value)}>
- {authChoices.map((item) => (
- <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
- </Grid>
- <Grid item xs={12} md={8}>
- <TextField
- label="璁よ瘉淇℃伅"
- fullWidth
- size="small"
- type={form.authType === "NONE" ? "text" : "password"}
- value={form.authValue}
- disabled={form.authType === "NONE"}
- onChange={(event) => handleChange("authValue", event.target.value)}
- />
- </Grid>
- <Grid item xs={12} md={6}>
- <FormControl fullWidth size="small">
- <InputLabel>鐢ㄩ�旈璁�</InputLabel>
- <Select value={form.usageScope} label="鐢ㄩ�旈璁�" onChange={(event) => handleChange("usageScope", event.target.value)}>
- {usageChoices.filter((item) => item.id !== "DISABLED").map((item) => (
- <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
- ))}
- </Select>
- </FormControl>
- </Grid>
- <Grid item xs={12} md={3}>
- <TextField
- label="瓒呮椂姣"
- type="number"
- size="small"
- fullWidth
- value={form.timeoutMs}
- onChange={(event) => handleChange("timeoutMs", Number(event.target.value || 10000))}
- />
- </Grid>
- <Grid item xs={12} md={3}>
- <Stack direction="row" alignItems="center" spacing={1} sx={{ height: "100%" }}>
- <Switch checked={Number(form.enabledFlag) === 1} onChange={(event) => handleChange("enabledFlag", event.target.checked ? 1 : 0)} />
- <Typography variant="body2">鍚敤鏈嶅姟</Typography>
- </Stack>
- </Grid>
- <Grid item xs={12}>
- <TextField
- label="澶囨敞"
- fullWidth
- size="small"
- value={form.memo}
- multiline
- minRows={2}
- onChange={(event) => handleChange("memo", event.target.value)}
- />
- </Grid>
- </Grid>
- </DialogContent>
- <DialogActions>
- <Button onClick={onClose}>鍙栨秷</Button>
- <Button variant="contained" onClick={handleSave} disabled={saving}>{saving ? "淇濆瓨涓�..." : "淇濆瓨"}</Button>
- </DialogActions>
- </Dialog>
- );
-};
-
-const ExternalServiceCard = ({ service, onEdit, onTest, onInspect, onRemove }) => (
- <Box sx={aiCardSx(service.enabledFlag === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: "#284059" }}>
- {service.name}
- </Typography>
- <Typography variant="caption" sx={{ color: "#8093a8" }}>
- {transportLabel(service.transportType)} 路 {usageLabel(service.usageScope)}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={service.enabledFlag === 1 ? "success" : "default"} label={service.enabledFlag === 1 ? "鍚敤" : "鍋滅敤"} />
- <Chip size="small" color={service.lastTestResult === 1 ? "success" : "default"} label={service.lastTestResult$ || "鏈祴璇�"} />
- </Stack>
- </Stack>
- <Typography variant="body2" sx={{ mt: 1.25, color: "#31465d", minHeight: 42 }}>
- {service.url}
- </Typography>
- {service.lastTestMessage ? (
- <Alert severity={service.lastTestResult === 1 ? "success" : "warning"} sx={{ mt: 1.25 }}>
- {service.lastTestMessage}
- </Alert>
- ) : null}
- <Typography variant="caption" display="block" sx={{ mt: 1.25, color: "#70839a" }}>
- {service.lastTestTime ? `鏈�杩戞祴璇�: ${service.lastTestTime}` : "鏈�杩戞祴璇�: -"}
- </Typography>
- <Typography variant="caption" display="block" sx={{ color: "#70839a" }}>
- {service.lastToolCount !== null && service.lastToolCount !== undefined ? `宸插彂鐜板伐鍏�: ${service.lastToolCount}` : "宸插彂鐜板伐鍏�: -"}
- </Typography>
- <Stack direction="row" spacing={1} flexWrap="wrap" sx={{ mt: 1.5 }}>
- <Button size="small" variant="outlined" onClick={() => onInspect(service)}>鏌ョ湅宸ュ叿</Button>
- <Button size="small" variant="outlined" onClick={() => onTest(service)}>杩炴帴娴嬭瘯</Button>
- <Button size="small" startIcon={<EditIcon />} onClick={() => onEdit(service)}>缂栬緫</Button>
- <Button size="small" color="error" startIcon={<DeleteOutlineIcon />} onClick={() => onRemove(service)}>鍒犻櫎</Button>
- </Stack>
- </Box>
-);
-
-const ExternalToolsPanel = ({ selectedService, tools, preview, onPreview }) => (
- <Grid container spacing={2}>
- <Grid item xs={12} lg={7}>
- <AiConsolePanel
- title="澶栭儴鏈嶅姟宸ュ叿鐩綍"
- subtitle={selectedService ? `褰撳墠鏈嶅姟锛�${selectedService.name}` : "閫変腑浠讳竴澶栭儴鏈嶅姟鍚庯紝浼氬睍绀烘渶鏂板彂鐜扮殑宸ュ叿鐩綍銆�"}
- minHeight={320}
- >
- <Box sx={{ display: "grid", gap: 1.5 }}>
- {tools.map((tool) => (
- <Paper key={tool.mcpToolName} variant="outlined" sx={{ p: 1.5, borderRadius: 2 }}>
- <Stack direction="row" justifyContent="space-between" spacing={1}>
- <Box>
- <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
- {tool.toolName || tool.mcpToolName}
- </Typography>
- <Typography variant="caption" color="text.secondary">
- {tool.mcpToolName}
- </Typography>
- </Box>
- <Button size="small" variant="text" onClick={() => onPreview(tool)}>鎵ц棰勮</Button>
- </Stack>
- <Typography variant="body2" sx={{ mt: 1 }}>
- {tool.description || tool.toolPrompt || "鏆傛棤璇存槑"}
- </Typography>
- </Paper>
- ))}
- {!tools.length ? <EmptyData /> : null}
- </Box>
- </AiConsolePanel>
- </Grid>
- <Grid item xs={12} lg={5}>
- <AiConsolePanel
- title="宸ュ叿棰勮"
- subtitle="鐢ㄤ簬蹇�熺‘璁よ繙绋嬪伐鍏锋槸鍚︾湡鐨勮兘杩斿洖缁撴灉銆�"
- minHeight={320}
- >
- {preview ? (
- <Box>
- <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
- {preview.toolName || preview.mcpToolName || preview.toolCode}
- </Typography>
- <Typography variant="caption" color="text.secondary">
- {preview.severity || "INFO"}
- </Typography>
- <Typography variant="body2" sx={{ mt: 1.5, whiteSpace: "pre-wrap" }}>
- {preview.summaryText || "鏆傛棤鎽樿"}
- </Typography>
- </Box>
- ) : (
- <EmptyData />
- )}
- </AiConsolePanel>
- </Grid>
- </Grid>
-);
-
-const AiMcpMountList = () => {
- const notify = useNotify();
- const [overview, setOverview] = useState({ builtInMount: null, builtInTools: [], externalServices: [] });
- const [serviceDialogOpen, setServiceDialogOpen] = useState(false);
- const [editingService, setEditingService] = useState(null);
- const [selectedService, setSelectedService] = useState(null);
- const [serviceTools, setServiceTools] = useState([]);
- const [preview, setPreview] = useState(null);
-
- const loadOverview = async () => {
- try {
- const { data: res } = await request.get("/ai/mcp/console/overview");
- if (res?.code !== 200) {
- throw new Error(res?.msg || "鍔犺浇澶辫触");
- }
- setOverview(res.data || { builtInMount: null, builtInTools: [], externalServices: [] });
- } catch (error) {
- notify(error.message || "鍔犺浇澶辫触", { type: "error" });
- }
- };
-
- useEffect(() => {
- loadOverview();
- }, []);
-
- const stats = useMemo(() => {
- const builtInEnabled = (overview.builtInTools || []).filter((item) => item.usageScope !== "DISABLED").length;
- const externalEnabled = (overview.externalServices || []).filter((item) => item.enabledFlag === 1).length;
- const successCount = (overview.externalServices || []).filter((item) => item.lastTestResult === 1).length;
- const discoveredTools = (overview.externalServices || []).reduce((sum, item) => sum + (item.lastToolCount || 0), 0);
- return [
- { label: "鍐呯疆宸ュ叿鍚敤", value: builtInEnabled },
- { label: "澶栭儴鏈嶅姟", value: (overview.externalServices || []).length },
- { label: "鏈�杩戞祴璇曟垚鍔�", value: successCount },
- { label: "鍙戠幇澶栭儴宸ュ叿", value: discoveredTools },
- ];
- }, [overview]);
-
- const handleBuiltInSaved = (tools) => {
- setOverview((prev) => ({ ...prev, builtInTools: tools }));
- };
-
- const handleServiceSaved = async () => {
- setServiceDialogOpen(false);
- setEditingService(null);
- await loadOverview();
- };
-
- const handleTestService = async (service) => {
- try {
- const { data: res } = await request.post("/ai/mcp/console/service/test", { id: service.id }, { timeout: (service.timeoutMs || 10000) + 5000 });
- if (res?.code !== 200) {
- throw new Error(res?.msg || "娴嬭瘯澶辫触");
- }
- notify(res?.data?.message || "杩炴帴娴嬭瘯瀹屾垚");
- await loadOverview();
- setSelectedService(res?.data?.service || service);
- setServiceTools(res?.data?.tools || []);
- setPreview(null);
- } catch (error) {
- notify(error.message || "娴嬭瘯澶辫触", { type: "error" });
- }
- };
-
- const handleInspectService = async (service) => {
- try {
- const { data: res } = await request.get("/ai/mcp/mount/toolList", { params: { mountId: service.id } });
- if (res?.code !== 200) {
- throw new Error(res?.msg || "鍔犺浇宸ュ叿澶辫触");
- }
- setSelectedService(service);
- setServiceTools(res.data || []);
- setPreview(null);
- } catch (error) {
- notify(error.message || "鍔犺浇宸ュ叿澶辫触", { type: "error" });
- }
- };
-
- const handlePreviewTool = async (tool) => {
- try {
- const { data: res } = await request.post("/ai/mcp/mount/toolPreview", {
- mountCode: tool.mountCode,
- toolCode: tool.toolCode,
- sceneCode: tool.sceneCode,
- question: "璇疯繑鍥炶宸ュ叿褰撳墠鐨勬憳瑕侀瑙堢粨鏋�",
- });
- if (res?.code !== 200) {
- throw new Error(res?.msg || "棰勮澶辫触");
- }
- setPreview(res.data);
- } catch (error) {
- notify(error.message || "棰勮澶辫触", { type: "error" });
- }
- };
-
- const handleRemoveService = async (service) => {
- try {
- const { data: res } = await request.post(`/ai/mcp/console/service/remove/${service.id}`);
- if (res?.code !== 200) {
- throw new Error(res?.msg || "鍒犻櫎澶辫触");
- }
- notify("鏈嶅姟宸插垹闄�");
- if (selectedService?.id === service.id) {
- setSelectedService(null);
- setServiceTools([]);
- setPreview(null);
- }
- await loadOverview();
- } catch (error) {
- notify(error.message || "鍒犻櫎澶辫触", { type: "error" });
- }
- };
-
- return (
- <Box sx={{ width: "100%" }}>
- <AiConsoleLayout
- title="MCP涓績"
- subtitle="鍐呯疆宸ュ叿鐢辩郴缁熻嚜鍔ㄦ墭绠★紱澶栭儴 MCP 鏈嶅姟鍙繚鐣欐帴鍏ュ湴鍧�銆佽璇佸拰鐢ㄩ�旈璁撅紝澶嶆潅鍗忚缁嗚妭鐢辩郴缁熻嚜鍔ㄥ鐞嗐��"
- stats={stats}
- >
- <AiConsolePanel
- title="鍐呯疆宸ュ叿"
- subtitle="杩欎簺宸ュ叿鐩存帴杩炴帴褰撳墠 WMS 鍐呴儴鏁版嵁锛岄粯璁ゅ紑绠卞嵆鐢紝涓嶉渶瑕佹墜鍔ㄩ厤缃寕杞藉湴鍧�鎴栧崗璁��"
- action={(
- <Chip
- icon={<StorageIcon />}
- label={overview.builtInMount ? "绯荤粺鎵樼" : "寰呭垵濮嬪寲"}
- color={overview.builtInMount ? "success" : "default"}
- variant="outlined"
- />
- )}
- minHeight={260}
- >
- <Grid container spacing={2}>
- {(overview.builtInTools || []).map((tool) => (
- <Grid item xs={12} md={6} xl={4} key={tool.toolCode}>
- <BuiltInToolCard tool={tool} onSave={handleBuiltInSaved} />
- </Grid>
- ))}
- {!overview?.builtInTools?.length ? <EmptyData /> : null}
- </Grid>
- </AiConsolePanel>
-
- <Box sx={{ mt: 1.5 }}>
- <AiConsolePanel
- title="澶栭儴 MCP 鏈嶅姟"
- subtitle="榛樿鍙渶瑕佸綍鍏ユ湇鍔″悕绉般�佸湴鍧�鍜岀敤閫斻�傜郴缁熶細浼樺厛鑷姩璇嗗埆鍗忚锛岃繛鎺ユ垚鍔熷悗鍐嶅睍绀哄彂鐜板埌鐨勫伐鍏枫��"
- action={(
- <Button variant="contained" startIcon={<AddIcon />} onClick={() => { setEditingService(null); setServiceDialogOpen(true); }}>
- 鏂板鏈嶅姟
- </Button>
- )}
- minHeight={280}
- >
- <Grid container spacing={2}>
- {(overview.externalServices || []).map((service) => (
- <Grid item xs={12} md={6} xl={4} key={service.id}>
- <ExternalServiceCard
- service={service}
- onEdit={(item) => { setEditingService(item); setServiceDialogOpen(true); }}
- onTest={handleTestService}
- onInspect={handleInspectService}
- onRemove={handleRemoveService}
- />
- </Grid>
- ))}
- {!overview?.externalServices?.length ? <EmptyData /> : null}
- </Grid>
- </AiConsolePanel>
- </Box>
-
- <Box sx={{ mt: 1.5 }}>
- <AiConsolePanel
- title="宸ュ叿浣跨敤绛栫暐"
- subtitle="鍐呯疆宸ュ叿鎸夌敤閫旈璁捐嚜鍔ㄥ弬涓庤亰澶╂垨璇婃柇锛涘閮ㄦ湇鍔″湪杩炴帴鎴愬姛鍚庡彲浠ユ煡鐪嬪叾瀹為檯宸ュ叿鐩綍骞跺仛璋冪敤棰勮銆�"
- minHeight={120}
- >
- <Stack direction="row" spacing={1} flexWrap="wrap">
- <Chip icon={<BuildIcon />} label="鍐呯疆宸ュ叿榛樿鑷姩鍙備笌璇婃柇" color="primary" variant="outlined" />
- <Chip icon={<HubIcon />} label="澶栭儴鏈嶅姟榛樿鎸夌敤閫旈璁惧弬涓庤繍琛屾椂宸ュ叿閫夋嫨" color="primary" variant="outlined" />
- <Chip label="楂樼骇瑙勫垯宸叉姌鍙犲埌鍚勫崱鐗囧唴閮�" variant="outlined" />
- </Stack>
- </AiConsolePanel>
- </Box>
-
- <Box sx={{ mt: 1.5 }}>
- <ExternalToolsPanel
- selectedService={selectedService}
- tools={serviceTools}
- preview={preview}
- onPreview={handlePreviewTool}
- />
- </Box>
- </AiConsoleLayout>
-
- <ServiceDialog
- open={serviceDialogOpen}
- record={editingService}
- onClose={() => {
- setServiceDialogOpen(false);
- setEditingService(null);
- }}
- onSaved={handleServiceSaved}
- />
- </Box>
- );
-};
-
-export default AiMcpMountList;
diff --git a/rsf-admin/src/page/system/aiMcpMount/index.jsx b/rsf-admin/src/page/system/aiMcpMount/index.jsx
deleted file mode 100644
index 5a75c39..0000000
--- a/rsf-admin/src/page/system/aiMcpMount/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiMcpMountList from "./AiMcpMountList";
-import AiMcpMountEdit from "./AiMcpMountEdit";
-
-export default {
- list: AiMcpMountList,
- edit: AiMcpMountEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.name || record.mountCode || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiParam/AiParamCreate.jsx b/rsf-admin/src/page/system/aiParam/AiParamCreate.jsx
deleted file mode 100644
index 15ed6ac..0000000
--- a/rsf-admin/src/page/system/aiParam/AiParamCreate.jsx
+++ /dev/null
@@ -1,175 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Stack,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const yesNoChoices = [
- { id: 1, name: 'common.enums.true' },
- { id: 0, name: 'common.enums.false' },
-];
-
-const providerChoices = [
- { id: 'openai', name: 'OpenAI Compatible' },
- { id: 'mock', name: 'Mock' },
-];
-
-const AiParamCreate = (props) => {
- const { open, setOpen } = props;
-
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{ defaultFlag: 0, sort: 0, maxContextMessages: 12, status: 1, provider: 'openai' }}
- transform={(data) => {
- return data;
- }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog
- open={open}
- onClose={handleClose}
- aria-labelledby="form-dialog-title"
- fullWidth
- disableRestoreFocus
- maxWidth="md"
- >
- <Form>
- <DialogTitle id="form-dialog-title" sx={{
- position: 'sticky',
- top: 0,
- backgroundColor: 'background.paper',
- zIndex: 1000
- }}
- >
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6} display="flex" gap={1}>
- <TextInput label="table.field.aiParam.name" source="name" parse={v => v} fullWidth />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <TextInput label="table.field.aiParam.modelCode" source="modelCode" parse={v => v} fullWidth />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <SelectInput
- label="table.field.aiParam.provider"
- source="provider"
- choices={providerChoices}
- fullWidth
- />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <TextInput
- label="table.field.aiParam.modelName"
- source="modelName"
- parse={v => v}
- helperText="濉啓鐪熷疄妯″瀷鍚嶏紝渚嬪 gpt-4o-mini銆乨eepseek-chat"
- fullWidth
- />
- </Grid>
- <Grid item xs={12} display="flex" gap={1}>
- <TextInput
- label="table.field.aiParam.chatUrl"
- source="chatUrl"
- parse={v => v}
- helperText="鏀寔濉啓 baseUrl锛屽 https://api.openai.com 鎴� https://api.siliconflow.cn锛岀郴缁熶細鑷姩琛ュ叏涓� /v1/chat/completions"
- fullWidth
- />
- </Grid>
- <Grid item xs={12} display="flex" gap={1}>
- <TextInput
- label="table.field.aiParam.apiKey"
- source="apiKey"
- parse={v => v}
- type="password"
- helperText="OpenAI 鎺ュ彛妯″紡涓嬪~鍐� Bearer Token锛屾棤闇�鎵嬪姩鍔� Bearer 鍓嶇紑"
- fullWidth
- />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <NumberInput label="table.field.aiParam.maxContextMessages" source="maxContextMessages" fullWidth />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <NumberInput label="table.field.aiParam.sort" source="sort" fullWidth />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <SelectInput
- label="table.field.aiParam.defaultFlag"
- source="defaultFlag"
- choices={yesNoChoices}
- fullWidth
- />
- </Grid>
- <Grid item xs={6} display="flex" gap={1}>
- <StatusSelectInput fullWidth />
- </Grid>
- <Grid item xs={12} display="flex" gap={1}>
- <TextInput
- label="table.field.aiParam.systemPrompt"
- source="systemPrompt"
- parse={v => v}
- fullWidth
- multiline
- minRows={4}
- />
- </Grid>
- <Grid item xs={12} display="flex" gap={1}>
- <Stack direction="column" spacing={1} width={'100%'}>
- <MemoInput />
- </Stack>
- </Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }} >
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiParamCreate;
diff --git a/rsf-admin/src/page/system/aiParam/AiParamEdit.jsx b/rsf-admin/src/page/system/aiParam/AiParamEdit.jsx
deleted file mode 100644
index 6165be5..0000000
--- a/rsf-admin/src/page/system/aiParam/AiParamEdit.jsx
+++ /dev/null
@@ -1,136 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { useFormContext } from "react-hook-form";
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const yesNoChoices = [
- { id: 1, name: 'common.enums.true' },
- { id: 0, name: 'common.enums.false' },
-];
-
-const providerChoices = [
- { id: 'openai', name: 'OpenAI Compatible' },
- { id: 'mock', name: 'Mock' },
-];
-
-const FormToolbar = () => {
- const { getValues } = useFormContext();
-
- return (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
- )
-}
-
-const AiParamEdit = () => {
- const translate = useTranslate();
-
- return (
- <Edit
- redirect="list"
- mutationMode={EDIT_MODE}
- actions={<CustomerTopToolBar />}
- aside={<EditBaseAside />}
- >
- <SimpleForm
- shouldUnregister
- warnWhenUnsavedChanges
- toolbar={<FormToolbar />}
- mode="onTouched"
- defaultValues={{}}
- >
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>
- {translate('common.edit.title.main')}
- </Typography>
- <Stack direction='row' gap={2}>
- <TextInput label="table.field.aiParam.uuid" source="uuid" parse={v => v} disabled />
- <TextInput label="table.field.aiParam.name" source="name" parse={v => v} />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput label="table.field.aiParam.modelCode" source="modelCode" parse={v => v} />
- <SelectInput
- label="table.field.aiParam.provider"
- source="provider"
- choices={providerChoices}
- />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput
- label="table.field.aiParam.modelName"
- source="modelName"
- parse={v => v}
- helperText="濉啓鐪熷疄妯″瀷鍚嶏紝渚嬪 gpt-4o-mini銆乨eepseek-chat"
- />
- <NumberInput label="table.field.aiParam.maxContextMessages" source="maxContextMessages" />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput label="table.field.aiParam.sort" source="sort" />
- <SelectInput
- label="table.field.aiParam.defaultFlag"
- source="defaultFlag"
- choices={yesNoChoices}
- />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput
- label="table.field.aiParam.chatUrl"
- source="chatUrl"
- parse={v => v}
- helperText="鏀寔濉啓 baseUrl锛屽 https://api.openai.com 鎴� https://api.siliconflow.cn锛岀郴缁熶細鑷姩琛ュ叏涓� /v1/chat/completions"
- fullWidth
- />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput
- label="table.field.aiParam.apiKey"
- source="apiKey"
- parse={v => v}
- type="password"
- helperText="OpenAI 鎺ュ彛妯″紡涓嬪~鍐� Bearer Token锛屾棤闇�鎵嬪姩鍔� Bearer 鍓嶇紑"
- fullWidth
- />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput
- label="table.field.aiParam.systemPrompt"
- source="systemPrompt"
- parse={v => v}
- fullWidth
- multiline
- minRows={5}
- />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>
- {translate('common.edit.title.common')}
- </Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit >
- )
-}
-
-export default AiParamEdit;
diff --git a/rsf-admin/src/page/system/aiParam/AiParamList.jsx b/rsf-admin/src/page/system/aiParam/AiParamList.jsx
deleted file mode 100644
index 41fb75c..0000000
--- a/rsf-admin/src/page/system/aiParam/AiParamList.jsx
+++ /dev/null
@@ -1,191 +0,0 @@
-import React, { useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useListContext,
- Pagination,
- EditButton,
- DeleteButton,
-} from 'react-admin';
-import { Box, Chip, Grid, Stack, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import AiParamCreate from "./AiParamCreate";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <TextInput source="name" label="table.field.aiParam.name" />,
- <TextInput source="modelCode" label="table.field.aiParam.modelCode" />,
- <TextInput source="provider" label="table.field.aiParam.provider" />,
- <TextInput source="modelName" label="table.field.aiParam.modelName" />,
- <SelectInput
- source="defaultFlag"
- label="table.field.aiParam.defaultFlag"
- choices={[
- { id: '1', name: 'common.enums.true' },
- { id: '0', name: 'common.enums.false' },
- ]}
- />,
- <TextInput label="common.field.memo" source="memo" />,
- <SelectInput
- label="common.field.status"
- source="status"
- choices={[
- { id: '1', name: 'common.enums.statusTrue' },
- { id: '0', name: 'common.enums.statusFalse' },
- ]}
- />,
-];
-
-const providerLabel = (provider) => {
- if (provider === 'openai') {
- return 'OpenAI Compatible';
- }
- if (provider === 'mock') {
- return 'Mock';
- }
- return provider || '鏈厤缃�';
-};
-
-const AiParamBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const enabledCount = records.filter((item) => item.status === 1).length;
- const defaultCount = records.filter((item) => item.defaultFlag === 1).length;
- const openaiCount = records.filter((item) => item.provider === 'openai').length;
- const mockCount = records.filter((item) => item.provider === 'mock').length;
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI鍙傛暟"
- subtitle="淇濇寔褰撳墠绯荤粺鍚庡彴鐨勭櫧搴曞崱鐗囧拰杞昏竟妗嗛鏍硷紝鐢ㄥ崱鐗囨柟寮忓睍绀烘ā鍨嬮厤缃鍐碉紝鏂逛究鍜屽叾浠� AI 椤甸潰缁熶竴鏌ョ湅銆�"
- stats={[
- { label: '鍙傛暟鎬绘暟', value: records.length },
- { label: '鍚敤', value: enabledCount },
- { label: '榛樿妯″瀷', value: defaultCount },
- { label: 'OpenAI / Mock', value: `${openaiCount} / ${mockCount}` },
- ]}
- >
- <AiConsolePanel
- title="妯″瀷閰嶇疆"
- subtitle="灞曠ず妯″瀷缂栫爜銆佷緵搴斿晢銆佷笂涓嬫枃杞暟鍜岄粯璁ょ姸鎬侊紱鍒涘缓銆佺紪杈戙�佸垹闄や粛娌跨敤鍘熺郴缁熺殑寮圭獥涓庣紪杈戦〉銆�"
- minHeight={460}
- >
- <Box
- sx={{
- display: 'grid',
- gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))',
- gap: 2,
- }}
- >
- {records.map((record) => (
- <Box key={record.id}>
- <Box sx={aiCardSx(record.defaultFlag === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
- {record.name || record.modelCode || record.uuid}
- </Typography>
- <Typography variant="caption" color="text.secondary">
- {record.modelCode || '鏈~鍐欐ā鍨嬬紪鐮�'}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={record.status === 1 ? 'success' : 'default'} label={record.status === 1 ? '鍚敤' : '鍋滅敤'} />
- {record.defaultFlag === 1 ? <Chip size="small" color="primary" label="榛樿" /> : null}
- </Stack>
- </Stack>
- <Grid container spacing={1.25} sx={{ mt: 1 }}>
- <Grid item xs={6}>
- <Typography variant="caption" color="text.secondary">渚涘簲鍟�</Typography>
- <Typography variant="body2" sx={{ mt: 0.25 }}>{providerLabel(record.provider)}</Typography>
- </Grid>
- <Grid item xs={6}>
- <Typography variant="caption" color="text.secondary">妯″瀷鍚�</Typography>
- <Typography variant="body2" sx={{ mt: 0.25 }}>{record.modelName || '-'}</Typography>
- </Grid>
- <Grid item xs={6}>
- <Typography variant="caption" color="text.secondary">涓婁笅鏂囪疆鏁�</Typography>
- <Typography variant="body2" sx={{ mt: 0.25 }}>{record.maxContextMessages || 0}</Typography>
- </Grid>
- <Grid item xs={6}>
- <Typography variant="caption" color="text.secondary">鎺掑簭</Typography>
- <Typography variant="body2" sx={{ mt: 0.25 }}>{record.sort || 0}</Typography>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="caption" color="text.secondary">鑱婂ぉ鍦板潃</Typography>
- <Typography variant="body2" sx={{ mt: 0.25, wordBreak: 'break-all' }}>
- {record.chatUrl || '鏈厤缃�'}
- </Typography>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="caption" color="text.secondary">澶囨敞</Typography>
- <Typography variant="body2" sx={{ mt: 0.25, minHeight: 42 }}>
- {record.memo || '鏈~鍐欏娉�'}
- </Typography>
- </Grid>
- </Grid>
- <Stack direction="row" spacing={1} sx={{ mt: 1.5 }}>
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- <DeleteButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} mutationMode={OPERATE_MODE} />
- </Stack>
- </Box>
- </Box>
- ))}
- </Box>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiParamList = () => {
- const [createDialog, setCreateDialog] = useState(false);
-
- return (
- <Box display="flex" sx={{ width: '100%' }}>
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiParam"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
- filters={filters}
- sort={{ field: "sort", order: "asc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiParam' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <AiParamBoard />
- </List>
- <AiParamCreate
- open={createDialog}
- setOpen={setCreateDialog}
- />
- </Box>
- )
-}
-
-export default AiParamList;
diff --git a/rsf-admin/src/page/system/aiParam/index.jsx b/rsf-admin/src/page/system/aiParam/index.jsx
deleted file mode 100644
index 917e915..0000000
--- a/rsf-admin/src/page/system/aiParam/index.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiParamList from "./AiParamList";
-import AiParamEdit from "./AiParamEdit";
-
-export default {
- list: AiParamList,
- edit: AiParamEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => {
- return `${record.name}`
- }
-};
diff --git a/rsf-admin/src/page/system/aiPrompt/AiPromptCreate.jsx b/rsf-admin/src/page/system/aiPrompt/AiPromptCreate.jsx
deleted file mode 100644
index 1a4d34e..0000000
--- a/rsf-admin/src/page/system/aiPrompt/AiPromptCreate.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Stack,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const AiPromptCreate = (props) => {
- const { open, setOpen } = props;
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{ sceneCode: 'system_diagnose', status: 1, publishedFlag: 0 }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog open={open} onClose={handleClose} fullWidth disableRestoreFocus maxWidth="md">
- <Form>
- <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6}><SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="templateName" label="妯℃澘鍚嶇О" fullWidth /></Grid>
- <Grid item xs={12}><TextInput source="basePrompt" label="鍩虹鎻愮ず璇�" fullWidth multiline minRows={4} /></Grid>
- <Grid item xs={12}><TextInput source="toolPrompt" label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={4} /></Grid>
- <Grid item xs={12}><TextInput source="outputPrompt" label="杈撳嚭鎻愮ず璇�" fullWidth multiline minRows={4} /></Grid>
- <Grid item xs={6}><NumberInput source="versionNo" label="鐗堟湰鍙�" fullWidth /></Grid>
- <Grid item xs={6}><StatusSelectInput fullWidth /></Grid>
- <Grid item xs={12}><Stack direction="column" spacing={1} width={'100%'}><MemoInput /></Stack></Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}>
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiPromptCreate;
diff --git a/rsf-admin/src/page/system/aiPrompt/AiPromptEdit.jsx b/rsf-admin/src/page/system/aiPrompt/AiPromptEdit.jsx
deleted file mode 100644
index 3493b5f..0000000
--- a/rsf-admin/src/page/system/aiPrompt/AiPromptEdit.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const FormToolbar = () => (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
-);
-
-const AiPromptEdit = () => (
- <Edit redirect="list" mutationMode={EDIT_MODE} actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm shouldUnregister warnWhenUnsavedChanges toolbar={<FormToolbar />} mode="onTouched">
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>涓昏</Typography>
- <Stack direction='row' gap={2}>
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} />
- <TextInput source="templateName" label="妯℃澘鍚嶇О" />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput source="versionNo" label="鐗堟湰鍙�" />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="basePrompt" label="鍩虹鎻愮ず璇�" fullWidth multiline minRows={4} />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="toolPrompt" label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={4} />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="outputPrompt" label="杈撳嚭鎻愮ず璇�" fullWidth multiline minRows={4} />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>閫氱敤</Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-)
-
-export default AiPromptEdit;
diff --git a/rsf-admin/src/page/system/aiPrompt/AiPromptList.jsx b/rsf-admin/src/page/system/aiPrompt/AiPromptList.jsx
deleted file mode 100644
index df57447..0000000
--- a/rsf-admin/src/page/system/aiPrompt/AiPromptList.jsx
+++ /dev/null
@@ -1,383 +0,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useNotify,
- useRefresh,
- useListContext,
- Pagination,
- EditButton,
-} from 'react-admin';
-import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, Grid, InputLabel, MenuItem, Select, Stack, TextField as MuiTextField, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { DEFAULT_PAGE_SIZE } from '@/config/setting';
-import AiPromptCreate from "./AiPromptCreate";
-import request from "@/utils/request";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const sceneLabels = {
- general_chat: '閫氱敤瀵硅瘽',
- system_diagnose: '绯荤粺璇婃柇',
-};
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} />,
- <TextInput source="templateName" label="妯℃澘鍚嶇О" />,
- <SelectInput
- source="publishedFlag"
- label="鍙戝竷鐘舵��"
- choices={[
- { id: '1', name: '宸插彂甯�' },
- { id: '0', name: '鑽夌' },
- ]}
- />,
- <SelectInput
- label="common.field.status"
- source="status"
- choices={[
- { id: '1', name: 'common.enums.statusTrue' },
- { id: '0', name: 'common.enums.statusFalse' },
- ]}
- />,
-];
-
-const PromptBoard = ({ onCreateDraft }) => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const refresh = useRefresh();
- const notify = useNotify();
- const [selectedScene, setSelectedScene] = useState('');
- const [versionDialog, setVersionDialog] = useState(false);
- const [activeRecord, setActiveRecord] = useState(null);
- const [logs, setLogs] = useState([]);
- const [compareId, setCompareId] = useState('');
- const [compareData, setCompareData] = useState(null);
-
- const groupedScenes = useMemo(() => {
- const map = {};
- records.forEach((item) => {
- const code = item.sceneCode || 'unknown';
- if (!map[code]) {
- map[code] = [];
- }
- map[code].push(item);
- });
- return map;
- }, [records]);
-
- const sceneCodes = Object.keys(groupedScenes).sort();
- const selectedRecords = groupedScenes[selectedScene] || [];
- const publishedCount = records.filter((item) => item.publishedFlag === 1).length;
- const draftCount = records.filter((item) => item.publishedFlag !== 1).length;
-
- useEffect(() => {
- if (!selectedScene && sceneCodes.length) {
- setSelectedScene(sceneCodes[0]);
- }
- if (selectedScene && !groupedScenes[selectedScene] && sceneCodes.length) {
- setSelectedScene(sceneCodes[0]);
- }
- }, [selectedScene, sceneCodes, groupedScenes]);
-
- const openVersionDialog = async (record) => {
- try {
- const logRes = await request.get(`/ai/prompt/publish-log/list?sceneCode=${record.sceneCode}`);
- setActiveRecord(record);
- setLogs(logRes.data?.data || []);
- setCompareId('');
- setCompareData(null);
- setVersionDialog(true);
- } catch (error) {
- notify(error.message || '鍔犺浇鐗堟湰鏃ュ織澶辫触', { type: 'error' });
- }
- };
-
- const handlePublish = async (record) => {
- try {
- await request.post('/ai/prompt/publish', { id: record.id });
- notify('common.response.success');
- refresh();
- } catch (error) {
- notify(error.message || '鎿嶄綔澶辫触', { type: 'error' });
- }
- };
-
- const handleRollback = async (record) => {
- try {
- await request.post('/ai/prompt/rollback', { id: record.id });
- notify('common.response.success');
- refresh();
- } catch (error) {
- notify(error.message || '鎿嶄綔澶辫触', { type: 'error' });
- }
- };
-
- const handleCopy = async (record) => {
- try {
- await request.post('/ai/prompt/copy', { id: record.id });
- notify('common.response.success');
- refresh();
- } catch (error) {
- notify(error.message || '鎿嶄綔澶辫触', { type: 'error' });
- }
- };
-
- const handleCompare = async () => {
- if (!activeRecord || !compareId) {
- return;
- }
- try {
- const res = await request.get(`/ai/prompt/compare?leftId=${activeRecord.id}&rightId=${compareId}`);
- setCompareData(res.data?.data || null);
- } catch (error) {
- notify(error.message || '鍔犺浇瀵规瘮澶辫触', { type: 'error' });
- }
- };
-
- if (!isLoading && !records.length) {
- return <EmptyData onClick={onCreateDraft} />;
- }
-
- return (
- <>
- <AiConsoleLayout
- title="Prompt 閰嶇疆涓績"
- subtitle="椤甸潰缁撴瀯鍙傝�� zy-wcs-master 鐨� Prompt 涓績锛屼繚鐣欏師绯荤粺鐨勬寜閽�佺紪杈戦〉鍜屾潈闄愭ā鍨嬶紝閲嶇偣澧炲己鍦烘櫙鎰熺煡銆佺増鏈槄璇诲拰杩愯惀鍔ㄤ綔灞曠ず銆�"
- actions={[
- <Button key="draft" variant="contained" onClick={onCreateDraft}>鏂板缓鑽夌</Button>,
- ]}
- stats={[
- { label: '鍦烘櫙鏁�', value: sceneCodes.length },
- { label: '鐗堟湰鎬绘暟', value: records.length },
- { label: '宸插彂甯�', value: publishedCount },
- { label: '鑽夌', value: draftCount },
- ]}
- >
- <Grid container spacing={2}>
- <Grid item xs={12} md={3}>
- <AiConsolePanel
- title="鍦烘櫙"
- subtitle="姣忎釜鍦烘櫙鍙細鏈変竴涓凡鍙戝竷鐗堟湰锛岃瘖鏂拰鑱婂ぉ杩愯鏃朵細鐩存帴璇诲彇瀹冦��"
- minHeight={520}
- >
- <Stack spacing={1.5}>
- {sceneCodes.map((code) => {
- const list = groupedScenes[code] || [];
- const published = list.find((item) => item.publishedFlag === 1);
- const drafts = list.filter((item) => item.publishedFlag !== 1).length;
- return (
- <Box key={code} sx={{ ...aiCardSx(selectedScene === code), cursor: 'pointer' }} onClick={() => setSelectedScene(code)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {sceneLabels[code] || code}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8aa0b7' }}>{code}</Typography>
- </Box>
- <Chip size="small" color={published ? 'success' : 'default'} label={published ? `v${published.versionNo}` : '鏈彂甯�'} />
- </Stack>
- <Grid container spacing={1} sx={{ mt: 1 }}>
- <Grid item xs={6}>
- <Box sx={{ p: 1, borderRadius: 2, border: '1px solid #e7eef7', backgroundColor: '#fff' }}>
- <Typography variant="caption" sx={{ color: '#7f92a8' }}>鐗堟湰鏁�</Typography>
- <Typography variant="h6" sx={{ mt: 0.25, color: '#2a3e55' }}>{list.length}</Typography>
- </Box>
- </Grid>
- <Grid item xs={6}>
- <Box sx={{ p: 1, borderRadius: 2, border: '1px solid #e7eef7', backgroundColor: '#fff' }}>
- <Typography variant="caption" sx={{ color: '#7f92a8' }}>鑽夌鏁�</Typography>
- <Typography variant="h6" sx={{ mt: 0.25, color: '#2a3e55' }}>{drafts}</Typography>
- </Box>
- </Grid>
- </Grid>
- </Box>
- );
- })}
- </Stack>
- </AiConsolePanel>
- </Grid>
- <Grid item xs={12} md={4}>
- <AiConsolePanel
- title="鐗堟湰鍒楄〃"
- subtitle={`褰撳墠鍦烘櫙锛�${sceneLabels[selectedScene] || selectedScene || '鏈�夋嫨鍦烘櫙'}`}
- action={<Button size="small" variant="outlined" onClick={onCreateDraft}>鏂板缓鑽夌</Button>}
- minHeight={520}
- >
- <Stack spacing={1.5}>
- {selectedRecords.map((record) => (
- <Box key={record.id} sx={aiCardSx(record.publishedFlag === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.templateName || `${sceneLabels[record.sceneCode] || record.sceneCode} v${record.versionNo}`}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- 鐗堟湰 v{record.versionNo} 路 鏇存柊鏃堕棿 {record.updateTime || '-'}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75}>
- <Chip size="small" color={record.publishedFlag === 1 ? 'warning' : 'default'} label={record.publishedFlag === 1 ? '宸插彂甯�' : '鑽夌'} />
- <Chip size="small" color={record.status === 1 ? 'success' : 'default'} label={record.status === 1 ? '鍚敤' : '鍋滅敤'} />
- </Stack>
- </Stack>
- <Typography variant="body2" sx={{ mt: 1.25, minHeight: 48, color: '#31465d' }}>
- {record.memo || '鏈~鍐欑増鏈娉�'}
- </Typography>
- <Stack direction="row" spacing={1} flexWrap="wrap" sx={{ mt: 1.5 }}>
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- <Button size="small" variant="outlined" onClick={() => openVersionDialog(record)}>鐗堟湰</Button>
- <Button size="small" variant="outlined" onClick={() => handleCopy(record)}>澶嶅埗</Button>
- <Button size="small" variant="outlined" onClick={() => handlePublish(record)}>鍙戝竷</Button>
- <Button size="small" variant="outlined" onClick={() => handleRollback(record)}>鍥炴粴</Button>
- </Stack>
- </Box>
- ))}
- </Stack>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </Grid>
- <Grid item xs={12} md={5}>
- <AiConsolePanel
- title="杩愯惀鎻愮ず"
- subtitle="褰撳墠鐗堟湰缂栬緫浠嶈繘鍏ュ師鏈夌紪杈戦〉锛岃繖閲屼富瑕佹壙鎺ョ増鏈槄璇汇�佸姣斿拰鍙戝竷鏃ュ織棰勮銆�"
- minHeight={520}
- >
- <Box sx={{ ...aiCardSx(false), minHeight: 140 }}>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>浣跨敤寤鸿</Typography>
- <Typography variant="body2" sx={{ mt: 1, color: '#31465d', lineHeight: 1.8 }}>
- 鍏堝湪宸︿晶鍒囨崲鍦烘櫙锛屽啀鍦ㄤ腑闂存寫閫夎崏绋挎垨绾夸笂鐗堟湰銆傞渶瑕佹繁搴︽煡鐪嬪彂甯冭建杩广�佸仛涓ょ増 Prompt 鏂囨湰瀵规瘮鏃讹紝鐐瑰嚮鈥滅増鏈�濊繘鍏ヨ繍钀ュ脊绐椼��
- </Typography>
- </Box>
- <Box sx={{ ...aiCardSx(true), mt: 1.5, minHeight: 230 }}>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>褰撳墠鍦烘櫙鎽樿</Typography>
- <Stack spacing={1} sx={{ mt: 1.25 }}>
- <Typography variant="body2" sx={{ color: '#31465d' }}>
- 鍦烘櫙缂栫爜锛歿selectedScene || '-'}
- </Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>
- 鍙戝竷鐗堟湰锛歿(selectedRecords.find((item) => item.publishedFlag === 1)?.versionNo) ? `v${selectedRecords.find((item) => item.publishedFlag === 1)?.versionNo}` : '鏆傛棤'}
- </Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>
- 鑽夌鏁伴噺锛歿selectedRecords.filter((item) => item.publishedFlag !== 1).length}
- </Typography>
- <Typography variant="body2" sx={{ color: '#31465d' }}>
- 鍚敤鐗堟湰锛歿selectedRecords.filter((item) => item.status === 1).length}
- </Typography>
- </Stack>
- </Box>
- </AiConsolePanel>
- </Grid>
- </Grid>
- </AiConsoleLayout>
-
- <Dialog open={versionDialog} onClose={() => setVersionDialog(false)} fullWidth maxWidth="lg">
- <DialogTitle>Prompt 鐗堟湰杩愯惀</DialogTitle>
- <DialogContent>
- <Stack spacing={3}>
- <Typography variant="body2">
- 褰撳墠妯℃澘锛歿activeRecord?.templateName || '-'} / 鐗堟湰 {activeRecord?.versionNo || '-'}
- </Typography>
- <Stack direction={{ xs: 'column', md: 'row' }} spacing={2}>
- <Box flex={1}>
- <Typography variant="subtitle1" gutterBottom>鍙戝竷鏃ュ織</Typography>
- {logs.map((item) => (
- <Box key={item.id} sx={{ borderBottom: '1px solid #eee', pb: 1, mb: 1 }}>
- <Typography variant="body2">{item.actionType} / v{item.versionNo} / {item.templateName}</Typography>
- <Typography variant="caption" color="text.secondary">{item.actionDesc} / {item.createTime}</Typography>
- </Box>
- ))}
- </Box>
- <Box flex={1}>
- <Typography variant="subtitle1" gutterBottom>鏂囨湰瀵规瘮</Typography>
- <Stack spacing={1.5}>
- <FormControl sx={{ minWidth: 220 }}>
- <InputLabel id="prompt-compare-label">瀵规瘮鐗堟湰</InputLabel>
- <Select
- labelId="prompt-compare-label"
- label="瀵规瘮鐗堟湰"
- value={compareId}
- onChange={(event) => setCompareId(event.target.value)}
- >
- {selectedRecords.filter((item) => item.id !== activeRecord?.id).map((item) => (
- <MenuItem key={item.id} value={item.id}>v{item.versionNo} / {item.templateName}</MenuItem>
- ))}
- </Select>
- </FormControl>
- <Button variant="outlined" onClick={handleCompare}>鍔犺浇瀵规瘮</Button>
- </Stack>
- </Box>
- </Stack>
- {compareData ? (
- <Stack direction={{ xs: 'column', md: 'row' }} spacing={2}>
- <Box flex={1}>
- <Typography variant="subtitle1" gutterBottom>褰撳墠鐗堟湰</Typography>
- <MuiTextField label="鍩虹鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.left?.basePrompt || ''} disabled margin="dense" />
- <MuiTextField label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.left?.toolPrompt || ''} disabled margin="dense" />
- <MuiTextField label="杈撳嚭鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.left?.outputPrompt || ''} disabled margin="dense" />
- </Box>
- <Box flex={1}>
- <Typography variant="subtitle1" gutterBottom>瀵规瘮鐗堟湰</Typography>
- <MuiTextField label="鍩虹鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.right?.basePrompt || ''} disabled margin="dense" />
- <MuiTextField label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.right?.toolPrompt || ''} disabled margin="dense" />
- <MuiTextField label="杈撳嚭鎻愮ず璇�" fullWidth multiline minRows={5} value={compareData.right?.outputPrompt || ''} disabled margin="dense" />
- </Box>
- </Stack>
- ) : null}
- </Stack>
- </DialogContent>
- <DialogActions>
- <Button onClick={() => setVersionDialog(false)}>鍏抽棴</Button>
- </DialogActions>
- </Dialog>
- </>
- );
-};
-
-const AiPromptList = () => {
- const [createDialog, setCreateDialog] = useState(false);
-
- return (
- <Box display="flex" sx={{ width: '100%' }}>
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiPrompt"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
- filters={filters}
- sort={{ field: "updateTime", order: "desc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiPrompt' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <PromptBoard onCreateDraft={() => setCreateDialog(true)} />
- </List>
- <AiPromptCreate open={createDialog} setOpen={setCreateDialog} />
- </Box>
- )
-}
-
-export default AiPromptList;
diff --git a/rsf-admin/src/page/system/aiPrompt/index.jsx b/rsf-admin/src/page/system/aiPrompt/index.jsx
deleted file mode 100644
index fd45e59..0000000
--- a/rsf-admin/src/page/system/aiPrompt/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiPromptList from "./AiPromptList";
-import AiPromptEdit from "./AiPromptEdit";
-
-export default {
- list: AiPromptList,
- edit: AiPromptEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.templateName || record.sceneCode || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiRoute/AiRouteCreate.jsx b/rsf-admin/src/page/system/aiRoute/AiRouteCreate.jsx
deleted file mode 100644
index 408d86e..0000000
--- a/rsf-admin/src/page/system/aiRoute/AiRouteCreate.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Stack,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const routeChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const AiRouteCreate = (props) => {
- const { open, setOpen } = props;
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{ routeCode: 'general_chat', priority: 1, failCount: 0, successCount: 0, status: 1 }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog open={open} onClose={handleClose} fullWidth disableRestoreFocus maxWidth="md">
- <Form>
- <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6}><SelectInput source="routeCode" label="璺敱缂栫爜" choices={routeChoices} fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="modelCode" label="妯″瀷缂栫爜" fullWidth /></Grid>
- <Grid item xs={6}><NumberInput source="priority" label="浼樺厛绾�" fullWidth /></Grid>
- <Grid item xs={6}><StatusSelectInput fullWidth /></Grid>
- <Grid item xs={12}><Stack direction="column" spacing={1} width={'100%'}><MemoInput /></Stack></Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}>
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiRouteCreate;
diff --git a/rsf-admin/src/page/system/aiRoute/AiRouteEdit.jsx b/rsf-admin/src/page/system/aiRoute/AiRouteEdit.jsx
deleted file mode 100644
index 8bd5dc5..0000000
--- a/rsf-admin/src/page/system/aiRoute/AiRouteEdit.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const routeChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const FormToolbar = () => (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
-);
-
-const AiRouteEdit = () => (
- <Edit redirect="list" mutationMode={EDIT_MODE} actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm shouldUnregister warnWhenUnsavedChanges toolbar={<FormToolbar />} mode="onTouched">
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>涓昏</Typography>
- <Stack direction='row' gap={2}>
- <SelectInput source="routeCode" label="璺敱缂栫爜" choices={routeChoices} />
- <TextInput source="modelCode" label="妯″瀷缂栫爜" />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput source="priority" label="浼樺厛绾�" />
- <NumberInput source="failCount" label="澶辫触娆℃暟" />
- </Stack>
- <Stack direction='row' gap={2}>
- <NumberInput source="successCount" label="鎴愬姛娆℃暟" />
- <TextInput source="cooldownUntil$" label="鍐峰嵈鎴" disabled />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>閫氱敤</Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-)
-
-export default AiRouteEdit;
diff --git a/rsf-admin/src/page/system/aiRoute/AiRouteList.jsx b/rsf-admin/src/page/system/aiRoute/AiRouteList.jsx
deleted file mode 100644
index 044f87c..0000000
--- a/rsf-admin/src/page/system/aiRoute/AiRouteList.jsx
+++ /dev/null
@@ -1,190 +0,0 @@
-import React, { useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useNotify,
- useRefresh,
- useListContext,
- Pagination,
- DeleteButton,
-} from 'react-admin';
-import { Box, Button, Chip, Grid, Stack, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import AiRouteCreate from "./AiRouteCreate";
-import request from "@/utils/request";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const routeChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <SelectInput source="routeCode" label="璺敱缂栫爜" choices={routeChoices} />,
- <TextInput source="modelCode" label="妯″瀷缂栫爜" />,
- <SelectInput label="common.field.status" source="status" choices={[
- { id: '1', name: 'common.enums.statusTrue' },
- { id: '0', name: 'common.enums.statusFalse' },
- ]} />,
-];
-
-const RouteCardActions = ({ record }) => {
- const refresh = useRefresh();
- const notify = useNotify();
-
- const handleToggle = async () => {
- try {
- await request.post('/ai/route/toggle', { id: record.id, status: record.status === 1 ? 0 : 1 });
- notify('common.response.success');
- refresh();
- } catch (error) {
- notify(error.message || '鎿嶄綔澶辫触', { type: 'error' });
- }
- };
-
- const handleReset = async () => {
- try {
- await request.post('/ai/route/reset', { id: record.id });
- notify('common.response.success');
- refresh();
- } catch (error) {
- notify(error.message || '鎿嶄綔澶辫触', { type: 'error' });
- }
- };
-
- return (
- <Stack direction="row" spacing={1} flexWrap="wrap">
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- <DeleteButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} mutationMode={OPERATE_MODE} />
- <Button size="small" variant="outlined" onClick={handleToggle}>{record.status === 1 ? '鍋滅敤' : '鍚敤'}</Button>
- <Button size="small" variant="outlined" onClick={handleReset}>閲嶇疆</Button>
- </Stack>
- );
-};
-
-const RouteBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const enabledCount = records.filter((item) => item.status === 1).length;
- const coolingCount = records.filter((item) => item.cooldownUntil).length;
- const failSwitchCount = records.filter((item) => (item.failCount || 0) > 0).length;
- const successCount = records.reduce((sum, item) => sum + (item.successCount || 0), 0);
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI妯″瀷璺敱"
- subtitle="鍙傝�� zy-wcs-master 鐨� LLM 鎺у埗鍙颁俊鎭眰娆★紝绐佸嚭璺敱鐘舵�併�佸喎鍗翠笌鎴愬姛澶辫触璁℃暟锛屼絾淇濈暀鐜版湁绯荤粺鐨勬寜閽�佺瓫閫夊拰琛ㄥ崟椋庢牸銆�"
- stats={[
- { label: '鎬昏矾鐢�', value: records.length },
- { label: '鍚敤', value: enabledCount },
- { label: '鍐峰嵈涓�', value: coolingCount },
- { label: '绱鎴愬姛', value: successCount, helper: `宸叉湁澶辫触璁板綍 ${failSwitchCount} 鏉 },
- ]}
- >
- <AiConsolePanel
- title="璺敱鍗$墖"
- subtitle="姣忓紶鍗$墖浠h〃涓�鏉℃ā鍨嬪�欓�夛紝浼樺厛绾ц秺灏忚秺鍏堝懡涓紱鍚仠銆侀噸缃細绔嬪嵆浣滅敤浜庡悗缁姹傘��"
- minHeight={460}
- >
- <Grid container spacing={2}>
- {records.map((record) => (
- <Grid item xs={12} md={6} xl={4} key={record.id}>
- <Box sx={aiCardSx(record.status === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.modelCode}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- {record.routeCode} 路 浼樺厛绾� {record.priority}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={record.status === 1 ? 'success' : 'default'} label={record.status === 1 ? '鍚敤' : '鍋滅敤'} />
- {record.cooldownUntil ? <Chip size="small" color="warning" label="鍐峰嵈涓�" /> : null}
- </Stack>
- </Stack>
- <Grid container spacing={1.25} sx={{ mt: 1 }}>
- <Grid item xs={6}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>澶辫触娆℃暟</Typography>
- <Typography variant="h6" sx={{ color: '#2f455c', mt: 0.25 }}>{record.failCount || 0}</Typography>
- </Grid>
- <Grid item xs={6}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>鎴愬姛娆℃暟</Typography>
- <Typography variant="h6" sx={{ color: '#2f455c', mt: 0.25 }}>{record.successCount || 0}</Typography>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>鍐峰嵈鎴</Typography>
- <Typography variant="body2" sx={{ mt: 0.25, color: '#31465d' }}>
- {record.cooldownUntil$ || '鏈繘鍏ュ喎鍗�'}
- </Typography>
- </Grid>
- <Grid item xs={12}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>澶囨敞</Typography>
- <Typography variant="body2" sx={{ mt: 0.25, color: '#31465d', minHeight: 42 }}>
- {record.memo || '鏈~鍐欏娉�'}
- </Typography>
- </Grid>
- </Grid>
- <Box sx={{ mt: 1.5 }}>
- <RouteCardActions record={record} />
- </Box>
- </Box>
- </Grid>
- ))}
- </Grid>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiRouteList = () => {
- const [createDialog, setCreateDialog] = useState(false);
-
- return (
- <Box display="flex" sx={{ width: '100%' }}>
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiRoute"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
- filters={filters}
- sort={{ field: "priority", order: "asc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiRoute' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <RouteBoard />
- </List>
- <AiRouteCreate open={createDialog} setOpen={setCreateDialog} />
- </Box>
- );
-}
-
-export default AiRouteList;
diff --git a/rsf-admin/src/page/system/aiRoute/index.jsx b/rsf-admin/src/page/system/aiRoute/index.jsx
deleted file mode 100644
index 223ed58..0000000
--- a/rsf-admin/src/page/system/aiRoute/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiRouteList from "./AiRouteList";
-import AiRouteEdit from "./AiRouteEdit";
-
-export default {
- list: AiRouteList,
- edit: AiRouteEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.routeCode || record.modelCode || ''}`
-};
diff --git a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigCreate.jsx b/rsf-admin/src/page/system/aiToolConfig/AiToolConfigCreate.jsx
deleted file mode 100644
index 6295a4c..0000000
--- a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigCreate.jsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React from "react";
-import {
- CreateBase,
- useTranslate,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- useNotify,
- Form,
-} from 'react-admin';
-import {
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Stack,
- Grid,
- Box,
-} from '@mui/material';
-import DialogCloseButton from "@/page/components/DialogCloseButton";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-import MemoInput from "@/page/components/MemoInput";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const enabledChoices = [
- { id: 1, name: '鍚敤' },
- { id: 0, name: '鍋滅敤' },
-];
-
-const AiToolConfigCreate = (props) => {
- const { open, setOpen } = props;
- const translate = useTranslate();
- const notify = useNotify();
-
- const handleClose = (event, reason) => {
- if (reason !== "backdropClick") {
- setOpen(false);
- }
- };
-
- const handleSuccess = async () => {
- setOpen(false);
- notify('common.response.success');
- };
-
- const handleError = async (error) => {
- notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
- };
-
- return (
- <CreateBase
- record={{ sceneCode: 'system_diagnose', priority: 10, enabledFlag: 1, status: 1 }}
- mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
- >
- <Dialog open={open} onClose={handleClose} fullWidth disableRestoreFocus maxWidth="md">
- <Form>
- <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- {translate('create.title')}
- <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
- <DialogCloseButton onClose={handleClose} />
- </Box>
- </DialogTitle>
- <DialogContent sx={{ mt: 2 }}>
- <Grid container rowSpacing={2} columnSpacing={2}>
- <Grid item xs={6}><SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="toolCode" label="宸ュ叿缂栫爜" fullWidth /></Grid>
- <Grid item xs={6}><TextInput source="toolName" label="宸ュ叿鍚嶇О" fullWidth /></Grid>
- <Grid item xs={6}><NumberInput source="priority" label="浼樺厛绾�" fullWidth /></Grid>
- <Grid item xs={6}><SelectInput source="enabledFlag" label="鍚敤" choices={enabledChoices} fullWidth /></Grid>
- <Grid item xs={6}><StatusSelectInput fullWidth /></Grid>
- <Grid item xs={12}><TextInput source="toolPrompt" label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={4} /></Grid>
- <Grid item xs={12}><Stack direction="column" spacing={1} width={'100%'}><MemoInput /></Stack></Grid>
- </Grid>
- </DialogContent>
- <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
- <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}>
- <SaveButton />
- </Toolbar>
- </DialogActions>
- </Form>
- </Dialog>
- </CreateBase>
- )
-}
-
-export default AiToolConfigCreate;
diff --git a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigEdit.jsx b/rsf-admin/src/page/system/aiToolConfig/AiToolConfigEdit.jsx
deleted file mode 100644
index fb23e7a..0000000
--- a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigEdit.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React from "react";
-import {
- Edit,
- SimpleForm,
- TextInput,
- NumberInput,
- SaveButton,
- SelectInput,
- Toolbar,
- DeleteButton,
-} from 'react-admin';
-import { Stack, Grid, Typography } from '@mui/material';
-import { EDIT_MODE } from '@/config/setting';
-import EditBaseAside from "@/page/components/EditBaseAside";
-import CustomerTopToolBar from "@/page/components/EditTopToolBar";
-import MemoInput from "@/page/components/MemoInput";
-import StatusSelectInput from "@/page/components/StatusSelectInput";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const enabledChoices = [
- { id: 1, name: '鍚敤' },
- { id: 0, name: '鍋滅敤' },
-];
-
-const FormToolbar = () => (
- <Toolbar sx={{ justifyContent: 'space-between' }}>
- <SaveButton />
- <DeleteButton mutationMode="optimistic" />
- </Toolbar>
-);
-
-const AiToolConfigEdit = () => (
- <Edit redirect="list" mutationMode={EDIT_MODE} actions={<CustomerTopToolBar />} aside={<EditBaseAside />}>
- <SimpleForm shouldUnregister warnWhenUnsavedChanges toolbar={<FormToolbar />} mode="onTouched">
- <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
- <Grid item xs={12} md={8}>
- <Typography variant="h6" gutterBottom>涓昏</Typography>
- <Stack direction='row' gap={2}>
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} />
- <TextInput source="toolCode" label="宸ュ叿缂栫爜" />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="toolName" label="宸ュ叿鍚嶇О" />
- <NumberInput source="priority" label="浼樺厛绾�" />
- </Stack>
- <Stack direction='row' gap={2}>
- <SelectInput source="enabledFlag" label="鍚敤" choices={enabledChoices} />
- </Stack>
- <Stack direction='row' gap={2}>
- <TextInput source="toolPrompt" label="宸ュ叿鎻愮ず璇�" fullWidth multiline minRows={4} />
- </Stack>
- </Grid>
- <Grid item xs={12} md={4}>
- <Typography variant="h6" gutterBottom>閫氱敤</Typography>
- <StatusSelectInput />
- <MemoInput />
- </Grid>
- </Grid>
- </SimpleForm>
- </Edit>
-)
-
-export default AiToolConfigEdit;
diff --git a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigList.jsx b/rsf-admin/src/page/system/aiToolConfig/AiToolConfigList.jsx
deleted file mode 100644
index ab8f376..0000000
--- a/rsf-admin/src/page/system/aiToolConfig/AiToolConfigList.jsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import React, { useState } from "react";
-import {
- List,
- SearchInput,
- TopToolbar,
- SelectColumnsButton,
- EditButton,
- FilterButton,
- TextInput,
- DateInput,
- SelectInput,
- useListContext,
- Pagination,
- DeleteButton,
-} from 'react-admin';
-import { Box, Button, Chip, Grid, Stack, Typography } from '@mui/material';
-import EmptyData from "@/page/components/EmptyData";
-import MyCreateButton from "@/page/components/MyCreateButton";
-import MyExportButton from '@/page/components/MyExportButton';
-import { OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import AiToolConfigCreate from "./AiToolConfigCreate";
-import { AiConsoleLayout, AiConsolePanel, aiCardSx } from "@/page/components/AiConsoleLayout";
-
-const sceneChoices = [
- { id: 'general_chat', name: '閫氱敤瀵硅瘽' },
- { id: 'system_diagnose', name: '绯荤粺璇婃柇' },
-];
-
-const filters = [
- <SearchInput source="condition" alwaysOn />,
- <DateInput label='common.time.after' source="timeStart" alwaysOn />,
- <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
- <SelectInput source="sceneCode" label="鍦烘櫙" choices={sceneChoices} />,
- <TextInput source="toolCode" label="宸ュ叿缂栫爜" />,
- <TextInput source="toolName" label="宸ュ叿鍚嶇О" />,
-];
-
-const ToolConfigBoard = () => {
- const { data, isLoading } = useListContext();
- const records = data || [];
- const enabledCount = records.filter((item) => item.enabledFlag === 1).length;
- const diagnoseCount = records.filter((item) => item.sceneCode === 'system_diagnose').length;
- const chatCount = records.filter((item) => item.sceneCode === 'general_chat').length;
-
- if (!isLoading && !records.length) {
- return <EmptyData />;
- }
-
- return (
- <AiConsoleLayout
- title="AI璇婃柇宸ュ叿涓績"
- subtitle="鍙傝�� zy-wcs-master 鐨勫伐浣滃尯甯冨眬锛屾彁渚涘満鏅寲宸ュ叿缂栨帓銆佸惎鍋滃拰鎻愮ず璇嶅寮猴紝浣嗘帶浠朵笌浜や簰浠嶄繚鎸佸綋鍓嶇郴缁熺殑 react-admin 椋庢牸銆�"
- stats={[
- { label: '宸ュ叿鎬绘暟', value: records.length },
- { label: '宸插惎鐢�', value: enabledCount },
- { label: '璇婃柇鍦烘櫙', value: diagnoseCount },
- { label: '閫氱敤瀵硅瘽', value: chatCount },
- ]}
- >
- <AiConsolePanel
- title="宸ュ叿缂栨帓"
- subtitle="鍚屼竴鍦烘櫙浼氭寜浼樺厛绾ф墽琛岋紝鍚敤鐘舵�佸拰宸ュ叿鎻愮ず璇嶄細鐩存帴杩涘叆璇婃柇杩愯鏃躲��"
- minHeight={420}
- >
- <Grid container spacing={2}>
- {records.map((record) => (
- <Grid item xs={12} md={6} xl={4} key={record.id}>
- <Box sx={aiCardSx(record.enabledFlag === 1)}>
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
- <Box>
- <Typography variant="subtitle1" sx={{ fontWeight: 700, color: '#284059' }}>
- {record.toolName || record.toolCode}
- </Typography>
- <Typography variant="caption" sx={{ color: '#8093a8' }}>
- {record.sceneCode} 路 {record.toolCode}
- </Typography>
- </Box>
- <Stack direction="row" spacing={0.75} flexWrap="wrap" justifyContent="flex-end">
- <Chip size="small" color={record.enabledFlag === 1 ? 'success' : 'default'} label={record.enabledFlag === 1 ? '鍚敤' : '鍋滅敤'} />
- <Chip size="small" color={record.status === 1 ? 'primary' : 'default'} label={`P${record.priority || 0}`} />
- </Stack>
- </Stack>
- <Box sx={{ mt: 1.5 }}>
- <Typography variant="caption" sx={{ color: '#70839a' }}>宸ュ叿鎻愮ず璇�</Typography>
- <Typography variant="body2" sx={{ mt: 0.5, color: '#31465d', minHeight: 72 }}>
- {record.toolPrompt || '鏈厤缃檮鍔犳彁绀鸿瘝锛屽皢鍙娇鐢ㄩ粯璁ゅ伐鍏锋憳瑕併��'}
- </Typography>
- </Box>
- <Stack direction="row" spacing={1} sx={{ mt: 1.5 }}>
- <EditButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} />
- <DeleteButton record={record} sx={{ px: 1.25, py: 0.5, minWidth: 64 }} mutationMode={OPERATE_MODE} />
- </Stack>
- </Box>
- </Grid>
- ))}
- </Grid>
- <Box sx={{ mt: 2 }}>
- <Pagination rowsPerPageOptions={[DEFAULT_PAGE_SIZE, 25, 50]} />
- </Box>
- </AiConsolePanel>
- </AiConsoleLayout>
- );
-};
-
-const AiToolConfigList = () => {
- const [createDialog, setCreateDialog] = useState(false);
-
- return (
- <Box display="flex" sx={{ width: '100%' }}>
- <List
- sx={{ width: '100%', flexGrow: 1 }}
- title={"menu.aiToolConfig"}
- empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
- filters={filters}
- sort={{ field: "priority", order: "asc" }}
- actions={(
- <TopToolbar>
- <FilterButton />
- <MyCreateButton onClick={() => { setCreateDialog(true) }} />
- <SelectColumnsButton preferenceKey='aiToolConfig' />
- <MyExportButton />
- </TopToolbar>
- )}
- perPage={DEFAULT_PAGE_SIZE}
- pagination={false}
- >
- <ToolConfigBoard />
- </List>
- <AiToolConfigCreate open={createDialog} setOpen={setCreateDialog} />
- </Box>
- )
-}
-
-export default AiToolConfigList;
diff --git a/rsf-admin/src/page/system/aiToolConfig/index.jsx b/rsf-admin/src/page/system/aiToolConfig/index.jsx
deleted file mode 100644
index 9532f87..0000000
--- a/rsf-admin/src/page/system/aiToolConfig/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {
- ShowGuesser,
-} from "react-admin";
-
-import AiToolConfigList from "./AiToolConfigList";
-import AiToolConfigEdit from "./AiToolConfigEdit";
-
-export default {
- list: AiToolConfigList,
- edit: AiToolConfigEdit,
- show: ShowGuesser,
- recordRepresentation: (record) => `${record.toolName || record.toolCode || ''}`
-};
diff --git a/rsf-ai-gateway/gateway-run.log b/rsf-ai-gateway/gateway-run.log
deleted file mode 100644
index 23a675c..0000000
--- a/rsf-ai-gateway/gateway-run.log
+++ /dev/null
@@ -1,60 +0,0 @@
-[INFO] Scanning for projects...
-[WARNING]
-[WARNING] Some problems were encountered while building the effective model for com.vincent:rsf-server:jar:1.0.0
-[WARNING] 'dependencies.dependency.systemPath' for RouteUtils:RouteUtils:jar should not point at files within the project directory, ${project.basedir}/src/main/resources/lib/RouteUtils.jar will be unresolvable by dependent projects @ line 41, column 16
-[WARNING]
-[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
-[WARNING]
-[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
-[WARNING]
-[INFO]
-[INFO] ---------------------< com.vincent:rsf-ai-gateway >---------------------
-[INFO] Building rsf-ai-gateway 1.0.0
-[INFO] from pom.xml
-[INFO] --------------------------------[ jar ]---------------------------------
-[INFO]
-[INFO] >>> spring-boot:2.5.3:run (default-cli) > test-compile @ rsf-ai-gateway >>>
-[INFO]
-[INFO] --- resources:3.2.0:resources (default-resources) @ rsf-ai-gateway ---
-[INFO] Using 'UTF-8' encoding to copy filtered resources.
-[INFO] Using 'UTF-8' encoding to copy filtered properties files.
-[INFO] Copying 1 resource
-[INFO] Copying 0 resource
-[INFO]
-[INFO] --- compiler:3.8.1:compile (default-compile) @ rsf-ai-gateway ---
-[INFO] Nothing to compile - all classes are up to date
-[INFO]
-[INFO] --- resources:3.2.0:testResources (default-testResources) @ rsf-ai-gateway ---
-[INFO] Using 'UTF-8' encoding to copy filtered resources.
-[INFO] Using 'UTF-8' encoding to copy filtered properties files.
-[INFO] skip non existing resourceDirectory C:\env\code\wms-master\rsf-ai-gateway\src\test\resources
-[INFO]
-[INFO] --- compiler:3.8.1:testCompile (default-testCompile) @ rsf-ai-gateway ---
-[INFO] No sources to compile
-[INFO]
-[INFO] <<< spring-boot:2.5.3:run (default-cli) < test-compile @ rsf-ai-gateway <<<
-[INFO]
-[INFO]
-[INFO] --- spring-boot:2.5.3:run (default-cli) @ rsf-ai-gateway ---
-[INFO] Attaching agents: []
-
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
-( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v2.5.3)
-
-2026-03-11 15:04:08.306 INFO 23404 --- [ main] com.vincent.rsf.ai.gateway.GatewayBoot : Starting GatewayBoot using Java 17.0.14 on WIN-P7MH6EA0OTE with PID 23404 (C:\env\code\wms-master\rsf-ai-gateway\target\classes started by Administrator in C:\env\code\wms-master\rsf-ai-gateway)
-2026-03-11 15:04:08.307 INFO 23404 --- [ main] com.vincent.rsf.ai.gateway.GatewayBoot : No active profile set, falling back to default profiles: default
-2026-03-11 15:04:08.788 INFO 23404 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8086 (http)
-2026-03-11 15:04:08.795 INFO 23404 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
-2026-03-11 15:04:08.795 INFO 23404 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.50]
-2026-03-11 15:04:08.835 INFO 23404 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
-2026-03-11 15:04:08.835 INFO 23404 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 500 ms
-2026-03-11 15:04:09.014 INFO 23404 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8086 (http) with context path ''
-2026-03-11 15:04:09.019 INFO 23404 --- [ main] com.vincent.rsf.ai.gateway.GatewayBoot : Started GatewayBoot in 0.93 seconds (JVM running for 1.099)
-2026-03-11 15:04:33.345 INFO 23404 --- [nio-8086-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
-2026-03-11 15:04:33.345 INFO 23404 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
-2026-03-11 15:04:33.346 INFO 23404 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
diff --git a/rsf-ai-gateway/pom.xml b/rsf-ai-gateway/pom.xml
deleted file mode 100644
index 77581f5..0000000
--- a/rsf-ai-gateway/pom.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <name>rsf-ai-gateway</name>
- <artifactId>rsf-ai-gateway</artifactId>
- <version>1.0.0</version>
- <packaging>jar</packaging>
-
- <parent>
- <groupId>com.vincent</groupId>
- <artifactId>rsf</artifactId>
- <version>1.0.0</version>
- </parent>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <finalName>rsf-ai-gateway</finalName>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/GatewayBoot.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/GatewayBoot.java
deleted file mode 100644
index 473b564..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/GatewayBoot.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.vincent.rsf.ai.gateway;
-
-import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.SpringApplication;
-
-@SpringBootApplication(exclude = {
- DataSourceAutoConfiguration.class,
- DruidDataSourceAutoConfigure.class
-})
-public class GatewayBoot {
-
- public static void main(String[] args) {
- SpringApplication.run(GatewayBoot.class, args);
- }
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/AiGatewayProperties.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/AiGatewayProperties.java
deleted file mode 100644
index edc5d5f..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/AiGatewayProperties.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.vincent.rsf.ai.gateway.config;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Data
-@Configuration
-@ConfigurationProperties(prefix = "gateway.ai")
-public class AiGatewayProperties {
-
- private String defaultModelCode = "deepseek-ai/DeepSeek-V3.2";
-
- private Integer connectTimeoutMillis = 10000;
-
- private Integer readTimeoutMillis = 0;
-
- private List<ModelConfig> models = new ArrayList<>();
-
- @Data
- public static class ModelConfig {
- private String code;
- private String name;
- private String provider = "mock";
- private String chatUrl;
- private String apiKey;
- private String modelName;
- private Boolean enabled = true;
- }
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/WebAsyncConfig.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/WebAsyncConfig.java
deleted file mode 100644
index 0c07bc3..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/config/WebAsyncConfig.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.vincent.rsf.ai.gateway.config;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-@Configuration
-public class WebAsyncConfig implements WebMvcConfigurer {
-
- @Override
- public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
- // StreamingResponseBody is used for long-lived model streams; disable the MVC async timeout.
- configurer.setDefaultTimeout(0L);
- }
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/controller/AiGatewayController.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/controller/AiGatewayController.java
deleted file mode 100644
index 5f72929..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/controller/AiGatewayController.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package com.vincent.rsf.ai.gateway.controller;
-
-import com.vincent.rsf.ai.gateway.dto.GatewayChatRequest;
-import com.vincent.rsf.ai.gateway.service.AiGatewayService;
-import com.vincent.rsf.ai.gateway.service.GatewayStreamEvent;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
-
-import jakarta.annotation.Resource;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@RestController
-@RequestMapping("/internal/chat")
-public class AiGatewayController {
-
- private static final Logger logger = LoggerFactory.getLogger(AiGatewayController.class);
-
- @Resource
- private AiGatewayService aiGatewayService;
- @Resource
- private ObjectMapper objectMapper;
-
- @PostMapping(value = "/stream", produces = "application/x-ndjson")
- public StreamingResponseBody stream(@RequestBody GatewayChatRequest request) {
- return outputStream -> {
- logger.info("AI gateway controller stream opened: sessionId={}, routeCode={}, attemptNo={}, modelCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode());
- AtomicBoolean streaming = new AtomicBoolean(true);
- Object writeLock = new Object();
- Thread heartbeatThread = new Thread(() -> {
- while (streaming.get()) {
- try {
- Thread.sleep(10000L);
- if (!streaming.get()) {
- break;
- }
- String json = objectMapper.writeValueAsString(new GatewayStreamEvent()
- .setType("ping")
- .setModelCode(request.getModelCode())
- .setResponseTime(System.currentTimeMillis())) + "\n";
- synchronized (writeLock) {
- outputStream.write(json.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- logger.info("AI gateway heartbeat interrupted: sessionId={}, routeCode={}, attemptNo={}, modelCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode());
- break;
- } catch (Exception e) {
- logger.warn("AI gateway heartbeat write failed: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, message={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode(),
- e.getMessage());
- break;
- }
- }
- }, "ai-gateway-heartbeat-" + (request.getSessionId() == null ? "unknown" : request.getSessionId()));
- heartbeatThread.setDaemon(true);
- heartbeatThread.start();
- try {
- aiGatewayService.stream(request, event -> {
- String json = objectMapper.writeValueAsString(event) + "\n";
- synchronized (writeLock) {
- outputStream.write(json.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- }
- });
- } catch (Exception e) {
- if (isInterruptedError(e)) {
- logger.warn("AI gateway controller stream interrupted: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, message={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode(),
- e.getMessage());
- return;
- }
- logger.error("AI gateway controller stream failed: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, message={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode(),
- e.getMessage(),
- e);
- throw new IOException(e);
- } finally {
- streaming.set(false);
- heartbeatThread.interrupt();
- logger.info("AI gateway controller stream closed: sessionId={}, routeCode={}, attemptNo={}, modelCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode());
- }
- };
- }
-
- private boolean isInterruptedError(Throwable throwable) {
- Throwable current = throwable;
- while (current != null) {
- if (current instanceof InterruptedException || current instanceof InterruptedIOException) {
- return true;
- }
- String message = current.getMessage();
- if (message != null) {
- String normalized = message.toLowerCase();
- if (normalized.contains("interrupted")
- || normalized.contains("broken pipe")
- || normalized.contains("connection reset")
- || normalized.contains("forcibly closed")) {
- return true;
- }
- }
- current = current.getCause();
- }
- return false;
- }
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatMessage.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatMessage.java
deleted file mode 100644
index fcf8f78..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatMessage.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.vincent.rsf.ai.gateway.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class GatewayChatMessage implements Serializable {
-
- private String role;
-
- private String content;
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatRequest.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatRequest.java
deleted file mode 100644
index 668d45b..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/dto/GatewayChatRequest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.vincent.rsf.ai.gateway.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-@Data
-public class GatewayChatRequest implements Serializable {
-
- private String sessionId;
-
- private String modelCode;
-
- private String routeCode;
-
- private Integer attemptNo;
-
- private String systemPrompt;
-
- private String chatUrl;
-
- private String apiKey;
-
- private String modelName;
-
- private List<GatewayChatMessage> messages = new ArrayList<>();
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/AiGatewayService.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/AiGatewayService.java
deleted file mode 100644
index 74420e0..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/AiGatewayService.java
+++ /dev/null
@@ -1,477 +0,0 @@
-package com.vincent.rsf.ai.gateway.service;
-
-import com.vincent.rsf.ai.gateway.config.AiGatewayProperties;
-import com.vincent.rsf.ai.gateway.dto.GatewayChatMessage;
-import com.vincent.rsf.ai.gateway.dto.GatewayChatRequest;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiGatewayService {
-
- private static final Logger logger = LoggerFactory.getLogger(AiGatewayService.class);
-
- @Resource
- private AiGatewayProperties aiGatewayProperties;
- @Resource
- private ObjectMapper objectMapper;
-
- public interface EventConsumer {
- void accept(GatewayStreamEvent event) throws Exception;
- }
-
- public void stream(GatewayChatRequest request, EventConsumer consumer) throws Exception {
- AiGatewayProperties.ModelConfig modelConfig = resolveModel(request);
- logger.info("AI gateway stream start: sessionId={}, routeCode={}, attemptNo={}, requestModelCode={}, resolvedModelCode={}, provider={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- request.getModelCode(),
- modelConfig == null ? null : modelConfig.getCode(),
- modelConfig == null ? null : modelConfig.getProvider());
- if (modelConfig == null || modelConfig.getChatUrl() == null || modelConfig.getChatUrl().trim().isEmpty()) {
- logger.info("AI gateway use mock stream: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, provider={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig == null ? request.getModelCode() : modelConfig.getCode(),
- modelConfig == null ? "mock" : modelConfig.getProvider());
- mockStream(request, modelConfig, consumer);
- return;
- }
- openAiCompatibleStream(request, modelConfig, consumer);
- }
-
- private AiGatewayProperties.ModelConfig resolveModel(GatewayChatRequest request) {
- String modelCode = request.getModelCode();
- String targetCode = (modelCode == null || modelCode.trim().isEmpty())
- ? aiGatewayProperties.getDefaultModelCode()
- : modelCode;
- for (AiGatewayProperties.ModelConfig model : aiGatewayProperties.getModels()) {
- if (Boolean.TRUE.equals(model.getEnabled()) && targetCode.equals(model.getCode())) {
- return mergeRequestOverride(model, request);
- }
- }
- if ((request.getChatUrl() != null && !request.getChatUrl().trim().isEmpty())
- || (request.getModelName() != null && !request.getModelName().trim().isEmpty())) {
- AiGatewayProperties.ModelConfig modelConfig = new AiGatewayProperties.ModelConfig();
- modelConfig.setCode(targetCode);
- modelConfig.setName(targetCode);
- modelConfig.setProvider("custom");
- modelConfig.setEnabled(true);
- return mergeRequestOverride(modelConfig, request);
- }
- return null;
- }
-
- private AiGatewayProperties.ModelConfig mergeRequestOverride(AiGatewayProperties.ModelConfig source,
- GatewayChatRequest request) {
- AiGatewayProperties.ModelConfig target = new AiGatewayProperties.ModelConfig();
- target.setCode(source.getCode());
- target.setName(source.getName());
- target.setProvider(source.getProvider());
- target.setChatUrl(normalizeChatUrl(source.getChatUrl()));
- target.setApiKey(source.getApiKey());
- target.setModelName(source.getModelName());
- target.setEnabled(source.getEnabled());
- if (request.getChatUrl() != null && !request.getChatUrl().trim().isEmpty()) {
- target.setChatUrl(normalizeChatUrl(request.getChatUrl().trim()));
- }
- if (request.getApiKey() != null && !request.getApiKey().trim().isEmpty()) {
- target.setApiKey(request.getApiKey().trim());
- }
- if (request.getModelName() != null && !request.getModelName().trim().isEmpty()) {
- target.setModelName(request.getModelName().trim());
- }
- return target;
- }
-
- private void mockStream(GatewayChatRequest request, AiGatewayProperties.ModelConfig modelConfig,
- EventConsumer consumer) throws Exception {
- long requestTime = System.currentTimeMillis();
- String modelCode = modelConfig == null ? aiGatewayProperties.getDefaultModelCode() : modelConfig.getCode();
- String lastQuestion = "";
- List<GatewayChatMessage> messages = request.getMessages();
- for (int i = messages.size() - 1; i >= 0; i--) {
- GatewayChatMessage message = messages.get(i);
- if ("user".equalsIgnoreCase(message.getRole())) {
- lastQuestion = message.getContent();
- break;
- }
- }
- String answer = "褰撳墠涓烘紨绀烘ā寮忥紝妯″瀷[" + modelCode + "]宸叉敹鍒颁綘鐨勯棶棰橈細" + lastQuestion;
- logger.info("AI gateway mock stream emitting response: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, answerLength={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelCode,
- answer.length());
- for (char c : answer.toCharArray()) {
- consumer.accept(new GatewayStreamEvent()
- .setType("delta")
- .setModelCode(modelCode)
- .setContent(String.valueOf(c)));
- try {
- Thread.sleep(20L);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- consumer.accept(new GatewayStreamEvent()
- .setType("done")
- .setModelCode(modelCode)
- .setSuccess(true)
- .setRequestTime(requestTime)
- .setResponseTime(System.currentTimeMillis())
- .setDurationMs(System.currentTimeMillis() - requestTime));
- logger.info("AI gateway mock stream completed: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, durationMs={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelCode,
- System.currentTimeMillis() - requestTime);
- }
-
- private void openAiCompatibleStream(GatewayChatRequest request, AiGatewayProperties.ModelConfig modelConfig,
- EventConsumer consumer) throws Exception {
- HttpURLConnection connection = null;
- long requestTime = System.currentTimeMillis();
- boolean terminalEventSent = false;
- int eventLineCount = 0;
- int deltaCount = 0;
- int contentChars = 0;
- boolean firstDeltaLogged = false;
- String normalizedUrl = modelConfig == null ? null : modelConfig.getChatUrl();
- try {
- logger.info("AI gateway opening upstream stream: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, provider={}, url={}, modelName={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig == null ? null : modelConfig.getCode(),
- modelConfig == null ? null : modelConfig.getProvider(),
- normalizedUrl,
- modelConfig == null ? null : modelConfig.getModelName());
- connection = (HttpURLConnection) new URL(modelConfig.getChatUrl()).openConnection();
- connection.setConnectTimeout(aiGatewayProperties.getConnectTimeoutMillis());
- connection.setReadTimeout(aiGatewayProperties.getReadTimeoutMillis());
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("Accept", "text/event-stream");
- if (modelConfig.getApiKey() != null && !modelConfig.getApiKey().trim().isEmpty()) {
- connection.setRequestProperty("Authorization", "Bearer " + modelConfig.getApiKey().trim());
- }
-
- Map<String, Object> body = new LinkedHashMap<>();
- body.put("model", modelConfig.getModelName());
- body.put("stream", true);
- body.put("messages", buildMessages(request));
-
- try (OutputStream outputStream = connection.getOutputStream()) {
- outputStream.write(objectMapper.writeValueAsBytes(body));
- outputStream.flush();
- }
-
- int statusCode = connection.getResponseCode();
- logger.info("AI gateway upstream response received: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, statusCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- statusCode);
- InputStream inputStream = statusCode >= 400 ? connection.getErrorStream() : connection.getInputStream();
- if (inputStream == null) {
- logger.warn("AI gateway upstream returned empty stream: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, url={}, statusCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- normalizedUrl,
- statusCode);
- consumer.accept(new GatewayStreamEvent()
- .setType("error")
- .setModelCode(modelConfig.getCode())
- .setMessage("妯″瀷鏈嶅姟鏃犲搷搴�")
- .setSuccess(false)
- .setRequestTime(requestTime)
- .setResponseTime(System.currentTimeMillis())
- .setDurationMs(System.currentTimeMillis() - requestTime));
- terminalEventSent = true;
- return;
- }
- if (statusCode >= 400) {
- logger.warn("AI gateway upstream http error: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, url={}, statusCode={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- normalizedUrl,
- statusCode);
- consumer.accept(new GatewayStreamEvent()
- .setType("error")
- .setModelCode(modelConfig.getCode())
- .setMessage(readErrorMessage(inputStream, statusCode))
- .setSuccess(false)
- .setRequestTime(requestTime)
- .setResponseTime(System.currentTimeMillis())
- .setDurationMs(System.currentTimeMillis() - requestTime));
- terminalEventSent = true;
- return;
- }
-
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- String line;
- while ((line = reader.readLine()) != null) {
- if (line.trim().isEmpty() || !line.startsWith("data:")) {
- continue;
- }
- eventLineCount++;
- String payload = line.substring(5).trim();
- if ("[DONE]".equals(payload)) {
- long responseTime = System.currentTimeMillis();
- logger.info("AI gateway upstream done marker received: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, eventLines={}, deltaCount={}, contentChars={}, durationMs={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- eventLineCount,
- deltaCount,
- contentChars,
- responseTime - requestTime);
- consumer.accept(new GatewayStreamEvent()
- .setType("done")
- .setModelCode(modelConfig.getCode())
- .setSuccess(true)
- .setRequestTime(requestTime)
- .setResponseTime(responseTime)
- .setDurationMs(responseTime - requestTime));
- terminalEventSent = true;
- break;
- }
- JsonNode root = objectMapper.readTree(payload);
- JsonNode choice = root.path("choices").path(0);
- JsonNode delta = choice.path("delta");
- JsonNode contentNode = delta.path("content");
- if (!contentNode.isMissingNode() && !contentNode.isNull()) {
- String content = contentNode.asText();
- deltaCount++;
- contentChars += content.length();
- if (!firstDeltaLogged) {
- logger.info("AI gateway upstream first delta received: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, afterMs={}, sampleLength={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- System.currentTimeMillis() - requestTime,
- content.length());
- firstDeltaLogged = true;
- }
- consumer.accept(new GatewayStreamEvent()
- .setType("delta")
- .setModelCode(modelConfig.getCode())
- .setContent(content));
- }
- JsonNode finishReason = choice.path("finish_reason");
- if (!finishReason.isMissingNode() && !finishReason.isNull()) {
- long responseTime = System.currentTimeMillis();
- logger.info("AI gateway upstream finish_reason received: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, finishReason={}, eventLines={}, deltaCount={}, contentChars={}, durationMs={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- finishReason.asText(),
- eventLineCount,
- deltaCount,
- contentChars,
- responseTime - requestTime);
- consumer.accept(new GatewayStreamEvent()
- .setType("done")
- .setModelCode(modelConfig.getCode())
- .setSuccess(true)
- .setRequestTime(requestTime)
- .setResponseTime(responseTime)
- .setDurationMs(responseTime - requestTime));
- terminalEventSent = true;
- break;
- }
- }
- }
- if (!terminalEventSent) {
- long responseTime = System.currentTimeMillis();
- logger.warn("AI gateway upstream ended without terminal event: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, url={}, eventLines={}, deltaCount={}, contentChars={}, durationMs={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig.getCode(),
- normalizedUrl,
- eventLineCount,
- deltaCount,
- contentChars,
- responseTime - requestTime);
- consumer.accept(new GatewayStreamEvent()
- .setType("error")
- .setModelCode(modelConfig.getCode())
- .setMessage("妯″瀷娴佸紓甯镐腑鏂�")
- .setSuccess(false)
- .setRequestTime(requestTime)
- .setResponseTime(responseTime)
- .setDurationMs(responseTime - requestTime));
- }
- } catch (Exception e) {
- if (isInterruptedError(e)) {
- logger.warn("AI gateway upstream interrupted: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, url={}, stage={}, message={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig == null ? null : modelConfig.getCode(),
- normalizedUrl,
- terminalEventSent ? "after_terminal" : "streaming",
- e.getMessage());
- if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- return;
- }
- logger.error("AI gateway upstream exception: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, url={}, eventLines={}, deltaCount={}, contentChars={}, message={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig == null ? null : modelConfig.getCode(),
- normalizedUrl,
- eventLineCount,
- deltaCount,
- contentChars,
- e.getMessage(),
- e);
- consumer.accept(new GatewayStreamEvent()
- .setType("error")
- .setModelCode(modelConfig.getCode())
- .setMessage(e.getMessage())
- .setSuccess(false)
- .setRequestTime(requestTime)
- .setResponseTime(System.currentTimeMillis())
- .setDurationMs(System.currentTimeMillis() - requestTime));
- } finally {
- if (connection != null) {
- connection.disconnect();
- }
- logger.info("AI gateway upstream stream closed: sessionId={}, routeCode={}, attemptNo={}, modelCode={}, terminalEventSent={}, eventLines={}, deltaCount={}, contentChars={}",
- request.getSessionId(),
- request.getRouteCode(),
- request.getAttemptNo(),
- modelConfig == null ? null : modelConfig.getCode(),
- terminalEventSent,
- eventLineCount,
- deltaCount,
- contentChars);
- }
- }
-
- private List<Map<String, String>> buildMessages(GatewayChatRequest request) {
- List<Map<String, String>> output = new ArrayList<>();
- if (request.getSystemPrompt() != null && !request.getSystemPrompt().trim().isEmpty()) {
- Map<String, String> systemMessage = new LinkedHashMap<>();
- systemMessage.put("role", "system");
- systemMessage.put("content", request.getSystemPrompt());
- output.add(systemMessage);
- }
- for (GatewayChatMessage message : request.getMessages()) {
- Map<String, String> item = new LinkedHashMap<>();
- item.put("role", message.getRole());
- item.put("content", message.getContent());
- output.add(item);
- }
- return output;
- }
-
- private String normalizeChatUrl(String chatUrl) {
- if (chatUrl == null) {
- return null;
- }
- String normalized = chatUrl.trim();
- if (normalized.isEmpty()) {
- return normalized;
- }
- if (normalized.endsWith("/chat/completions") || normalized.endsWith("/v1/chat/completions")) {
- return normalized;
- }
- if (normalized.endsWith("/v1")) {
- return normalized + "/chat/completions";
- }
- if (normalized.contains("/v1/")) {
- return normalized;
- }
- if (normalized.endsWith("/")) {
- return normalized + "v1/chat/completions";
- }
- return normalized + "/v1/chat/completions";
- }
-
- private String readErrorMessage(InputStream inputStream, int statusCode) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- StringBuilder builder = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- builder.append(line);
- }
- String body = builder.toString();
- if (body.isEmpty()) {
- return "妯″瀷鏈嶅姟璋冪敤澶辫触锛岀姸鎬佺爜锛�" + statusCode;
- }
- JsonNode root = objectMapper.readTree(body);
- JsonNode errorNode = root.path("error");
- if (!errorNode.isMissingNode() && !errorNode.isNull()) {
- String message = errorNode.path("message").asText("");
- if (!message.isEmpty()) {
- return message;
- }
- }
- if (root.path("message").isTextual()) {
- return root.path("message").asText();
- }
- return body;
- } catch (Exception ignore) {
- return "妯″瀷鏈嶅姟璋冪敤澶辫触锛岀姸鎬佺爜锛�" + statusCode;
- }
- }
-
- private boolean isInterruptedError(Throwable throwable) {
- Throwable current = throwable;
- while (current != null) {
- if (current instanceof InterruptedException || current instanceof InterruptedIOException) {
- return true;
- }
- String message = current.getMessage();
- if (message != null) {
- String normalized = message.toLowerCase();
- if (normalized.contains("interrupted")
- || normalized.contains("broken pipe")
- || normalized.contains("connection reset")
- || normalized.contains("forcibly closed")) {
- return true;
- }
- }
- current = current.getCause();
- }
- return false;
- }
-
-}
diff --git a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/GatewayStreamEvent.java b/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/GatewayStreamEvent.java
deleted file mode 100644
index 2f71f3e..0000000
--- a/rsf-ai-gateway/src/main/java/com/vincent/rsf/ai/gateway/service/GatewayStreamEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.vincent.rsf.ai.gateway.service;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-@Data
-@Accessors(chain = true)
-public class GatewayStreamEvent implements Serializable {
-
- private String type;
-
- private String content;
-
- private String message;
-
- private String modelCode;
-
- private Boolean success;
-
- private Long requestTime;
-
- private Long responseTime;
-
- private Long durationMs;
-
-}
diff --git a/rsf-ai-gateway/src/main/resources/application.yml b/rsf-ai-gateway/src/main/resources/application.yml
deleted file mode 100644
index 68c956a..0000000
--- a/rsf-ai-gateway/src/main/resources/application.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-server:
- port: 8086
-
-spring:
- application:
- name: rsf-ai-gateway
-
-gateway:
- ai:
- default-model-code: deepseek-ai/DeepSeek-V3.2
- connect-timeout-millis: 10000
- read-timeout-millis: 0
- models:
- - code: deepseek-ai/DeepSeek-V3.2
- name: DEEPSEEK
- provider: openai
- chat-url: https://api.siliconflow.cn
- api-key:
- model-name: deepseek-ai/DeepSeek-V3.2
- enabled: true
diff --git a/rsf-server/skills/rsf-server-maintainer/agents/openai.yaml b/rsf-server/skills/rsf-server-maintainer/agents/openai.yaml
deleted file mode 100644
index 363026c..0000000
--- a/rsf-server/skills/rsf-server-maintainer/agents/openai.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-interface:
- display_name: "RSF Server Maintainer"
- short_description: "Maintain RSF server backend workflows"
- default_prompt: "Use $rsf-server-maintainer to analyze and safely modify the RSF server backend codebase."
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiProperties.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiProperties.java
deleted file mode 100644
index 4d85462..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiProperties.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.vincent.rsf.server.ai.config;
-
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Data
-@Configuration
-@ConfigurationProperties(prefix = "ai")
-public class AiProperties {
-
- private String gatewayBaseUrl = "http://127.0.0.1:8086";
-
- private Integer sessionTtlSeconds = 86400;
-
- private Integer maxContextMessages = 12;
-
- private String systemPrompt = "浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂浼樺厛淇濇寔鍑嗙‘銆佺畝娲侊紝骞剁粨鍚堜笂涓嬫枃甯姪鐢ㄦ埛鐞嗚В浠撳偍涓氬姟銆�";
-
- private String diagnosisSystemPrompt = "浣犳槸涓�鍚嶈祫娣盬MS鏅鸿兘璇婃柇鍔╂墜锛岀洰鏍囨槸缁撳悎褰撳墠绯荤粺涓婁笅鏂囧浠撳簱杩愯鎯呭喌鍋氬贰妫�鍒嗘瀽銆�"
- + "鍥炵瓟鏃剁姝㈠嚟绌虹寽娴嬶紝蹇呴』浼樺厛渚濇嵁鎻愪緵鐨勫疄鏃舵憳瑕佽繘琛屽垽鏂��"
- + "璇蜂紭鍏堟寜浠ヤ笅椤哄簭鍒嗘瀽锛氬厛鎬荤粨搴撳瓨銆佷换鍔°�佽澶囩珯鐐圭殑瀹炴椂鐘舵�侊紝鎸囧嚭鏄惁瀛樺湪鏄庢樉寮傚父锛�"
- + "濡傛灉鍙戠幇寮傚父锛岃缁欏嚭寮傚父鐜拌薄銆佸彲鑳藉師鍥犮�佸奖鍝嶈寖鍥淬�佸缓璁鐞嗘楠わ紱"
- + "濡傛灉鏁版嵁姝e父锛岃鏄庣‘璇存槑褰撳墠鏈彂鐜版槑鏄惧紓甯革紝骞舵彁閱掍粛闇�浜哄伐缁撳悎鐜板満鐘舵�佸鏍搞��"
- + "鍥炵瓟灏介噺寮曠敤浣犳嬁鍒扮殑瀹炴椂鏁版嵁锛屼笉瑕佺紪閫犳湭鏌ヨ鍒扮殑璁惧鐘舵�佹垨涓氬姟浜嬪疄銆�"
- + "璇锋寜鈥滈棶棰樻杩般�佸叧閿瘉鎹�佸彲鑳藉師鍥犮�佸缓璁姩浣溿�侀闄╄瘎浼扳�濈殑缁撴瀯杈撳嚭锛屽苟浼樺厛缁欏嚭鍙墽琛屽缓璁��";
-
- private Integer routeFailThreshold = 3;
-
- private Integer routeCooldownMinutes = 10;
-
- private Integer diagnosticLogWindowHours = 24;
-
- private Integer apiFailureWindowHours = 24;
-
- private String defaultModelCode = "deepseek-ai/DeepSeek-V3.2";
-
- private List<ModelConfig> models = new ArrayList<>();
-
- public List<ModelConfig> getEnabledModels() {
- return models.stream().filter(model -> Boolean.TRUE.equals(model.getEnabled())).collect(Collectors.toList());
- }
-
- public String resolveDefaultModelCode() {
- if (defaultModelCode != null && !defaultModelCode.trim().isEmpty()) {
- return defaultModelCode;
- }
- return getEnabledModels().isEmpty() ? "deepseek-ai/DeepSeek-V3.2" : getEnabledModels().get(0).getCode();
- }
-
- public String buildScenePrompt(String sceneCode, String basePrompt) {
- String prompt = basePrompt == null ? null : basePrompt.trim();
- if (AiSceneCode.SYSTEM_DIAGNOSE.equals(sceneCode)) {
- if (prompt == null || prompt.isEmpty()) {
- return diagnosisSystemPrompt;
- }
- return prompt + "\n\n" + diagnosisSystemPrompt;
- }
- return prompt;
- }
-
- @Data
- public static class ModelConfig {
- private String code;
- private String name;
- private String provider;
- private Boolean enabled = true;
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiSchemaGuard.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiSchemaGuard.java
deleted file mode 100644
index eb8a033..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/config/AiSchemaGuard.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.vincent.rsf.server.ai.config;
-
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.Resource;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.util.Arrays;
-import java.util.List;
-
-@Component
-public class AiSchemaGuard {
-
- private static final List<String> REQUIRED_TABLES = Arrays.asList(
- "sys_ai_chat_session",
- "sys_ai_chat_message",
- "sys_ai_prompt_template",
- "sys_ai_prompt_publish_log",
- "sys_ai_diagnosis_record",
- "sys_ai_diagnosis_plan",
- "sys_ai_call_log",
- "sys_ai_mcp_mount",
- "sys_ai_model_route",
- "sys_ai_diagnostic_tool_config"
- );
-
- @Resource
- private DataSource dataSource;
-
- @PostConstruct
- public void validate() {
- try (Connection connection = dataSource.getConnection()) {
- for (String table : REQUIRED_TABLES) {
- if (!tableExists(connection, table)) {
- throw new IllegalStateException("AI feature table missing: " + table + "锛岃鍏堟墽琛� AI 杩佺Щ鑴氭湰");
- }
- }
- } catch (IllegalStateException e) {
- throw e;
- } catch (Exception e) {
- throw new IllegalStateException("AI feature schema validation failed: " + e.getMessage(), e);
- }
- }
-
- private boolean tableExists(Connection connection, String tableName) throws Exception {
- try (ResultSet resultSet = connection.getMetaData().getTables(connection.getCatalog(), null, tableName, null)) {
- if (resultSet.next()) {
- return true;
- }
- }
- try (ResultSet resultSet = connection.getMetaData().getTables(connection.getCatalog(), null, tableName.toUpperCase(), null)) {
- if (resultSet.next()) {
- return true;
- }
- }
- try (ResultSet resultSet = connection.getMetaData().getTables(connection.getCatalog(), null, tableName.toLowerCase(), null)) {
- return resultSet.next();
- }
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiMcpConstants.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiMcpConstants.java
deleted file mode 100644
index 4d71d3b..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiMcpConstants.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.vincent.rsf.server.ai.constant;
-
-public class AiMcpConstants {
-
- public static final String DEFAULT_LOCAL_MOUNT_CODE = "wms_local";
- public static final String DEFAULT_LOCAL_MOUNT_NAME = "WMS鏈湴MCP";
- public static final String TRANSPORT_AUTO = "AUTO";
- public static final String TRANSPORT_INTERNAL = "INTERNAL";
- public static final String TRANSPORT_HTTP = "HTTP";
- public static final String TRANSPORT_SSE = "SSE";
- public static final String USAGE_SCOPE_DIAGNOSE_ONLY = "DIAGNOSE_ONLY";
- public static final String USAGE_SCOPE_CHAT_AND_DIAGNOSE = "CHAT_AND_DIAGNOSE";
- public static final String USAGE_SCOPE_DISABLED = "DISABLED";
- public static final String AUTH_TYPE_NONE = "NONE";
- public static final String AUTH_TYPE_BEARER = "BEARER";
- public static final String AUTH_TYPE_API_KEY = "API_KEY";
- public static final String PROTOCOL_VERSION = "2025-03-26";
- public static final String SERVER_NAME = "wms-rsf-mcp";
- public static final String SERVER_VERSION = "1.0.0";
-
- private AiMcpConstants() {
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiSceneCode.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiSceneCode.java
deleted file mode 100644
index e3e4f21..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/constant/AiSceneCode.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.vincent.rsf.server.ai.constant;
-
-public final class AiSceneCode {
-
- public static final String GENERAL_CHAT = "general_chat";
-
- public static final String SYSTEM_DIAGNOSE = "system_diagnose";
-
- private AiSceneCode() {
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java
deleted file mode 100644
index 1b1506a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package com.vincent.rsf.server.ai.controller;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.dto.AiChatStreamRequest;
-import com.vincent.rsf.server.ai.dto.AiSessionCreateRequest;
-import com.vincent.rsf.server.ai.dto.AiSessionRenameRequest;
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import com.vincent.rsf.server.ai.model.AiChatSession;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.diagnosis.AiChatStreamOrchestrator;
-import com.vincent.rsf.server.ai.service.diagnosis.AiDiagnosisRuntimeService;
-import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
-import com.vincent.rsf.server.ai.service.AiRuntimeConfigService;
-import com.vincent.rsf.server.ai.service.AiSessionService;
-import com.vincent.rsf.server.system.controller.BaseController;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-
-import jakarta.annotation.Resource;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-@RequestMapping("/ai")
-public class AiController extends BaseController {
-
- @Resource
- private AiSessionService aiSessionService;
- @Resource
- private AiRuntimeConfigService aiRuntimeConfigService;
- @Resource
- private AiModelRouteRuntimeService aiModelRouteRuntimeService;
- @Resource
- private AiDiagnosisRuntimeService aiDiagnosisRuntimeService;
- @Resource
- private AiChatStreamOrchestrator aiChatStreamOrchestrator;
-
- @GetMapping("/model/list")
- public R modelList() {
- List<Map<String, Object>> models = new ArrayList<>();
- for (AiRuntimeConfigService.ModelRuntimeConfig model : aiRuntimeConfigService.listEnabledModels()) {
- Map<String, Object> item = new LinkedHashMap<>();
- item.put("code", model.getCode());
- item.put("name", model.getName());
- item.put("provider", model.getProvider());
- item.put("enabled", model.getEnabled());
- models.add(item);
- }
- return R.ok().add(models);
- }
-
- @GetMapping("/session/list")
- public R sessionList() {
- return R.ok().add(aiSessionService.listSessions(getTenantId(), getLoginUserId()));
- }
-
- @PostMapping("/session/create")
- public R createSession(@RequestBody(required = false) AiSessionCreateRequest request) {
- AiChatSession session = aiSessionService.createSession(
- getTenantId(),
- getLoginUserId(),
- request == null ? null : request.getTitle(),
- request == null ? null : request.getModelCode()
- );
- return R.ok().add(session);
- }
-
- @PostMapping("/session/{sessionId}/rename")
- public R renameSession(@PathVariable("sessionId") String sessionId, @RequestBody AiSessionRenameRequest request) {
- AiChatSession session = aiSessionService.renameSession(getTenantId(), getLoginUserId(), sessionId, request.getTitle());
- return R.ok().add(session);
- }
-
- @PostMapping("/session/remove/{sessionId}")
- public R removeSession(@PathVariable("sessionId") String sessionId) {
- aiSessionService.removeSession(getTenantId(), getLoginUserId(), sessionId);
- return R.ok();
- }
-
- @GetMapping("/session/{sessionId}/messages")
- public R messageList(@PathVariable("sessionId") String sessionId) {
- return R.ok().add(aiSessionService.listMessages(getTenantId(), getLoginUserId(), sessionId));
- }
-
- @PostMapping("/chat/stop")
- public R stop(@RequestBody AiChatStreamRequest request) {
- if (request != null && request.getSessionId() != null) {
- aiSessionService.requestStop(request.getSessionId());
- }
- return R.ok();
- }
-
- @PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public SseEmitter chatStream(@RequestBody AiChatStreamRequest request) {
- return doChatStream(normalizeRequest(request));
- }
-
- @PostMapping(value = "/diagnose/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public SseEmitter diagnoseStream(@RequestBody(required = false) AiChatStreamRequest request) {
- AiChatStreamRequest diagnosisRequest = normalizeRequest(request);
- diagnosisRequest.setSceneCode(AiSceneCode.SYSTEM_DIAGNOSE);
- if (diagnosisRequest.getMessage() == null || diagnosisRequest.getMessage().trim().isEmpty()) {
- diagnosisRequest.setMessage("璇峰褰撳墠WMS绯荤粺杩涜涓�娆″贰妫�璇婃柇锛岀粨鍚堝簱瀛樸�佷换鍔°�佽澶囩珯鐐规暟鎹瘑鍒紓甯稿苟缁欏嚭澶勭悊寤鸿銆�");
- }
- return doChatStream(diagnosisRequest);
- }
-
- private SseEmitter doChatStream(AiChatStreamRequest request) {
- SseEmitter emitter = new SseEmitter(0L);
- Long tenantId = getTenantId();
- Long userId = getLoginUserId();
- if (tenantId == null || userId == null) {
- completeWithError(emitter, "璇峰厛鐧诲綍鍚庡啀浣跨敤AI鍔╂墜");
- return emitter;
- }
- if (request == null || request.getMessage() == null || request.getMessage().trim().isEmpty()) {
- completeWithError(emitter, "娑堟伅鍐呭涓嶈兘涓虹┖");
- return emitter;
- }
-
- AiChatSession session = aiSessionService.ensureSession(tenantId, userId, request.getSessionId(), request.getModelCode());
- aiSessionService.clearStopFlag(session.getId());
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "user", request.getMessage(), session.getModelCode());
-
- AiPromptContext promptContext = new AiPromptContext()
- .setTenantId(tenantId)
- .setUserId(userId)
- .setSessionId(session.getId())
- .setModelCode(session.getModelCode())
- .setQuestion(request.getMessage())
- .setSceneCode(request.getSceneCode());
-
- List<AiModelRouteRuntimeService.RouteCandidate> candidates = aiModelRouteRuntimeService.resolveCandidates(
- tenantId,
- request.getSceneCode(),
- session.getModelCode()
- );
- if (candidates.isEmpty()) {
- completeWithError(emitter, "鏈壘鍒板彲鐢ㄧ殑AI妯″瀷閰嶇疆");
- return emitter;
- }
- int maxContextMessages = resolveContextSize(candidates);
- List<AiChatMessage> contextMessages = aiSessionService.listContextMessages(
- tenantId,
- userId,
- session.getId(),
- maxContextMessages
- );
- AiDiagnosisRecord diagnosisRecord = AiSceneCode.SYSTEM_DIAGNOSE.equals(request.getSceneCode())
- ? aiDiagnosisRuntimeService.startDiagnosis(tenantId, userId, session.getId(), request.getSceneCode(), request.getMessage())
- : null;
-
- Thread thread = new Thread(() -> executeStream(
- emitter, tenantId, userId, session, request, promptContext, contextMessages, diagnosisRecord, candidates
- ), "ai-chat-stream-" + session.getId());
- thread.setDaemon(true);
- thread.start();
- return emitter;
- }
-
- private void executeStream(SseEmitter emitter,
- Long tenantId,
- Long userId,
- AiChatSession session,
- AiChatStreamRequest request,
- AiPromptContext promptContext,
- List<AiChatMessage> contextMessages,
- AiDiagnosisRecord diagnosisRecord,
- List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
- aiChatStreamOrchestrator.executeStream(
- emitter,
- tenantId,
- userId,
- session,
- request,
- promptContext,
- contextMessages,
- diagnosisRecord,
- candidates
- );
- }
-
- private int resolveContextSize(List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
- return aiChatStreamOrchestrator.resolveContextSize(candidates);
- }
-
- private AiChatStreamRequest normalizeRequest(AiChatStreamRequest request) {
- AiChatStreamRequest normalized = request == null ? new AiChatStreamRequest() : request;
- if (normalized.getSceneCode() == null || normalized.getSceneCode().trim().isEmpty()) {
- normalized.setSceneCode(AiSceneCode.GENERAL_CHAT);
- }
- return normalized;
- }
-
- private Map<String, Object> buildErrorPayload(String message) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("message", message == null ? "AI鏈嶅姟寮傚父" : message);
- return payload;
- }
-
- private void completeWithError(SseEmitter emitter, String message) {
- try {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(message), MediaType.APPLICATION_JSON));
- } catch (IOException ignore) {
- }
- emitter.complete();
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiChatStreamRequest.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiChatStreamRequest.java
deleted file mode 100644
index 5a6c93c..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiChatStreamRequest.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.vincent.rsf.server.ai.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class AiChatStreamRequest implements Serializable {
-
- private String sessionId;
-
- private String message;
-
- private String modelCode;
-
- private String sceneCode;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionCreateRequest.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionCreateRequest.java
deleted file mode 100644
index 83f4d1c..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionCreateRequest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.vincent.rsf.server.ai.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class AiSessionCreateRequest implements Serializable {
-
- private String title;
-
- private String modelCode;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionRenameRequest.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionRenameRequest.java
deleted file mode 100644
index caba8e8..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/AiSessionRenameRequest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.ai.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class AiSessionRenameRequest implements Serializable {
-
- private String title;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatMessage.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatMessage.java
deleted file mode 100644
index fb4144f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatMessage.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.vincent.rsf.server.ai.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class GatewayChatMessage implements Serializable {
-
- private String role;
-
- private String content;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatRequest.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatRequest.java
deleted file mode 100644
index 8d0cbbd..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/dto/GatewayChatRequest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.vincent.rsf.server.ai.dto;
-
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-@Data
-public class GatewayChatRequest implements Serializable {
-
- private String sessionId;
-
- private String modelCode;
-
- private String routeCode;
-
- private Integer attemptNo;
-
- private String systemPrompt;
-
- private String chatUrl;
-
- private String apiKey;
-
- private String modelName;
-
- private List<GatewayChatMessage> messages = new ArrayList<>();
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatMessageMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatMessageMapper.java
deleted file mode 100644
index 8142a25..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatMessageMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.ai.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiChatMessageMapper extends BaseMapper<AiChatMessage> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatSessionMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatSessionMapper.java
deleted file mode 100644
index 635fb5a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/mapper/AiChatSessionMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.ai.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.ai.model.AiChatSession;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiChatSessionMapper extends BaseMapper<AiChatSession> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatMessage.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatMessage.java
deleted file mode 100644
index ea1c384..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatMessage.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.vincent.rsf.server.ai.model;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_chat_message")
-public class AiChatMessage implements Serializable {
-
- @TableId(value = "id", type = IdType.INPUT)
- private String id;
-
- private Long tenantId;
-
- private Long userId;
-
- private String sessionId;
-
- private String role;
-
- private String content;
-
- private String modelCode;
-
- private Date createTime;
-
- private Integer status;
-
- @TableLogic
- private Integer deleted;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatSession.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatSession.java
deleted file mode 100644
index 097d95e..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiChatSession.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.vincent.rsf.server.ai.model;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_chat_session")
-public class AiChatSession implements Serializable {
-
- @TableId(value = "id", type = IdType.INPUT)
- private String id;
-
- private Long tenantId;
-
- private Long userId;
-
- private String title;
-
- private String modelCode;
-
- private String lastMessage;
-
- private Date lastMessageAt;
-
- private Date createTime;
-
- private Date updateTime;
-
- private Integer status;
-
- @TableLogic
- private Integer deleted;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiDiagnosticToolResult.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiDiagnosticToolResult.java
deleted file mode 100644
index 91d0e0f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiDiagnosticToolResult.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.vincent.rsf.server.ai.model;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Map;
-
-@Data
-@Accessors(chain = true)
-public class AiDiagnosticToolResult implements Serializable {
-
- private String toolCode;
-
- private String mountCode;
-
- private String mcpToolName;
-
- private String toolName;
-
- private String severity;
-
- private String summaryText;
-
- private Map<String, Object> rawMeta;
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiMcpToolDescriptor.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiMcpToolDescriptor.java
deleted file mode 100644
index ccf8b30..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiMcpToolDescriptor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.vincent.rsf.server.ai.model;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Map;
-
-@Data
-@Accessors(chain = true)
-public class AiMcpToolDescriptor implements Serializable {
-
- private String mountCode;
-
- private String mountName;
-
- private String toolCode;
-
- private String mcpToolName;
-
- private String toolName;
-
- private String sceneCode;
-
- private String description;
-
- private Integer enabledFlag;
-
- private Integer priority;
-
- private String toolPrompt;
-
- private String usageScope;
-
- private String transportType;
-
- private Map<String, Object> inputSchema;
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiPromptContext.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiPromptContext.java
deleted file mode 100644
index ffbc0e7..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/model/AiPromptContext.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.vincent.rsf.server.ai.model;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-@Data
-@Accessors(chain = true)
-public class AiPromptContext implements Serializable {
-
- private Long tenantId;
-
- private Long userId;
-
- private String sessionId;
-
- private String modelCode;
-
- private String question;
-
- private String sceneCode;
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiGatewayClient.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiGatewayClient.java
deleted file mode 100644
index 6899022..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiGatewayClient.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.config.AiProperties;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-
-@Component
-public class AiGatewayClient {
-
- @Resource
- private AiProperties aiProperties;
- @Resource
- private ObjectMapper objectMapper;
-
- public interface StreamCallback {
- /**
- * 澶勭悊缃戝叧杩斿洖鐨勪竴鏉� NDJSON 浜嬩欢銆�
- * 杩斿洖 true 琛ㄧず缁х画娑堣垂鍚庣画浜嬩欢锛岃繑鍥� false 琛ㄧず涓诲姩鍋滄鏈娴佽鍙栥��
- */
- boolean handle(JsonNode event) throws Exception;
- }
-
- /**
- * 璋冪敤 AI 缃戝叧鐨勫唴閮ㄦ祦寮忔帴鍙o紝骞跺皢缃戝叧杩斿洖鐨勪簨浠堕�愭潯鍥炶皟缁欎笂灞傜紪鎺掗�昏緫銆�
- * 杩欓噷灞忚斀浜� HTTP 缁嗚妭锛岃皟鐢ㄦ柟鍙渶瑕佸叧娉� delta / done / error 浜嬩欢鏈韩銆�
- */
- public void stream(GatewayChatRequest request, StreamCallback callback) throws Exception {
- HttpURLConnection connection = null;
- boolean terminalEventReceived = false;
- try {
- String url = aiProperties.getGatewayBaseUrl() + "/internal/chat/stream";
- connection = (HttpURLConnection) new URL(url).openConnection();
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.setConnectTimeout(10000);
- connection.setReadTimeout(0);
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("Accept", "application/x-ndjson");
- try (OutputStream outputStream = connection.getOutputStream()) {
- outputStream.write(objectMapper.writeValueAsBytes(request));
- outputStream.flush();
- }
- InputStream inputStream = connection.getResponseCode() >= 400 ? connection.getErrorStream() : connection.getInputStream();
- if (inputStream == null) {
- return;
- }
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- String line;
- while ((line = reader.readLine()) != null) {
- if (line.trim().isEmpty()) {
- continue;
- }
- JsonNode event = objectMapper.readTree(line);
- String type = event.path("type").asText();
- if ("done".equals(type) || "error".equals(type)) {
- terminalEventReceived = true;
- }
- if (!callback.handle(event)) {
- break;
- }
- }
- }
- if (!terminalEventReceived) {
- throw new IOException("AI缃戝叧娴佸紓甯镐腑鏂�");
- }
- } finally {
- if (connection != null) {
- connection.disconnect();
- }
- }
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiModelRouteRuntimeService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiModelRouteRuntimeService.java
deleted file mode 100644
index 7317d4f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiModelRouteRuntimeService.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.system.entity.AiModelRoute;
-import com.vincent.rsf.server.system.service.AiModelRouteService;
-import lombok.Data;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-@Service
-public class AiModelRouteRuntimeService {
-
- @Resource
- private AiRuntimeConfigService aiRuntimeConfigService;
- @Resource
- private AiModelRouteService aiModelRouteService;
- @Resource
- private com.vincent.rsf.server.ai.config.AiProperties aiProperties;
-
- /**
- * 涓轰竴娆¤亰澶�/璇婃柇璇锋眰瑙f瀽鍊欓�夋ā鍨嬪垪琛ㄣ��
- * 椤哄簭鏄細浼氳瘽棣栭�夋ā鍨� -> 褰撳墠鍦烘櫙鍙敤璺敱 -> 榛樿妯″瀷銆�
- */
- public List<RouteCandidate> resolveCandidates(Long tenantId, String sceneCode, String preferredModelCode) {
- List<RouteCandidate> output = new ArrayList<>();
- Set<String> seen = new LinkedHashSet<>();
- if (preferredModelCode != null && !preferredModelCode.trim().isEmpty()) {
- RouteCandidate preferredCandidate = toCandidate(null, preferredModelCode, seen);
- if (preferredCandidate != null) {
- output.add(preferredCandidate);
- }
- }
- String routeCode = resolveRouteCode(sceneCode);
- for (AiModelRoute route : aiModelRouteService.listAvailableRoutes(tenantId, routeCode)) {
- RouteCandidate candidate = toCandidate(route, route.getModelCode(), seen);
- if (candidate != null) {
- output.add(candidate);
- }
- }
- if (output.isEmpty()) {
- RouteCandidate defaultCandidate = toCandidate(null, aiRuntimeConfigService.resolveDefaultModelCode(), seen);
- if (defaultCandidate != null) {
- output.add(defaultCandidate);
- }
- }
- return output;
- }
-
- /**
- * 鏍囪鏌愭潯妯″瀷璺敱鏈璋冪敤鎴愬姛锛屽苟娓呯┖澶辫触璁℃暟鍜屽喎鍗寸姸鎬併��
- */
- public void markSuccess(Long routeId) {
- if (routeId == null) {
- return;
- }
- aiModelRouteService.update(new LambdaUpdateWrapper<AiModelRoute>()
- .setSql("success_count = IFNULL(success_count, 0) + 1")
- .set(AiModelRoute::getFailCount, 0)
- .set(AiModelRoute::getCooldownUntil, null)
- .eq(AiModelRoute::getId, routeId));
- }
-
- /**
- * 鏍囪鏌愭潯妯″瀷璺敱鏈璋冪敤澶辫触銆�
- * 杩炵画澶辫触杈惧埌闃堝�煎悗浼氳繘鍏ュ喎鍗存湡锛岄伩鍏嶇煭鏃堕棿鍐呮寔缁懡涓晠闅滄ā鍨嬨��
- */
- public void markFailure(Long routeId) {
- if (routeId == null) {
- return;
- }
- AiModelRoute route = aiModelRouteService.getById(routeId);
- if (route == null) {
- return;
- }
- int nextFailCount = (route.getFailCount() == null ? 0 : route.getFailCount()) + 1;
- Date cooldownUntil = route.getCooldownUntil();
- if (nextFailCount >= aiProperties.getRouteFailThreshold()) {
- cooldownUntil = new Date(System.currentTimeMillis() + aiProperties.getRouteCooldownMinutes() * 60_000L);
- }
- route.setFailCount(nextFailCount);
- route.setCooldownUntil(cooldownUntil);
- aiModelRouteService.updateById(route);
- }
-
- /**
- * 鎵嬪姩閲嶇疆涓�鏉¤矾鐢辩殑鎴愬姛/澶辫触缁熻鍜屽喎鍗存椂闂淬��
- */
- public void resetRoute(Long routeId) {
- if (routeId == null) {
- return;
- }
- aiModelRouteService.update(new LambdaUpdateWrapper<AiModelRoute>()
- .set(AiModelRoute::getFailCount, 0)
- .set(AiModelRoute::getSuccessCount, 0)
- .set(AiModelRoute::getCooldownUntil, null)
- .eq(AiModelRoute::getId, routeId));
- }
-
- /**
- * 灏嗚亰澶╁満鏅粺涓�鏄犲皠鎴愯矾鐢辩粍缂栫爜銆�
- */
- private String resolveRouteCode(String sceneCode) {
- if (AiSceneCode.SYSTEM_DIAGNOSE.equals(sceneCode)) {
- return AiSceneCode.SYSTEM_DIAGNOSE;
- }
- return AiSceneCode.GENERAL_CHAT;
- }
-
- /**
- * 鍩轰簬妯″瀷缂栫爜鍜岃矾鐢辫褰曠粍瑁呰繍琛屾椂鍊欓�夐」锛屽苟璐熻矗鍘婚噸涓庡彲鐢ㄦ�ф牎楠屻��
- */
- private RouteCandidate toCandidate(AiModelRoute route, String modelCode, Set<String> seen) {
- if (modelCode == null || modelCode.trim().isEmpty() || seen.contains(modelCode)) {
- return null;
- }
- AiRuntimeConfigService.ModelRuntimeConfig runtimeConfig = aiRuntimeConfigService.resolveModel(modelCode);
- if (runtimeConfig == null || !Boolean.TRUE.equals(runtimeConfig.getEnabled())) {
- return null;
- }
- seen.add(modelCode);
- RouteCandidate candidate = new RouteCandidate();
- candidate.setRouteId(route == null ? null : route.getId());
- candidate.setRouteCode(route == null ? null : route.getRouteCode());
- candidate.setAttemptModelCode(runtimeConfig.getCode());
- candidate.setRuntimeConfig(runtimeConfig);
- return candidate;
- }
-
- @Data
- public static class RouteCandidate {
- private Long routeId;
- private String routeCode;
- private String attemptModelCode;
- private AiRuntimeConfigService.ModelRuntimeConfig runtimeConfig;
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextProvider.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextProvider.java
deleted file mode 100644
index 3e3ddee..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextProvider.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-
-public interface AiPromptContextProvider {
-
- /**
- * 鍒ゆ柇褰撳墠涓婁笅鏂囨槸鍚﹂渶瑕佺敱璇ユ彁渚涘櫒琛ュ厖绯荤粺鎻愮ず璇嶃��
- */
- boolean supports(AiPromptContext context);
-
- /**
- * 鏍规嵁褰撳墠璇锋眰涓婁笅鏂囩敓鎴愪竴娈靛彲闄勫姞鍒扮郴缁� Prompt 鐨勪笟鍔¤儗鏅弿杩般��
- */
- String buildContext(AiPromptContext context);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextService.java
deleted file mode 100644
index 00a81f0..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptContextService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Service
-public class AiPromptContextService {
-
- private final List<AiPromptContextProvider> providers;
-
- public AiPromptContextService(List<AiPromptContextProvider> providers) {
- this.providers = providers == null ? new ArrayList<>() : providers;
- }
-
- /**
- * 灏嗗熀纭� Prompt 涓庢墍鏈夊懡涓殑涓婁笅鏂囨彁渚涘櫒缁撴灉鎷艰鎴愭渶缁堢郴缁熸彁绀鸿瘝銆�
- * 鏅�氳亰澶╁満鏅富瑕佷緷璧栬繖鏉¢摼琛ュ厖涓氬姟鑳屾櫙锛岃瘖鏂満鏅垯鍦ㄦ鍩虹涓婂彔鍔犲伐鍏锋憳瑕併��
- */
- public String buildSystemPrompt(String basePrompt, AiPromptContext context) {
- List<String> promptParts = new ArrayList<>();
- if (basePrompt != null && !basePrompt.trim().isEmpty()) {
- promptParts.add(basePrompt.trim());
- }
- for (AiPromptContextProvider provider : providers) {
- if (!provider.supports(context)) {
- continue;
- }
- String providerPrompt = provider.buildContext(context);
- if (providerPrompt != null && !providerPrompt.trim().isEmpty()) {
- promptParts.add(providerPrompt.trim());
- }
- }
- if (promptParts.isEmpty()) {
- return null;
- }
- return String.join("\n\n", promptParts);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptRuntimeService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptRuntimeService.java
deleted file mode 100644
index 3880aeb..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiPromptRuntimeService.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.vincent.rsf.server.ai.config.AiProperties;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.diagnosis.AiDiagnosticToolService;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-import com.vincent.rsf.server.system.service.AiPromptTemplateService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.List;
-
-@Service
-public class AiPromptRuntimeService {
-
- @Resource
- private AiProperties aiProperties;
- @Resource
- private AiPromptContextService aiPromptContextService;
- @Resource
- private AiPromptTemplateService aiPromptTemplateService;
- @Resource
- private AiDiagnosticToolService aiDiagnosticToolService;
-
- /**
- * 鏋勯�犳寚瀹氬満鏅殑绯荤粺 Prompt銆�
- * 褰撴湭鏄惧紡浼犲叆璇婃柇宸ュ叿缁撴灉鏃讹紝璇婃柇鍦烘櫙浼氬湪鍐呴儴涓诲姩鏀堕泦涓�娆″伐鍏锋憳瑕併��
- */
- public String buildSystemPrompt(String sceneCode, String fallbackBasePrompt, AiPromptContext context) {
- return buildSystemPrompt(sceneCode, fallbackBasePrompt, context, null);
- }
-
- /**
- * 鏋勯�犳渶缁堢郴缁� Prompt锛屾寜鈥滃凡鍙戝竷妯℃澘/榛樿妯℃澘 + 涓婁笅鏂� + 宸ュ叿鎽樿鈥濈殑椤哄簭鎷艰銆�
- */
- public String buildSystemPrompt(String sceneCode, String fallbackBasePrompt, AiPromptContext context,
- List<AiDiagnosticToolResult> diagnosticResults) {
- String basePrompt = resolveBasePrompt(sceneCode, fallbackBasePrompt, context.getTenantId());
- List<AiDiagnosticToolResult> results = diagnosticResults;
- if (results == null && AiSceneCode.SYSTEM_DIAGNOSE.equals(sceneCode)) {
- results = aiDiagnosticToolService.collect(context);
- }
- if (results != null && !results.isEmpty()) {
- List<String> parts = new ArrayList<>();
- String contextPrompt = aiPromptContextService.buildSystemPrompt(basePrompt, context);
- if (contextPrompt != null && !contextPrompt.trim().isEmpty()) {
- parts.add(contextPrompt.trim());
- }
- String diagnosticPrompt = aiDiagnosticToolService.buildPrompt(context.getTenantId(), sceneCode, results);
- if (!diagnosticPrompt.trim().isEmpty()) {
- parts.add(diagnosticPrompt);
- }
- return String.join("\n\n", parts);
- }
- return aiPromptContextService.buildSystemPrompt(basePrompt, context);
- }
-
- /**
- * 瑙f瀽褰撳墠鍦烘櫙鐨勫熀纭� Prompt銆�
- * 浼樺厛浣跨敤宸插彂甯冪殑 Prompt 妯℃澘锛涜嫢妯℃澘涓嶅瓨鍦ㄦ垨涓虹┖锛屽垯鍥為��鍒伴厤缃枃浠朵腑鐨勯粯璁ゆ彁绀鸿瘝銆�
- */
- private String resolveBasePrompt(String sceneCode, String fallbackBasePrompt, Long tenantId) {
- AiPromptTemplate publishedTemplate = aiPromptTemplateService.getPublishedTemplate(tenantId, sceneCode);
- if (publishedTemplate == null) {
- return aiProperties.buildScenePrompt(sceneCode, fallbackBasePrompt);
- }
- List<String> parts = new ArrayList<>();
- if (publishedTemplate.getBasePrompt() != null && !publishedTemplate.getBasePrompt().trim().isEmpty()) {
- parts.add(publishedTemplate.getBasePrompt().trim());
- }
- if (publishedTemplate.getToolPrompt() != null && !publishedTemplate.getToolPrompt().trim().isEmpty()) {
- parts.add(publishedTemplate.getToolPrompt().trim());
- }
- if (publishedTemplate.getOutputPrompt() != null && !publishedTemplate.getOutputPrompt().trim().isEmpty()) {
- parts.add(publishedTemplate.getOutputPrompt().trim());
- }
- if (parts.isEmpty()) {
- return aiProperties.buildScenePrompt(sceneCode, fallbackBasePrompt);
- }
- return String.join("\n\n", parts);
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiRuntimeConfigService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiRuntimeConfigService.java
deleted file mode 100644
index 9470cf6..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiRuntimeConfigService.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.vincent.rsf.server.ai.config.AiProperties;
-import com.vincent.rsf.server.system.entity.AiParam;
-import com.vincent.rsf.server.system.service.AiParamService;
-import lombok.Data;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.List;
-
-@Service
-public class AiRuntimeConfigService {
-
- @Resource
- private AiProperties aiProperties;
- @Resource
- private AiParamService aiParamService;
-
- /**
- * 鏋氫妇褰撳墠鍙敤鐨勬ā鍨嬭繍琛屾椂閰嶇疆銆�
- * 浼樺厛浠庢暟鎹簱璇诲彇绉熸埛鍙繍钀ョ殑妯″瀷鍙傛暟锛涙暟鎹簱涓嶅彲鐢ㄦ椂鍥為��鍒� application 閰嶇疆銆�
- */
- public List<ModelRuntimeConfig> listEnabledModels() {
- List<ModelRuntimeConfig> output = new ArrayList<>();
- try {
- List<AiParam> list = aiParamService.listEnabledModels();
- for (AiParam item : list) {
- output.add(toRuntimeConfig(item));
- }
- } catch (Exception ignore) {
- }
- if (!output.isEmpty()) {
- return output;
- }
- for (AiProperties.ModelConfig model : aiProperties.getEnabledModels()) {
- ModelRuntimeConfig config = new ModelRuntimeConfig();
- config.setCode(model.getCode());
- config.setName(model.getName());
- config.setProvider(model.getProvider());
- config.setSystemPrompt(aiProperties.getSystemPrompt());
- config.setMaxContextMessages(aiProperties.getMaxContextMessages());
- config.setEnabled(model.getEnabled());
- output.add(config);
- }
- return output;
- }
-
- /**
- * 瑙f瀽鎸囧畾妯″瀷缂栫爜瀵瑰簲鐨勮繍琛屾椂閰嶇疆銆�
- * 濡傛灉鏈寚瀹氭ā鍨嬬紪鐮侊紝鍒欒繑鍥炲綋鍓嶉粯璁ゆā鍨嬶紱濡傛灉鏁版嵁搴撴棤璁板綍锛屽垯鍥為��鍒伴潤鎬侀厤缃��
- */
- public ModelRuntimeConfig resolveModel(String modelCode) {
- try {
- AiParam aiParam;
- if (modelCode == null || modelCode.trim().isEmpty()) {
- aiParam = aiParamService.getDefaultModel();
- } else {
- aiParam = aiParamService.getEnabledModel(modelCode);
- }
- if (aiParam != null) {
- return toRuntimeConfig(aiParam);
- }
- } catch (Exception ignore) {
- }
- String targetCode = modelCode;
- if (targetCode == null || targetCode.trim().isEmpty()) {
- targetCode = aiProperties.resolveDefaultModelCode();
- }
- for (AiProperties.ModelConfig model : aiProperties.getEnabledModels()) {
- if (targetCode.equals(model.getCode())) {
- ModelRuntimeConfig config = new ModelRuntimeConfig();
- config.setCode(model.getCode());
- config.setName(model.getName());
- config.setProvider(model.getProvider());
- config.setSystemPrompt(aiProperties.getSystemPrompt());
- config.setMaxContextMessages(aiProperties.getMaxContextMessages());
- config.setEnabled(model.getEnabled());
- return config;
- }
- }
- ModelRuntimeConfig config = new ModelRuntimeConfig();
- config.setCode(aiProperties.resolveDefaultModelCode());
- config.setName(aiProperties.resolveDefaultModelCode());
- config.setProvider("mock");
- config.setSystemPrompt(aiProperties.getSystemPrompt());
- config.setMaxContextMessages(aiProperties.getMaxContextMessages());
- config.setEnabled(true);
- return config;
- }
-
- /**
- * 鑾峰彇绯荤粺褰撳墠榛樿妯″瀷缂栫爜銆�
- */
- public String resolveDefaultModelCode() {
- return resolveModel(null).getCode();
- }
-
- /**
- * 灏嗘暟鎹簱涓殑 AI 鍙傛暟瀹炰綋杞崲涓鸿繍琛屾椂缁熶竴浣跨敤鐨勬ā鍨嬮厤缃璞°��
- */
- private ModelRuntimeConfig toRuntimeConfig(AiParam aiParam) {
- ModelRuntimeConfig config = new ModelRuntimeConfig();
- config.setCode(aiParam.getModelCode());
- config.setName(aiParam.getName());
- config.setProvider(aiParam.getProvider());
- config.setChatUrl(aiParam.getChatUrl());
- config.setApiKey(aiParam.getApiKey());
- config.setModelName(aiParam.getModelName());
- config.setSystemPrompt(aiParam.getSystemPrompt() == null || aiParam.getSystemPrompt().trim().isEmpty()
- ? aiProperties.getSystemPrompt()
- : aiParam.getSystemPrompt());
- config.setMaxContextMessages(aiParam.getMaxContextMessages() == null || aiParam.getMaxContextMessages() <= 0
- ? aiProperties.getMaxContextMessages()
- : aiParam.getMaxContextMessages());
- config.setEnabled(Integer.valueOf(1).equals(aiParam.getStatus()));
- return config;
- }
-
- @Data
- public static class ModelRuntimeConfig {
- private String code;
- private String name;
- private String provider;
- private String chatUrl;
- private String apiKey;
- private String modelName;
- private String systemPrompt;
- private Integer maxContextMessages;
- private Boolean enabled;
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiSessionService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiSessionService.java
deleted file mode 100644
index 0eea538..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiSessionService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import com.vincent.rsf.server.ai.model.AiChatSession;
-
-import java.util.List;
-
-public interface AiSessionService {
-
- /**
- * 鏌ヨ褰撳墠鐢ㄦ埛鍙鐨� AI 浼氳瘽鍒楄〃锛屾寜鏈�杩戞洿鏂版椂闂村�掑簭杩斿洖銆�
- */
- List<AiChatSession> listSessions(Long tenantId, Long userId);
-
- /**
- * 鏄惧紡鍒涘缓涓�涓柊浼氳瘽锛屽苟鏍规嵁浼犲叆妯″瀷鎴栭粯璁ゆā鍨嬪垵濮嬪寲浼氳瘽鍏冩暟鎹��
- */
- AiChatSession createSession(Long tenantId, Long userId, String title, String modelCode);
-
- /**
- * 纭繚鎸囧畾浼氳瘽瀛樺湪锛涘鏋滀細璇濅笉瀛樺湪鍒欒嚜鍔ㄥ垱寤猴紝瀛樺湪鏃跺彲椤哄甫鏇存柊妯″瀷鍋忓ソ銆�
- */
- AiChatSession ensureSession(Long tenantId, Long userId, String sessionId, String modelCode);
-
- /**
- * 鎸夌鎴枫�佺敤鎴峰拰浼氳瘽 ID 绮剧‘璇诲彇浼氳瘽锛岄伩鍏嶈法绉熸埛/璺ㄧ敤鎴蜂覆浼氳瘽銆�
- */
- AiChatSession getSession(Long tenantId, Long userId, String sessionId);
-
- /**
- * 閲嶅懡鍚嶄細璇濇爣棰樸��
- */
- AiChatSession renameSession(Long tenantId, Long userId, String sessionId, String title);
-
- /**
- * 鍒犻櫎浼氳瘽鍙婂叾鑱婂ぉ娑堟伅銆�
- */
- void removeSession(Long tenantId, Long userId, String sessionId);
-
- /**
- * 鏌ヨ浼氳瘽涓嬬殑瀹屾暣娑堟伅鍒楄〃銆�
- */
- List<AiChatMessage> listMessages(Long tenantId, Long userId, String sessionId);
-
- /**
- * 鏌ヨ鏋勯�犱笂涓嬫枃鎵�闇�鐨勬渶杩戣嫢骞叉潯娑堟伅銆�
- */
- List<AiChatMessage> listContextMessages(Long tenantId, Long userId, String sessionId, int maxCount);
-
- /**
- * 杩藉姞涓�鏉¤亰澶╂秷鎭紝骞跺悓姝ュ埛鏂颁細璇濇渶鍚庢秷鎭�佹渶鍚庢椿璺冩椂闂村拰妯″瀷淇℃伅銆�
- */
- AiChatMessage appendMessage(Long tenantId, Long userId, String sessionId, String role, String content, String modelCode);
-
- /**
- * 娓呴櫎浼氳瘽鐨勨�滃仠姝㈢敓鎴愨�濇爣璁帮紝閫氬父鍦ㄤ竴娆℃祦寮忓璇濇敹灏炬椂璋冪敤銆�
- */
- void clearStopFlag(String sessionId);
-
- /**
- * 鏍囪浼氳瘽闇�瑕佸仠姝㈢敓鎴愶紝渚涙祦寮忕紪鎺掔嚎绋嬭疆璇㈡秷璐广��
- */
- void requestStop(String sessionId);
-
- /**
- * 鍒ゆ柇褰撳墠浼氳瘽鏄惁宸叉敹鍒板仠姝㈢敓鎴愯姹傘��
- */
- boolean isStopRequested(String sessionId);
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiTextCompletionService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiTextCompletionService.java
deleted file mode 100644
index 6699206..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/AiTextCompletionService.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.vincent.rsf.server.ai.service;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-
-@Service
-public class AiTextCompletionService {
-
- @Resource
- private AiGatewayClient aiGatewayClient;
-
- /**
- * 鐢ㄦ祦寮忕綉鍏虫帴鍙fā鎷熶竴娆♀�滃悓姝ヨˉ鍏ㄦ枃鏈�濊皟鐢ㄣ��
- * 閫傚悎宸ュ叿瑙勫垝銆佺粨鏋勫寲 JSON 閫夋嫨杩欑被鐭枃鏈换鍔★紝鍐呴儴浠嶇劧澶嶇敤缁熶竴鐨勬祦寮忕綉鍏宠兘鍔涖��
- */
- public String complete(GatewayChatRequest request) throws Exception {
- final StringBuilder output = new StringBuilder();
- aiGatewayClient.stream(request, event -> handleEvent(event, output));
- return output.toString().trim();
- }
-
- /**
- * 鍙敹闆� delta 鏂囨湰锛屽苟鎶� error / done 浜嬩欢杞崲鎴愬悓姝ヨ皟鐢ㄨ涔夈��
- */
- private boolean handleEvent(JsonNode event, StringBuilder output) {
- String type = event.path("type").asText("");
- if ("delta".equals(type)) {
- output.append(event.path("content").asText(""));
- return true;
- }
- if ("error".equals(type)) {
- throw new IllegalStateException(event.path("message").asText("妯″瀷璋冪敤澶辫触"));
- }
- return !"done".equals(type);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiChatStreamOrchestrator.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiChatStreamOrchestrator.java
deleted file mode 100644
index 0a12a39..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiChatStreamOrchestrator.java
+++ /dev/null
@@ -1,467 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.dto.AiChatStreamRequest;
-import com.vincent.rsf.server.ai.dto.GatewayChatMessage;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import com.vincent.rsf.server.ai.model.AiChatSession;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.AiGatewayClient;
-import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
-import com.vincent.rsf.server.ai.service.AiPromptRuntimeService;
-import com.vincent.rsf.server.ai.service.AiSessionService;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import org.springframework.http.MediaType;
-import org.springframework.stereotype.Service;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-
-import jakarta.annotation.Resource;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiChatStreamOrchestrator {
-
- @Resource
- private AiSessionService aiSessionService;
- @Resource
- private AiGatewayClient aiGatewayClient;
- @Resource
- private AiPromptRuntimeService aiPromptRuntimeService;
- @Resource
- private AiDiagnosticToolService aiDiagnosticToolService;
- @Resource
- private AiModelRouteRuntimeService aiModelRouteRuntimeService;
- @Resource
- private AiDiagnosisRuntimeService aiDiagnosisRuntimeService;
- @Resource
- private AiDiagnosisMcpRuntimeService aiDiagnosisMcpRuntimeService;
-
- /**
- * 浠庡�欓�夋ā鍨嬪垪琛ㄤ腑鎸戝嚭鏈�澶х殑涓婁笅鏂囩獥鍙o紝鐢ㄤ簬鎻愬墠鎴柇浼氳瘽鍘嗗彶銆�
- */
- public int resolveContextSize(List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
- int max = 12;
- for (AiModelRouteRuntimeService.RouteCandidate item : candidates) {
- if (item != null && item.getRuntimeConfig() != null
- && item.getRuntimeConfig().getMaxContextMessages() != null
- && item.getRuntimeConfig().getMaxContextMessages() > max) {
- max = item.getRuntimeConfig().getMaxContextMessages();
- }
- }
- return max;
- }
-
- /**
- * 鎵ц涓�娆″畬鏁寸殑娴佸紡鑱婂ぉ/璇婃柇缂栨帓銆�
- * 杩欓噷缁熶竴璐熻矗 MCP 缁撴灉鍑嗗銆佹ā鍨嬮噸璇曘�佷簨浠惰浆鍙戙�佽皟鐢ㄦ棩蹇楀拰璇婃柇鏀跺熬銆�
- */
- public void executeStream(SseEmitter emitter,
- Long tenantId,
- Long userId,
- AiChatSession session,
- AiChatStreamRequest request,
- AiPromptContext promptContext,
- List<AiChatMessage> contextMessages,
- AiDiagnosisRecord diagnosisRecord,
- List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
- StringBuilder assistantReply = new StringBuilder();
- String finalModelCode = session.getModelCode();
- String finalErrorMessage = null;
- boolean stopped = false;
- boolean success = false;
- boolean assistantSaved = false;
- boolean errorSent = false;
- List<AiDiagnosticToolResult> runtimeDiagnosticResults = new ArrayList<>();
- String toolSummary = "[]";
- try {
- emitter.send(SseEmitter.event().name("session").data(buildSessionPayload(session), MediaType.APPLICATION_JSON));
- if (aiDiagnosisMcpRuntimeService.shouldUseMcp(promptContext)) {
- runtimeDiagnosticResults = aiDiagnosisMcpRuntimeService.resolveToolResults(
- tenantId,
- promptContext,
- contextMessages,
- candidates.isEmpty() ? null : candidates.get(0)
- );
- toolSummary = aiDiagnosticToolService.serializeResults(runtimeDiagnosticResults);
- }
- int attemptNo = 1;
- for (AiModelRouteRuntimeService.RouteCandidate candidate : candidates) {
- AttemptState attemptState = new AttemptState();
- Date requestTime = new Date();
- try {
- GatewayChatRequest gatewayChatRequest = buildGatewayRequest(
- session,
- contextMessages,
- candidate,
- request,
- promptContext,
- runtimeDiagnosticResults,
- attemptNo
- );
- aiGatewayClient.stream(gatewayChatRequest, event -> handleGatewayEvent(
- emitter,
- event,
- session,
- assistantReply,
- attemptState
- ));
- } catch (Exception e) {
- attemptState.setSuccess(false);
- attemptState.setErrorMessage(e.getMessage());
- attemptState.setInterrupted(isInterruptedError(e));
- attemptState.setResponseTime(new Date());
- }
- if (attemptState.getResponseTime() == null) {
- attemptState.setResponseTime(new Date());
- }
- String actualModelCode = attemptState.getActualModelCode() == null
- ? candidate.getAttemptModelCode()
- : attemptState.getActualModelCode();
- finalModelCode = actualModelCode;
- aiDiagnosisRuntimeService.saveCallLog(
- tenantId,
- userId,
- session.getId(),
- diagnosisRecord == null ? null : diagnosisRecord.getId(),
- resolveRouteCode(candidate, request),
- actualModelCode,
- attemptNo,
- requestTime,
- attemptState.getResponseTime(),
- Boolean.TRUE.equals(attemptState.getSuccess()) ? 1 : 0,
- attemptState.getErrorMessage()
- );
- if (Boolean.TRUE.equals(attemptState.getSuccess())) {
- aiModelRouteRuntimeService.markSuccess(candidate.getRouteId());
- success = assistantReply.length() > 0;
- if (!success) {
- finalErrorMessage = "妯″瀷鏈繑鍥炴湁鏁堝唴瀹�";
- }
- break;
- }
- if (attemptState.isStopped() || aiSessionService.isStopRequested(session.getId())) {
- stopped = true;
- break;
- }
- if (!attemptState.isInterrupted()) {
- aiModelRouteRuntimeService.markFailure(candidate.getRouteId());
- }
- finalErrorMessage = attemptState.getErrorMessage();
- if (attemptState.isReceivedDelta() || attemptNo >= candidates.size()) {
- if (!attemptState.isInterrupted() && finalErrorMessage != null && !finalErrorMessage.trim().isEmpty()) {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(finalErrorMessage), MediaType.APPLICATION_JSON));
- errorSent = true;
- }
- break;
- }
- attemptNo++;
- }
-
- if (aiSessionService.isStopRequested(session.getId())) {
- stopped = true;
- }
-
- if (stopped) {
- if (assistantReply.length() > 0) {
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "assistant", assistantReply.toString(), finalModelCode);
- assistantSaved = true;
- }
- emitter.send(SseEmitter.event().name("done").data(buildDonePayload(session, finalModelCode, true), MediaType.APPLICATION_JSON));
- if (diagnosisRecord != null) {
- aiDiagnosisRuntimeService.finishDiagnosisFailure(diagnosisRecord, assistantReply.toString(), "鐢ㄦ埛宸插仠姝㈢敓鎴�", toolSummary);
- }
- return;
- }
-
- if (success) {
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "assistant", assistantReply.toString(), finalModelCode);
- assistantSaved = true;
- emitter.send(SseEmitter.event().name("done").data(buildDonePayload(session, finalModelCode, false), MediaType.APPLICATION_JSON));
- if (diagnosisRecord != null) {
- aiDiagnosisRuntimeService.finishDiagnosisSuccess(diagnosisRecord, assistantReply.toString(), finalModelCode, toolSummary);
- }
- return;
- }
-
- if (assistantReply.length() > 0 && !assistantSaved) {
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "assistant", assistantReply.toString(), finalModelCode);
- assistantSaved = true;
- }
- if (diagnosisRecord != null) {
- aiDiagnosisRuntimeService.finishDiagnosisFailure(diagnosisRecord, assistantReply.toString(), finalErrorMessage, toolSummary);
- }
- if (!errorSent && finalErrorMessage != null && !finalErrorMessage.trim().isEmpty()) {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(finalErrorMessage), MediaType.APPLICATION_JSON));
- }
- } catch (Exception e) {
- if (diagnosisRecord != null) {
- aiDiagnosisRuntimeService.finishDiagnosisFailure(diagnosisRecord, assistantReply.toString(), e.getMessage(), toolSummary);
- }
- if (!isInterruptedError(e)) {
- try {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(e.getMessage()), MediaType.APPLICATION_JSON));
- } catch (IOException ignore) {
- }
- } else {
- Thread.currentThread().interrupt();
- }
- } finally {
- emitter.complete();
- aiSessionService.clearStopFlag(session.getId());
- }
- }
-
- /**
- * 娑堣垂缃戝叧杩斿洖鐨勫崟鏉℃祦寮忎簨浠讹紝骞舵妸鐘舵�佸啓鍥炴湰娆″皾璇曚笂涓嬫枃銆�
- */
- private boolean handleGatewayEvent(SseEmitter emitter, JsonNode event, AiChatSession session,
- StringBuilder assistantReply, AttemptState attemptState) throws Exception {
- if (aiSessionService.isStopRequested(session.getId())) {
- attemptState.setStopped(true);
- attemptState.setResponseTime(new Date());
- return false;
- }
- String type = event.path("type").asText();
- String modelCode = event.path("modelCode").asText(session.getModelCode());
- if ("delta".equals(type)) {
- String content = event.path("content").asText("");
- assistantReply.append(content);
- attemptState.setReceivedDelta(true);
- attemptState.setActualModelCode(modelCode);
- emitter.send(SseEmitter.event().name("delta").data(buildDeltaPayload(session, modelCode, content), MediaType.APPLICATION_JSON));
- return true;
- }
- if ("error".equals(type)) {
- String message = event.path("message").asText("妯″瀷璋冪敤澶辫触");
- attemptState.setSuccess(false);
- attemptState.setErrorMessage(message);
- attemptState.setActualModelCode(modelCode);
- attemptState.setResponseTime(parseResponseTime(event));
- attemptState.setInterrupted(isInterruptedMessage(message));
- return false;
- }
- if ("done".equals(type)) {
- attemptState.setSuccess(true);
- attemptState.setActualModelCode(modelCode);
- attemptState.setResponseTime(parseResponseTime(event));
- return false;
- }
- if ("ping".equals(type)) {
- emitter.send(SseEmitter.event().name("ping").data(buildPingPayload(modelCode), MediaType.APPLICATION_JSON));
- return true;
- }
- return true;
- }
-
- /**
- * 灏嗗綋鍓嶅皾璇曢渶瑕佺殑涓婁笅鏂囥�佺郴缁� Prompt 鍜屾ā鍨嬩俊鎭粍瑁呮垚缃戝叧璇锋眰銆�
- */
- private GatewayChatRequest buildGatewayRequest(AiChatSession session,
- List<AiChatMessage> contextMessages,
- AiModelRouteRuntimeService.RouteCandidate candidate,
- AiChatStreamRequest chatRequest,
- AiPromptContext promptContext,
- List<AiDiagnosticToolResult> diagnosticResults,
- Integer attemptNo) {
- GatewayChatRequest request = new GatewayChatRequest();
- request.setSessionId(session.getId());
- request.setModelCode(candidate.getAttemptModelCode());
- request.setRouteCode(resolveRouteCode(candidate, chatRequest));
- request.setAttemptNo(attemptNo);
- request.setSystemPrompt(aiPromptRuntimeService.buildSystemPrompt(
- chatRequest.getSceneCode(),
- candidate.getRuntimeConfig().getSystemPrompt(),
- promptContext,
- diagnosticResults
- ));
- request.setChatUrl(candidate.getRuntimeConfig().getChatUrl());
- request.setApiKey(candidate.getRuntimeConfig().getApiKey());
- request.setModelName(candidate.getRuntimeConfig().getModelName());
- for (AiChatMessage contextMessage : contextMessages) {
- GatewayChatMessage item = new GatewayChatMessage();
- item.setRole(contextMessage.getRole());
- item.setContent(contextMessage.getContent());
- request.getMessages().add(item);
- }
- return request;
- }
-
- /**
- * 瑙f瀽褰撳墠灏濊瘯搴旇惤鍒板摢涓矾鐢辩紪鐮侊紝浼樺厛浣跨敤璺敱鍊欓�夎嚜甯︾殑 routeCode銆�
- */
- private String resolveRouteCode(AiModelRouteRuntimeService.RouteCandidate candidate, AiChatStreamRequest request) {
- if (candidate != null && candidate.getRouteCode() != null && !candidate.getRouteCode().trim().isEmpty()) {
- return candidate.getRouteCode();
- }
- return AiSceneCode.SYSTEM_DIAGNOSE.equals(request.getSceneCode())
- ? AiSceneCode.SYSTEM_DIAGNOSE
- : AiSceneCode.GENERAL_CHAT;
- }
-
- /**
- * 浠庣綉鍏充簨浠朵腑瑙f瀽鍝嶅簲鏃堕棿锛岀己鐪佹椂鍥為��涓哄綋鍓嶆椂闂淬��
- */
- private Date parseResponseTime(JsonNode event) {
- long millis = event.path("responseTime").asLong(0L);
- return millis <= 0L ? new Date() : new Date(millis);
- }
-
- /**
- * 鏋勯�犱細璇濆垵濮嬪寲浜嬩欢缁欏墠绔��
- */
- private Map<String, Object> buildSessionPayload(AiChatSession session) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("title", session.getTitle());
- payload.put("modelCode", session.getModelCode());
- return payload;
- }
-
- /**
- * 鏋勯�犲閲忚緭鍑轰簨浠剁粰鍓嶇銆�
- */
- private Map<String, Object> buildDeltaPayload(AiChatSession session, String modelCode, String content) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("modelCode", modelCode);
- payload.put("content", content);
- payload.put("timestamp", new Date().getTime());
- return payload;
- }
-
- /**
- * 鏋勯�犳祦寮忓畬鎴愪簨浠剁粰鍓嶇銆�
- */
- private Map<String, Object> buildDonePayload(AiChatSession session, String modelCode, boolean stopped) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("modelCode", modelCode);
- payload.put("stopped", stopped);
- return payload;
- }
-
- /**
- * 鏋勯�犻敊璇簨浠剁粰鍓嶇銆�
- */
- private Map<String, Object> buildErrorPayload(String message) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("message", message == null ? "AI鏈嶅姟寮傚父" : message);
- return payload;
- }
-
- /**
- * 鏋勯�犲績璺充簨浠剁粰鍓嶇锛岀敤浜庣淮鎸侀暱杩炴帴娲绘�с��
- */
- private Map<String, Object> buildPingPayload(String modelCode) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("modelCode", modelCode);
- payload.put("timestamp", new Date().getTime());
- return payload;
- }
-
- /**
- * 鍒ゆ柇寮傚父閾句腑鏄惁鍖呭惈绾跨▼涓柇绫婚敊璇��
- */
- private boolean isInterruptedError(Throwable throwable) {
- Throwable current = throwable;
- while (current != null) {
- if (current instanceof InterruptedException || current instanceof InterruptedIOException) {
- return true;
- }
- if (isInterruptedMessage(current.getMessage())) {
- return true;
- }
- current = current.getCause();
- }
- return false;
- }
-
- private boolean isInterruptedMessage(String message) {
- if (message == null || message.trim().isEmpty()) {
- return false;
- }
- String normalized = message.toLowerCase();
- return normalized.contains("interrupted")
- || normalized.contains("broken pipe")
- || normalized.contains("connection reset")
- || normalized.contains("forcibly closed");
- }
-
- private static class AttemptState {
- private Boolean success;
- private String actualModelCode;
- private String errorMessage;
- private boolean receivedDelta;
- private boolean interrupted;
- private boolean stopped;
- private Date responseTime;
-
- public Boolean getSuccess() {
- return success;
- }
-
- public void setSuccess(Boolean success) {
- this.success = success;
- }
-
- public String getActualModelCode() {
- return actualModelCode;
- }
-
- public void setActualModelCode(String actualModelCode) {
- this.actualModelCode = actualModelCode;
- }
-
- public String getErrorMessage() {
- return errorMessage;
- }
-
- public void setErrorMessage(String errorMessage) {
- this.errorMessage = errorMessage;
- }
-
- public boolean isReceivedDelta() {
- return receivedDelta;
- }
-
- public void setReceivedDelta(boolean receivedDelta) {
- this.receivedDelta = receivedDelta;
- }
-
- public boolean isInterrupted() {
- return interrupted;
- }
-
- public void setInterrupted(boolean interrupted) {
- this.interrupted = interrupted;
- }
-
- public boolean isStopped() {
- return stopped;
- }
-
- public void setStopped(boolean stopped) {
- this.stopped = stopped;
- }
-
- public Date getResponseTime() {
- return responseTime;
- }
-
- public void setResponseTime(Date responseTime) {
- this.responseTime = responseTime;
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisMcpRuntimeService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisMcpRuntimeService.java
deleted file mode 100644
index 8dc4cdd..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisMcpRuntimeService.java
+++ /dev/null
@@ -1,426 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.dto.GatewayChatMessage;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
-import com.vincent.rsf.server.ai.service.AiTextCompletionService;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpRegistryService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-@Service
-public class AiDiagnosisMcpRuntimeService {
-
- @Resource
- private AiMcpRegistryService aiMcpRegistryService;
- @Resource
- private AiDiagnosticToolService aiDiagnosticToolService;
- @Resource
- private AiTextCompletionService aiTextCompletionService;
- @Resource
- private ObjectMapper objectMapper;
-
- /**
- * 鏍规嵁褰撳墠闂銆佷笂涓嬫枃娑堟伅鍜屽彲鐢ㄦā鍨嬶紝瑙f瀽鍑烘湰杞湡姝h鎵ц鐨� MCP 宸ュ叿缁撴灉銆�
- * 鍏堝仛宸ュ叿杩囨护锛屽啀鍋氬惎鍙戝紡鎴栨ā鍨嬭鍒掗�夋嫨锛屾渶鍚庨『搴忔墽琛屽伐鍏枫��
- */
- public List<AiDiagnosticToolResult> resolveToolResults(Long tenantId,
- AiPromptContext context,
- List<AiChatMessage> contextMessages,
- AiModelRouteRuntimeService.RouteCandidate plannerCandidate) {
- List<AiMcpToolDescriptor> tools = filterTools(aiMcpRegistryService.listTools(tenantId, null), context.getSceneCode());
- if (tools.isEmpty()) {
- return fallbackResults(context);
- }
- List<AiMcpToolDescriptor> selectedTools = selectTools(context, contextMessages, plannerCandidate, tools);
- if (selectedTools.isEmpty()) {
- return fallbackResults(context);
- }
- List<AiDiagnosticToolResult> output = new ArrayList<>();
- for (AiMcpToolDescriptor tool : selectedTools) {
- try {
- AiDiagnosticToolResult result = aiMcpRegistryService.executeTool(tenantId, tool, context);
- if (result != null && result.getSummaryText() != null && !result.getSummaryText().trim().isEmpty()) {
- output.add(result);
- }
- } catch (Exception e) {
- output.add(new AiDiagnosticToolResult()
- .setToolCode(tool.getToolCode())
- .setMountCode(tool.getMountCode())
- .setMcpToolName(tool.getMcpToolName())
- .setToolName(tool.getToolName())
- .setSeverity("WARN")
- .setSummaryText("MCP宸ュ叿鎵ц澶辫触锛�" + e.getMessage()));
- }
- }
- if (output.isEmpty()) {
- return fallbackResults(context);
- }
- return output;
- }
-
- /**
- * 鍒ゆ柇褰撳墠璇锋眰鏄惁鍊煎緱鍚敤 MCP銆�
- * 璇婃柇鍦烘櫙榛樿鍚敤锛屾櫘閫氳亰澶╁垯鎸夊叧閿瘝鍜屼笟鍔¢棶棰樼壒寰佽Е鍙戙��
- */
- public boolean shouldUseMcp(AiPromptContext context) {
- if (context == null) {
- return false;
- }
- if (AiSceneCode.SYSTEM_DIAGNOSE.equals(context.getSceneCode())) {
- return true;
- }
- String question = context.getQuestion();
- if (question == null || question.trim().isEmpty()) {
- return false;
- }
- String normalized = question.toLowerCase();
- return normalized.contains("mcp")
- || normalized.contains("宸ュ叿")
- || normalized.contains("license")
- || question.contains("璁稿彲璇�")
- || normalized.contains("task")
- || normalized.contains("device")
- || normalized.contains("site")
- || normalized.contains("loc")
- || question.contains("搴撳瓨")
- || question.contains("搴撲綅")
- || question.contains("璐т綅")
- || question.contains("鐗╂枡")
- || question.contains("浠诲姟")
- || question.contains("璁惧")
- || question.contains("绔欑偣")
- || question.contains("宸烽亾");
- }
-
- /**
- * 璇婃柇鍦烘櫙鍦ㄦ病鏈夐�夊嚭鍏蜂綋宸ュ叿鏃讹紝閫�鍥炲埌鍐呯疆宸ュ叿鍏ㄩ噺鏀堕泦妯″紡锛�
- * 鏅�氳亰澶╁垯鐩存帴杩斿洖绌虹粨鏋滐紝閬垮厤鏃犲叧宸ュ叿姹℃煋鍥炵瓟銆�
- */
- private List<AiDiagnosticToolResult> fallbackResults(AiPromptContext context) {
- if (context != null && AiSceneCode.SYSTEM_DIAGNOSE.equals(context.getSceneCode())) {
- return aiDiagnosticToolService.collect(context);
- }
- return new ArrayList<>();
- }
-
- /**
- * 鎸夊満鏅拰鍚敤鐘舵�佽繃婊ゅ彲鐢ㄥ伐鍏风洰褰曘��
- */
- private List<AiMcpToolDescriptor> filterTools(List<AiMcpToolDescriptor> tools, String sceneCode) {
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- for (AiMcpToolDescriptor tool : tools) {
- if (tool == null || !Integer.valueOf(1).equals(tool.getEnabledFlag())) {
- continue;
- }
- if (sceneCode != null && tool.getSceneCode() != null && !tool.getSceneCode().trim().isEmpty()
- && !sceneCode.equals(tool.getSceneCode())) {
- continue;
- }
- output.add(tool);
- }
- return output;
- }
-
- /**
- * 鏅�氳亰澶╀紭鍏堜娇鐢ㄥ惎鍙戝紡宸ュ叿閫夋嫨锛岃瘖鏂満鏅紭鍏堝皾璇曟ā鍨嬭鍒掋��
- */
- private List<AiMcpToolDescriptor> selectTools(AiPromptContext context,
- List<AiChatMessage> contextMessages,
- AiModelRouteRuntimeService.RouteCandidate plannerCandidate,
- List<AiMcpToolDescriptor> tools) {
- List<AiMcpToolDescriptor> heuristic = heuristicSelect(tools, context == null ? null : context.getQuestion());
- if (context != null && !AiSceneCode.SYSTEM_DIAGNOSE.equals(context.getSceneCode()) && !heuristic.isEmpty()) {
- return heuristic;
- }
- List<AiMcpToolDescriptor> byModel = selectToolsByModel(context, contextMessages, plannerCandidate, tools);
- if (!byModel.isEmpty()) {
- return byModel;
- }
- return heuristic;
- }
-
- /**
- * 浣跨敤涓�涓交閲忚鍒掕姹傦紝璁╂ā鍨嬪湪鐜版湁宸ュ叿鐩綍涓�夊嚭鏈�闇�瑕佽皟鐢ㄧ殑宸ュ叿銆�
- */
- private List<AiMcpToolDescriptor> selectToolsByModel(AiPromptContext context,
- List<AiChatMessage> contextMessages,
- AiModelRouteRuntimeService.RouteCandidate plannerCandidate,
- List<AiMcpToolDescriptor> tools) {
- if (plannerCandidate == null || plannerCandidate.getRuntimeConfig() == null) {
- return new ArrayList<>();
- }
- try {
- GatewayChatRequest request = new GatewayChatRequest();
- request.setSessionId(context == null ? null : context.getSessionId());
- request.setModelCode(plannerCandidate.getAttemptModelCode());
- request.setRouteCode("system_diagnose_planner");
- request.setAttemptNo(0);
- request.setChatUrl(plannerCandidate.getRuntimeConfig().getChatUrl());
- request.setApiKey(plannerCandidate.getRuntimeConfig().getApiKey());
- request.setModelName(plannerCandidate.getRuntimeConfig().getModelName());
- request.setSystemPrompt(buildPlannerSystemPrompt());
- for (AiChatMessage item : contextMessages) {
- GatewayChatMessage message = new GatewayChatMessage();
- message.setRole(item.getRole());
- message.setContent(item.getContent());
- request.getMessages().add(message);
- }
- GatewayChatMessage plannerMessage = new GatewayChatMessage();
- plannerMessage.setRole("user");
- plannerMessage.setContent(buildPlannerUserPrompt(context, tools));
- request.getMessages().add(plannerMessage);
- String response = aiTextCompletionService.complete(request);
- return mapSelection(parseToolNames(response), tools);
- } catch (Exception ignore) {
- return new ArrayList<>();
- }
- }
-
- /**
- * 鐢熸垚涓撶敤浜庡伐鍏疯鍒掔殑 system prompt銆�
- */
- private String buildPlannerSystemPrompt() {
- return "浣犳槸WMS璇婃柇宸ュ叿璋冨害鍣ㄣ�備綘鍙兘浠庢彁渚涚殑 MCP 宸ュ叿鐩綍涓�夋嫨鏈�闇�瑕佽皟鐢ㄧ殑宸ュ叿銆�"
- + "鍙緭鍑� JSON锛屼笉瑕� markdown锛屼笉瑕佽В閲娿�傝緭鍑烘牸寮忓繀椤绘槸 "
- + "{\"tools\":[{\"name\":\"宸ュ叿鍚峔",\"reason\":\"绠�鐭師鍥燶"}]}"
- + "銆傚伐鍏峰悕蹇呴』鏉ヨ嚜鐩綍锛屾渶澶氶�夋嫨4涓紱濡傛灉鏃犻渶宸ュ叿锛岃繑鍥� {\"tools\":[]}銆�";
- }
-
- /**
- * 灏嗛棶棰樺拰宸ュ叿鐩綍鏁寸悊鎴愯鍒掓ā鍨嬪彲鐩存帴娑堣垂鐨勮緭鍏ャ��
- */
- private String buildPlannerUserPrompt(AiPromptContext context, List<AiMcpToolDescriptor> tools) {
- List<String> parts = new ArrayList<>();
- parts.add("闂:");
- parts.add(context == null || context.getQuestion() == null ? "" : context.getQuestion());
- parts.add("");
- parts.add("鍙敤宸ュ叿鐩綍:");
- for (AiMcpToolDescriptor tool : tools) {
- parts.add("- " + tool.getMcpToolName()
- + " | " + safe(tool.getToolName())
- + " | " + safe(tool.getDescription())
- + " | " + safe(tool.getToolPrompt()));
- }
- return String.join("\n", parts);
- }
-
- /**
- * 瑙f瀽瑙勫垝妯″瀷杩斿洖鐨� JSON锛屾彁鍙栬閫変腑鐨勫伐鍏峰悕鍒楄〃銆�
- */
- private List<String> parseToolNames(String response) {
- List<String> output = new ArrayList<>();
- if (response == null || response.trim().isEmpty()) {
- return output;
- }
- String normalized = unwrapJson(response);
- try {
- JsonNode root = objectMapper.readTree(normalized);
- JsonNode toolsNode = root.path("tools");
- if (!toolsNode.isArray()) {
- return output;
- }
- for (JsonNode item : toolsNode) {
- String name = item.path("name").asText("");
- if (!name.trim().isEmpty()) {
- output.add(name.trim());
- }
- }
- } catch (Exception ignore) {
- }
- return output;
- }
-
- /**
- * 灏嗘ā鍨嬭繑鍥炵殑宸ュ叿鍚嶆槧灏勫洖鏈湴宸ュ叿鎻忚堪绗︼紝骞舵寜杩斿洖椤哄簭鍘婚噸鎴柇銆�
- */
- private List<AiMcpToolDescriptor> mapSelection(List<String> names, List<AiMcpToolDescriptor> tools) {
- Map<String, AiMcpToolDescriptor> byName = new LinkedHashMap<>();
- for (AiMcpToolDescriptor tool : tools) {
- byName.put(tool.getMcpToolName(), tool);
- }
- Set<String> seen = new LinkedHashSet<>();
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- for (String name : names) {
- if (seen.contains(name)) {
- continue;
- }
- AiMcpToolDescriptor tool = byName.get(name);
- if (tool != null) {
- output.add(tool);
- seen.add(name);
- }
- if (output.size() >= 4) {
- break;
- }
- }
- return output;
- }
-
- /**
- * 鍩轰簬涓枃涓氬姟鍏抽敭璇嶄笌宸ュ叿鎻忚堪鎵撳垎锛岄�夊嚭鏈�鍙兘鍛戒腑鐨勫伐鍏枫��
- */
- private List<AiMcpToolDescriptor> heuristicSelect(List<AiMcpToolDescriptor> tools, String question) {
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- if (tools.isEmpty()) {
- return output;
- }
- String normalized = normalize(question);
- List<ScoredTool> scoredTools = new ArrayList<>();
- for (AiMcpToolDescriptor tool : tools) {
- int score = matchScore(tool, normalized);
- if (score > 0) {
- scoredTools.add(new ScoredTool(tool, score));
- }
- }
- scoredTools.sort(Comparator.comparingInt(ScoredTool::getScore).reversed());
- for (ScoredTool scoredTool : scoredTools) {
- output.add(scoredTool.getTool());
- if (output.size() >= 4) {
- return output;
- }
- }
- for (AiMcpToolDescriptor tool : tools) {
- if (!output.contains(tool)) {
- output.add(tool);
- }
- if (output.size() >= 3) {
- break;
- }
- }
- return output;
- }
-
- /**
- * 璁$畻鍗曚釜宸ュ叿涓庡綋鍓嶉棶棰樼殑鍛戒腑鍒嗘暟銆�
- */
- private int matchScore(AiMcpToolDescriptor tool, String question) {
- if (question == null || question.trim().isEmpty()) {
- return 0;
- }
- return countMatches(question, tool.getToolCode(), tool.getToolName(), tool.getDescription(), tool.getToolPrompt());
- }
-
- /**
- * 缁熻闂鏂囨湰涓庝竴缁勫�欓�夊瓧娈电殑鐗囨鍛戒腑娆℃暟銆�
- */
- private int countMatches(String question, String... values) {
- int score = 0;
- for (String value : values) {
- for (String piece : buildMatchFragments(value)) {
- if (piece.length() >= 2 && question.contains(piece)) {
- score++;
- }
- }
- }
- return score;
- }
-
- /**
- * 灏嗗伐鍏峰悕銆佹弿杩扮瓑鏂囨湰鎷嗘垚閫傚悎涓枃涓氬姟鏌ヨ鍖归厤鐨勭墖娈甸泦鍚堛��
- */
- private List<String> buildMatchFragments(String value) {
- List<String> fragments = new ArrayList<>();
- if (value == null || value.trim().isEmpty()) {
- return fragments;
- }
- String[] pieces = normalize(value).split("[^a-z0-9\\u4e00-\\u9fa5]+");
- for (String piece : pieces) {
- if (piece.length() < 2) {
- continue;
- }
- fragments.add(piece);
- if (piece.matches("[\\u4e00-\\u9fa5]+")) {
- if (piece.endsWith("鎽樿") || piece.endsWith("姒傚喌") || piece.endsWith("姒傝") || piece.endsWith("鐘舵��")) {
- fragments.add(piece.substring(0, piece.length() - 2));
- }
- for (int size = 2; size <= Math.min(4, piece.length()); size++) {
- for (int i = 0; i <= piece.length() - size; i++) {
- fragments.add(piece.substring(i, i + size));
- }
- }
- }
- }
- return fragments;
- }
-
- /**
- * 瀵规瘮瀵规枃鏈仛灏忓啓褰掍竴鍖栵紝渚夸簬缁熶竴鍖归厤銆�
- */
- private String normalize(String value) {
- return value == null ? "" : value.toLowerCase();
- }
-
- /**
- * 灏介噺浠庢ā鍨嬭緭鍑轰腑鍓ョ鍑哄彲瑙f瀽鐨� JSON 鐗囨銆�
- */
- private String unwrapJson(String response) {
- String normalized = response.trim();
- if (normalized.startsWith("```")) {
- int firstBrace = normalized.indexOf('{');
- int lastBrace = normalized.lastIndexOf('}');
- if (firstBrace >= 0 && lastBrace > firstBrace) {
- return normalized.substring(firstBrace, lastBrace + 1);
- }
- }
- int firstBrace = normalized.indexOf('{');
- int lastBrace = normalized.lastIndexOf('}');
- if (firstBrace >= 0 && lastBrace > firstBrace) {
- return normalized.substring(firstBrace, lastBrace + 1);
- }
- return normalized;
- }
-
- /**
- * 涓� prompt 缁勮闃舵鎻愪緵绌哄�煎畨鍏ㄥ瓧绗︿覆銆�
- */
- private String safe(String value) {
- return value == null ? "" : value;
- }
-
- private static class ScoredTool {
- private final AiMcpToolDescriptor tool;
- private final int score;
-
- /**
- * 淇濆瓨宸ュ叿鍙婂叾鍖归厤寰楀垎銆�
- */
- private ScoredTool(AiMcpToolDescriptor tool, int score) {
- this.tool = tool;
- this.score = score;
- }
-
- /**
- * 杩斿洖琚墦鍒嗙殑宸ュ叿銆�
- */
- public AiMcpToolDescriptor getTool() {
- return tool;
- }
-
- /**
- * 杩斿洖宸ュ叿鍖归厤寰楀垎銆�
- */
- public int getScore() {
- return score;
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanRunnerService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanRunnerService.java
deleted file mode 100644
index 21abcc7..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanRunnerService.java
+++ /dev/null
@@ -1,355 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.dto.GatewayChatMessage;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.AiGatewayClient;
-import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
-import com.vincent.rsf.server.ai.service.AiPromptRuntimeService;
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import com.vincent.rsf.server.system.service.AiDiagnosisPlanService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.io.InterruptedIOException;
-import java.util.Date;
-import java.util.List;
-
-@Service
-public class AiDiagnosisPlanRunnerService {
-
- @Resource
- private AiDiagnosisPlanService aiDiagnosisPlanService;
- @Resource
- private AiDiagnosticToolService aiDiagnosticToolService;
- @Resource
- private AiModelRouteRuntimeService aiModelRouteRuntimeService;
- @Resource
- private AiPromptRuntimeService aiPromptRuntimeService;
- @Resource
- private AiGatewayClient aiGatewayClient;
- @Resource
- private AiDiagnosisRuntimeService aiDiagnosisRuntimeService;
-
- /**
- * 鎵ц涓�娆″贰妫�璁″垝銆�
- * 璁″垝鎵ц鏈川涓婂鐢ㄨ瘖鏂富閾捐矾锛屽彧鏄緭鍏ユ潵婧愪粠浜哄伐鎻愰棶鍙樻垚浜嗚鍒掗厤缃��
- */
- public void runPlan(Long planId, boolean manualTrigger) {
- AiDiagnosisPlan plan = aiDiagnosisPlanService.getById(planId);
- if (plan == null) {
- return;
- }
- Date finishTime = new Date();
- Date nextRunTime = Integer.valueOf(1).equals(plan.getStatus())
- ? aiDiagnosisPlanService.calculateNextRunTime(plan.getCronExpr(), finishTime)
- : null;
- Long userId = plan.getUpdateBy() == null ? plan.getCreateBy() : plan.getUpdateBy();
- String sessionId = "plan-" + plan.getId() + "-" + System.currentTimeMillis();
- String question = plan.getPrompt();
- if (question == null || question.trim().isEmpty()) {
- question = "璇峰褰撳墠WMS绯荤粺杩涜涓�娆″贰妫�璇婃柇锛岀粨鍚堝簱瀛樸�佷换鍔°�佽澶囩珯鐐规暟鎹瘑鍒紓甯稿苟缁欏嚭澶勭悊寤鸿銆�";
- }
-
- AiPromptContext promptContext = new AiPromptContext()
- .setTenantId(plan.getTenantId())
- .setUserId(userId)
- .setSessionId(sessionId)
- .setModelCode(plan.getPreferredModelCode())
- .setQuestion(question)
- .setSceneCode(plan.getSceneCode() == null || plan.getSceneCode().trim().isEmpty()
- ? AiSceneCode.SYSTEM_DIAGNOSE
- : plan.getSceneCode());
-
- List<AiDiagnosticToolResult> diagnosticResults = aiDiagnosticToolService.collect(promptContext);
- String toolSummary = aiDiagnosticToolService.serializeResults(diagnosticResults);
- AiDiagnosisRecord diagnosisRecord = aiDiagnosisRuntimeService.startDiagnosis(
- plan.getTenantId(),
- userId,
- sessionId,
- promptContext.getSceneCode(),
- question
- );
-
- StringBuilder assistantReply = new StringBuilder();
- String finalModelCode = plan.getPreferredModelCode();
- String finalErrorMessage = null;
- boolean success = false;
-
- try {
- List<AiModelRouteRuntimeService.RouteCandidate> candidates = aiModelRouteRuntimeService.resolveCandidates(
- plan.getTenantId(),
- promptContext.getSceneCode(),
- plan.getPreferredModelCode()
- );
- if (candidates.isEmpty()) {
- finalErrorMessage = "鏈壘鍒板彲鐢ㄧ殑AI妯″瀷閰嶇疆";
- } else {
- int attemptNo = 1;
- for (AiModelRouteRuntimeService.RouteCandidate candidate : candidates) {
- AttemptState attemptState = new AttemptState();
- Date requestTime = new Date();
- try {
- GatewayChatRequest gatewayChatRequest = buildGatewayRequest(
- sessionId,
- question,
- candidate,
- promptContext,
- diagnosticResults,
- attemptNo
- );
- aiGatewayClient.stream(gatewayChatRequest, event -> handleGatewayEvent(event, assistantReply, attemptState));
- } catch (Exception e) {
- attemptState.setSuccess(false);
- attemptState.setErrorMessage(e.getMessage());
- attemptState.setInterrupted(isInterruptedError(e));
- attemptState.setResponseTime(new Date());
- }
- if (attemptState.getResponseTime() == null) {
- attemptState.setResponseTime(new Date());
- }
- String actualModelCode = attemptState.getActualModelCode() == null
- ? candidate.getAttemptModelCode()
- : attemptState.getActualModelCode();
- finalModelCode = actualModelCode;
- aiDiagnosisRuntimeService.saveCallLog(
- plan.getTenantId(),
- userId,
- sessionId,
- diagnosisRecord.getId(),
- candidate.getRouteCode(),
- actualModelCode,
- attemptNo,
- requestTime,
- attemptState.getResponseTime(),
- Boolean.TRUE.equals(attemptState.getSuccess()) ? 1 : 0,
- attemptState.getErrorMessage()
- );
- if (Boolean.TRUE.equals(attemptState.getSuccess())) {
- aiModelRouteRuntimeService.markSuccess(candidate.getRouteId());
- success = assistantReply.length() > 0;
- if (!success) {
- finalErrorMessage = "妯″瀷鏈繑鍥炴湁鏁堝唴瀹�";
- }
- break;
- }
- if (!attemptState.isInterrupted()) {
- aiModelRouteRuntimeService.markFailure(candidate.getRouteId());
- }
- finalErrorMessage = attemptState.getErrorMessage();
- if (attemptState.isReceivedDelta() || attemptNo >= candidates.size()) {
- break;
- }
- attemptNo++;
- }
- }
-
- if (success) {
- aiDiagnosisRuntimeService.finishDiagnosisSuccess(diagnosisRecord, assistantReply.toString(), finalModelCode, toolSummary);
- aiDiagnosisPlanService.finishExecution(
- plan.getId(),
- 1,
- diagnosisRecord.getId(),
- buildPlanMessage(assistantReply.toString(), manualTrigger ? "鎵嬪姩鎵ц鎴愬姛" : "璁″垝鎵ц鎴愬姛"),
- new Date(),
- nextRunTime
- );
- return;
- }
- aiDiagnosisRuntimeService.finishDiagnosisFailure(diagnosisRecord, assistantReply.toString(), finalErrorMessage, toolSummary);
- aiDiagnosisPlanService.finishExecution(
- plan.getId(),
- 0,
- diagnosisRecord.getId(),
- buildPlanMessage(finalErrorMessage, manualTrigger ? "鎵嬪姩鎵ц澶辫触" : "璁″垝鎵ц澶辫触"),
- new Date(),
- nextRunTime
- );
- } catch (Exception e) {
- aiDiagnosisRuntimeService.finishDiagnosisFailure(diagnosisRecord, assistantReply.toString(), e.getMessage(), toolSummary);
- aiDiagnosisPlanService.finishExecution(
- plan.getId(),
- 0,
- diagnosisRecord.getId(),
- buildPlanMessage(e.getMessage(), manualTrigger ? "鎵嬪姩鎵ц澶辫触" : "璁″垝鎵ц澶辫触"),
- new Date(),
- nextRunTime
- );
- }
- }
-
- /**
- * 涓哄贰妫�璁″垝缁勮缃戝叧璇锋眰銆�
- */
- private GatewayChatRequest buildGatewayRequest(String sessionId,
- String question,
- AiModelRouteRuntimeService.RouteCandidate candidate,
- AiPromptContext promptContext,
- List<AiDiagnosticToolResult> diagnosticResults,
- Integer attemptNo) {
- GatewayChatRequest request = new GatewayChatRequest();
- request.setSessionId(sessionId);
- request.setModelCode(candidate.getAttemptModelCode());
- request.setRouteCode(candidate.getRouteCode());
- request.setAttemptNo(attemptNo);
- request.setSystemPrompt(aiPromptRuntimeService.buildSystemPrompt(
- promptContext.getSceneCode(),
- candidate.getRuntimeConfig().getSystemPrompt(),
- promptContext,
- diagnosticResults
- ));
- request.setChatUrl(candidate.getRuntimeConfig().getChatUrl());
- request.setApiKey(candidate.getRuntimeConfig().getApiKey());
- request.setModelName(candidate.getRuntimeConfig().getModelName());
-
- GatewayChatMessage message = new GatewayChatMessage();
- message.setRole("user");
- message.setContent(question);
- request.getMessages().add(message);
- return request;
- }
-
- /**
- * 娑堣垂璁″垝鎵ц鏃剁殑娴佸紡缃戝叧浜嬩欢銆�
- */
- private boolean handleGatewayEvent(JsonNode event, StringBuilder assistantReply, AttemptState attemptState) {
- String type = event.path("type").asText();
- String modelCode = event.path("modelCode").asText();
- if ("delta".equals(type)) {
- String content = event.path("content").asText("");
- assistantReply.append(content);
- attemptState.setReceivedDelta(true);
- attemptState.setActualModelCode(modelCode);
- return true;
- }
- if ("error".equals(type)) {
- attemptState.setSuccess(false);
- attemptState.setActualModelCode(modelCode);
- attemptState.setErrorMessage(event.path("message").asText("妯″瀷璋冪敤澶辫触"));
- attemptState.setResponseTime(parseResponseTime(event));
- attemptState.setInterrupted(isInterruptedMessage(attemptState.getErrorMessage()));
- return false;
- }
- if ("done".equals(type)) {
- attemptState.setSuccess(true);
- attemptState.setActualModelCode(modelCode);
- attemptState.setResponseTime(parseResponseTime(event));
- return false;
- }
- return true;
- }
-
- /**
- * 浠庣綉鍏充簨浠朵腑鎻愬彇鍝嶅簲鏃堕棿銆�
- */
- private Date parseResponseTime(JsonNode event) {
- long millis = event.path("responseTime").asLong(0L);
- return millis <= 0L ? new Date() : new Date(millis);
- }
-
- /**
- * 灏嗚鍒掓墽琛岀粨鏋滃帇缂╂垚閫傚悎鍥炲啓鍒拌鍒掕褰曠殑鐭秷鎭��
- */
- private String buildPlanMessage(String text, String fallback) {
- String source = text == null ? "" : text.trim();
- if (source.isEmpty()) {
- return fallback;
- }
- return source.length() > 120 ? source.substring(0, 120) : source;
- }
-
- /**
- * 鍒ゆ柇寮傚父閾句腑鏄惁鍖呭惈涓柇绫婚敊璇��
- */
- private boolean isInterruptedError(Throwable throwable) {
- Throwable current = throwable;
- while (current != null) {
- if (current instanceof InterruptedException || current instanceof InterruptedIOException) {
- return true;
- }
- if (isInterruptedMessage(current.getMessage())) {
- return true;
- }
- current = current.getCause();
- }
- return false;
- }
-
- /**
- * 鏍规嵁寮傚父娑堟伅鏂囨湰鍒ゆ柇鏄惁灞炰簬杩炴帴涓柇绫婚敊璇��
- */
- private boolean isInterruptedMessage(String message) {
- if (message == null || message.trim().isEmpty()) {
- return false;
- }
- String normalized = message.toLowerCase();
- return normalized.contains("interrupted")
- || normalized.contains("broken pipe")
- || normalized.contains("connection reset")
- || normalized.contains("forcibly closed");
- }
-
- private static class AttemptState {
- private Boolean success;
- private String actualModelCode;
- private String errorMessage;
- private boolean receivedDelta;
- private boolean interrupted;
- private Date responseTime;
-
- public Boolean getSuccess() {
- return success;
- }
-
- public void setSuccess(Boolean success) {
- this.success = success;
- }
-
- public String getActualModelCode() {
- return actualModelCode;
- }
-
- public void setActualModelCode(String actualModelCode) {
- this.actualModelCode = actualModelCode;
- }
-
- public String getErrorMessage() {
- return errorMessage;
- }
-
- public void setErrorMessage(String errorMessage) {
- this.errorMessage = errorMessage;
- }
-
- public boolean isReceivedDelta() {
- return receivedDelta;
- }
-
- public void setReceivedDelta(boolean receivedDelta) {
- this.receivedDelta = receivedDelta;
- }
-
- public boolean isInterrupted() {
- return interrupted;
- }
-
- public void setInterrupted(boolean interrupted) {
- this.interrupted = interrupted;
- }
-
- public Date getResponseTime() {
- return responseTime;
- }
-
- public void setResponseTime(Date responseTime) {
- this.responseTime = responseTime;
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanScheduler.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanScheduler.java
deleted file mode 100644
index 4a52f10..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisPlanScheduler.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-import com.vincent.rsf.server.system.service.AiDiagnosisPlanService;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.Resource;
-import java.util.Date;
-import java.util.List;
-
-@Component
-public class AiDiagnosisPlanScheduler {
-
- @Resource
- private AiDiagnosisPlanService aiDiagnosisPlanService;
- @Resource
- private AiDiagnosisPlanRunnerService aiDiagnosisPlanRunnerService;
- @Resource
- private ThreadPoolTaskScheduler taskScheduler;
-
- /**
- * 姣� 30 绉掓壂鎻忎竴娆″埌鏈熷贰妫�璁″垝锛屽苟寮傛鍒嗗彂鎵ц銆�
- */
- @Scheduled(cron = "0/30 * * * * ?")
- public void dispatchDuePlans() {
- Date now = new Date();
- List<AiDiagnosisPlan> plans = aiDiagnosisPlanService.listDuePlans(now);
- for (AiDiagnosisPlan plan : plans) {
- Long operatorId = plan.getUpdateBy() == null ? plan.getCreateBy() : plan.getUpdateBy();
- Date nextRunTime = aiDiagnosisPlanService.calculateNextRunTime(plan.getCronExpr(), now);
- boolean acquired = aiDiagnosisPlanService.acquireForExecution(
- plan.getId(),
- operatorId,
- "璁″垝鎵ц涓�",
- nextRunTime
- );
- if (!acquired) {
- continue;
- }
- taskScheduler.execute(() -> aiDiagnosisPlanRunnerService.runPlan(plan.getId(), false));
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisReportService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisReportService.java
deleted file mode 100644
index c8b627e..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisReportService.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiDiagnosisReportService {
-
- /**
- * 鍩轰簬妯″瀷缁撹鍜屽伐鍏锋憳瑕佸洖濉瘖鏂姤鍛婄粨鏋勫寲瀛楁涓� Markdown 鍐呭銆�
- */
- public void fillReport(AiDiagnosisRecord record, String conclusion, String toolSummary) {
- if (record == null) {
- return;
- }
- Map<String, String> sections = extractSections(conclusion);
- String executiveSummary = firstNonBlank(
- sections.get("闂姒傝堪"),
- sections.get("鎵ц鎽樿"),
- safeText(conclusion)
- );
- String evidenceSummary = firstNonBlank(
- sections.get("鍏抽敭璇佹嵁"),
- sections.get("璇佹嵁鎽樿"),
- safeText(toolSummary)
- );
- String actionSummary = firstNonBlank(
- sections.get("寤鸿鍔ㄤ綔"),
- sections.get("澶勭疆寤鸿"),
- "璇风粨鍚堢幇鍦烘祦绋嬬户缁‘璁ゃ��"
- );
- String riskSummary = firstNonBlank(
- sections.get("椋庨櫓璇勪及"),
- sections.get("娼滃湪椋庨櫓"),
- "濡傛湭鍙婃椂澶勭悊锛屽彲鑳界户缁奖鍝嶅綋鍓嶄笟鍔¤繍琛屻��"
- );
- record.setReportTitle(buildTitle(record));
- record.setExecutiveSummary(executiveSummary);
- record.setEvidenceSummary(evidenceSummary);
- record.setActionSummary(actionSummary);
- record.setRiskSummary(riskSummary);
- record.setReportMarkdown(buildMarkdown(record, executiveSummary, evidenceSummary, actionSummary, riskSummary, conclusion));
- }
-
- /**
- * 灏濊瘯浠庡師濮嬬粨璁烘枃鏈腑鎻愬彇鈥滈棶棰樻杩�/鍏抽敭璇佹嵁/寤鸿鍔ㄤ綔/椋庨櫓璇勪及鈥濈瓑绔犺妭銆�
- */
- private Map<String, String> extractSections(String conclusion) {
- Map<String, String> sections = new LinkedHashMap<>();
- if (conclusion == null || conclusion.trim().isEmpty()) {
- return sections;
- }
- String[] lines = conclusion.replace("\r", "").split("\n");
- String currentTitle = "闂姒傝堪";
- StringBuilder currentBody = new StringBuilder();
- for (String rawLine : lines) {
- String line = rawLine == null ? "" : rawLine.trim();
- String title = normalizeTitle(line);
- if (title != null) {
- saveSection(sections, currentTitle, currentBody);
- currentTitle = title;
- currentBody = new StringBuilder();
- String remainder = line.substring(line.indexOf(title) + title.length()).trim();
- if (remainder.startsWith("锛�") || remainder.startsWith(":")) {
- remainder = remainder.substring(1).trim();
- }
- if (!remainder.isEmpty()) {
- currentBody.append(remainder);
- }
- continue;
- }
- if (currentBody.length() > 0) {
- currentBody.append("\n");
- }
- currentBody.append(rawLine == null ? "" : rawLine);
- }
- saveSection(sections, currentTitle, currentBody);
- return sections;
- }
-
- /**
- * 灏嗗綋鍓嶇珷鑺傚唴瀹瑰啓鍏� section map銆�
- */
- private void saveSection(Map<String, String> sections, String title, StringBuilder body) {
- String content = body == null ? "" : body.toString().trim();
- if (content.isEmpty()) {
- return;
- }
- sections.put(title, content);
- }
-
- /**
- * 鍒ゆ柇涓�琛屾枃鏈槸鍚﹀彲瑙嗕负鎶ュ憡绔犺妭鏍囬銆�
- */
- private String normalizeTitle(String line) {
- if (line == null) {
- return null;
- }
- String normalized = line.replace("#", "").replace("锛�", "").replace(":", "").trim();
- List<String> titles = new ArrayList<>();
- titles.add("闂姒傝堪");
- titles.add("鎵ц鎽樿");
- titles.add("鍏抽敭璇佹嵁");
- titles.add("璇佹嵁鎽樿");
- titles.add("鍙兘鍘熷洜");
- titles.add("寤鸿鍔ㄤ綔");
- titles.add("澶勭疆寤鸿");
- titles.add("椋庨櫓璇勪及");
- titles.add("娼滃湪椋庨櫓");
- for (String title : titles) {
- if (normalized.startsWith(title)) {
- return title;
- }
- }
- return null;
- }
-
- /**
- * 鏋勯�犺瘖鏂姤鍛婃爣棰樸��
- */
- private String buildTitle(AiDiagnosisRecord record) {
- String diagnosisNo = record.getDiagnosisNo() == null ? "-" : record.getDiagnosisNo();
- return "WMS璇婃柇鎶ュ憡-" + diagnosisNo;
- }
-
- /**
- * 鐢熸垚鏈�缁堟姤鍛� Markdown銆�
- */
- private String buildMarkdown(AiDiagnosisRecord record, String executiveSummary, String evidenceSummary,
- String actionSummary, String riskSummary, String conclusion) {
- List<String> parts = new ArrayList<>();
- parts.add("# " + buildTitle(record));
- parts.add("");
- parts.add("## 闂姒傝堪");
- parts.add(safeText(executiveSummary));
- parts.add("");
- parts.add("## 鍏抽敭璇佹嵁");
- parts.add(safeText(evidenceSummary));
- parts.add("");
- parts.add("## 寤鸿鍔ㄤ綔");
- parts.add(safeText(actionSummary));
- parts.add("");
- parts.add("## 椋庨櫓璇勪及");
- parts.add(safeText(riskSummary));
- if (conclusion != null && !conclusion.trim().isEmpty()) {
- parts.add("");
- parts.add("## 鍘熷缁撹");
- parts.add(conclusion.trim());
- }
- return String.join("\n", parts);
- }
-
- /**
- * 杩斿洖绗竴涓潪绌烘枃鏈��
- */
- private String firstNonBlank(String... values) {
- if (values == null) {
- return "";
- }
- for (String value : values) {
- if (value != null && !value.trim().isEmpty()) {
- return value.trim();
- }
- }
- return "";
- }
-
- /**
- * 瀵瑰睍绀烘枃鏈仛绌哄�煎厹搴曘��
- */
- private String safeText(String value) {
- return value == null || value.trim().isEmpty() ? "鏆傛棤鍐呭" : value.trim();
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisRuntimeService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisRuntimeService.java
deleted file mode 100644
index 4dfa850..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosisRuntimeService.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import com.vincent.rsf.server.system.service.AiCallLogService;
-import com.vincent.rsf.server.system.service.AiDiagnosisRecordService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.Date;
-
-@Service
-public class AiDiagnosisRuntimeService {
-
- @Resource
- private AiDiagnosisRecordService aiDiagnosisRecordService;
- @Resource
- private AiCallLogService aiCallLogService;
- @Resource
- private SnowflakeIdWorker snowflakeIdWorker;
- @Resource
- private AiDiagnosisReportService aiDiagnosisReportService;
-
- /**
- * 鍦ㄨ瘖鏂紑濮嬫椂鍒涘缓涓�鏉¤瘖鏂褰曪紝骞舵爣璁颁负鈥滆繘琛屼腑鈥濄��
- */
- public AiDiagnosisRecord startDiagnosis(Long tenantId, Long userId, String sessionId, String sceneCode, String question) {
- AiDiagnosisRecord record = new AiDiagnosisRecord()
- .setDiagnosisNo(String.valueOf(snowflakeIdWorker.nextId()).substring(3))
- .setTenantId(tenantId)
- .setUserId(userId)
- .setSessionId(sessionId)
- .setSceneCode(sceneCode)
- .setQuestion(question)
- .setResult(2)
- .setStatus(1)
- .setDeleted(0)
- .setStartTime(new Date())
- .setCreateTime(new Date())
- .setUpdateTime(new Date());
- aiDiagnosisRecordService.save(record);
- return record;
- }
-
- /**
- * 灏嗚瘖鏂褰曟爣璁颁负鎴愬姛锛屽苟琛ラ綈鎶ュ憡鍐呭銆佹ā鍨嬩俊鎭拰鑰楁椂銆�
- */
- public void finishDiagnosisSuccess(AiDiagnosisRecord record, String conclusion, String modelCode, String toolSummary) {
- if (record == null) {
- return;
- }
- Date now = new Date();
- record.setConclusion(conclusion);
- aiDiagnosisReportService.fillReport(record, conclusion, toolSummary);
- record.setModelCode(modelCode);
- record.setToolSummary(toolSummary);
- record.setResult(1);
- record.setEndTime(now);
- record.setSpendTime(now.getTime() - record.getStartTime().getTime());
- record.setUpdateTime(now);
- aiDiagnosisRecordService.updateById(record);
- }
-
- /**
- * 灏嗚瘖鏂褰曟爣璁颁负澶辫触锛屽苟淇濈暀宸叉湁缁撹銆侀敊璇俊鎭拰宸ュ叿杞ㄨ抗銆�
- */
- public void finishDiagnosisFailure(AiDiagnosisRecord record, String conclusion, String err, String toolSummary) {
- if (record == null) {
- return;
- }
- Date now = new Date();
- record.setConclusion(conclusion);
- aiDiagnosisReportService.fillReport(record, conclusion, toolSummary);
- record.setToolSummary(toolSummary);
- record.setErr(err);
- record.setResult(0);
- record.setEndTime(now);
- record.setSpendTime(record.getStartTime() == null ? null : now.getTime() - record.getStartTime().getTime());
- record.setUpdateTime(now);
- aiDiagnosisRecordService.updateById(record);
- }
-
- /**
- * 淇濆瓨涓�娆℃ā鍨嬭皟鐢ㄦ棩蹇楋紝渚涘璁°�佺粺璁″拰鏁呴殰瀹氫綅浣跨敤銆�
- */
- public AiCallLog saveCallLog(Long tenantId, Long userId, String sessionId, Long diagnosisId, String routeCode,
- String modelCode, Integer attemptNo, Date requestTime, Date responseTime,
- Integer result, String err) {
- AiCallLog log = new AiCallLog()
- .setTenantId(tenantId)
- .setUserId(userId)
- .setSessionId(sessionId)
- .setDiagnosisId(diagnosisId)
- .setRouteCode(routeCode)
- .setModelCode(modelCode)
- .setAttemptNo(attemptNo)
- .setRequestTime(requestTime)
- .setResponseTime(responseTime)
- .setSpendTime(requestTime == null || responseTime == null ? null : responseTime.getTime() - requestTime.getTime())
- .setResult(result)
- .setErr(err)
- .setStatus(1)
- .setDeleted(0)
- .setCreateTime(responseTime == null ? new Date() : responseTime);
- aiCallLogService.save(log);
- return log;
- }
-
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosticToolService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosticToolService.java
deleted file mode 100644
index 4525963..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/diagnosis/AiDiagnosticToolService.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package com.vincent.rsf.server.ai.service.diagnosis;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpPayloadMapper;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpRegistryService;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiDiagnosticToolService {
-
- @Resource
- private ObjectMapper objectMapper;
- @Resource
- private AiDiagnosticToolConfigService aiDiagnosticToolConfigService;
- @Resource
- private AiMcpRegistryService aiMcpRegistryService;
- @Resource
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- /**
- * 鏀堕泦褰撳墠绉熸埛鐨勬墍鏈夊唴缃瘖鏂伐鍏风粨鏋溿��
- * 杩欐槸璇婃柇鍦烘櫙鐨勫厹搴曡矾寰勶紝涔熺敤浜庡湪妯″瀷瑙勫垝澶辫触鏃跺己鍒惰仛鍚堜竴杞唴閮ㄦ暟鎹��
- */
- public List<AiDiagnosticToolResult> collect(AiPromptContext context) {
- List<AiDiagnosticToolResult> output = new ArrayList<>();
- for (AiMcpToolDescriptor descriptor : resolveInternalTools(context)) {
- try {
- AiDiagnosticToolResult result = aiMcpRegistryService.executeTool(context.getTenantId(), descriptor, context);
- if (result != null && result.getSummaryText() != null && !result.getSummaryText().trim().isEmpty()) {
- output.add(result);
- }
- } catch (Exception e) {
- output.add(new AiDiagnosticToolResult()
- .setToolCode(descriptor.getToolCode())
- .setMountCode(descriptor.getMountCode() == null ? AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE : descriptor.getMountCode())
- .setMcpToolName(descriptor.getMcpToolName())
- .setToolName(descriptor.getToolName())
- .setSeverity("WARN")
- .setSummaryText("宸ュ叿鎵ц澶辫触锛�" + e.getMessage()));
- }
- }
- return output;
- }
-
- /**
- * 灏嗗伐鍏风粨鏋滃垪琛ㄨ浆鎹负鍙洿鎺ユ嫾杩涚郴缁� Prompt 鐨勬憳瑕佹枃鏈��
- */
- public String buildPrompt(List<AiDiagnosticToolResult> results) {
- return buildPrompt(null, null, results);
- }
-
- /**
- * 鏍规嵁褰撳墠鍦烘櫙琛ュ厖宸ュ叿绾ч檮鍔犺鍒欙紝骞剁敓鎴愭渶缁堝伐鍏锋憳瑕� Prompt銆�
- */
- public String buildPrompt(Long tenantId, String sceneCode, List<AiDiagnosticToolResult> results) {
- if (results == null || results.isEmpty()) {
- return "";
- }
- Map<String, AiDiagnosticToolConfig> configMap = buildConfigMap(tenantId, sceneCode);
- List<String> parts = new ArrayList<>();
- parts.add("浠ヤ笅鏄瘖鏂伐鍏疯繑鍥炵殑瀹炴椂鎽樿锛岃浼樺厛渚濇嵁杩欎簺缁撴灉鍒ゆ柇锛�");
- for (AiDiagnosticToolResult item : results) {
- AiDiagnosticToolConfig config = configMap.get(item.getToolCode());
- if (config != null && config.getToolPrompt() != null && !config.getToolPrompt().trim().isEmpty()) {
- parts.add("[" + safeValue(item.getToolName()) + "][鎸囦护] " + safeValue(config.getToolPrompt()));
- }
- parts.add("[" + safeValue(item.getToolName()) + "][" + safeValue(item.getSeverity()) + "] " + safeValue(item.getSummaryText()));
- }
- return String.join("\n", parts);
- }
-
- /**
- * 灏嗗伐鍏风粨鏋滃簭鍒楀寲鎴� JSON锛屼究浜庤瘖鏂褰曚笌宸ュ叿杞ㄨ抗钀藉簱銆�
- */
- public String serializeResults(List<AiDiagnosticToolResult> results) {
- if (results == null || results.isEmpty()) {
- return "[]";
- }
- try {
- return objectMapper.writeValueAsString(results);
- } catch (Exception e) {
- return "[]";
- }
- }
-
- private String safeValue(String value) {
- return value == null ? "" : value;
- }
-
- /**
- * 鎸夊綋鍓嶅満鏅В鏋愬彲浠ュ弬涓庢墽琛岀殑鍐呴儴宸ュ叿鍒楄〃锛屽苟鎸変紭鍏堢骇鎺掑簭銆�
- */
- private List<AiMcpToolDescriptor> resolveInternalTools(AiPromptContext context) {
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- if (context == null || context.getTenantId() == null) {
- return output;
- }
- for (AiMcpToolDescriptor descriptor : aiMcpRegistryService.listInternalTools(context.getTenantId())) {
- if (descriptor == null || !Integer.valueOf(1).equals(descriptor.getEnabledFlag())) {
- continue;
- }
- if (context.getSceneCode() != null
- && descriptor.getSceneCode() != null
- && !descriptor.getSceneCode().trim().isEmpty()
- && !context.getSceneCode().equals(descriptor.getSceneCode())) {
- continue;
- }
- output.add(descriptor);
- }
- output.sort(Comparator.comparing(
- item -> item.getPriority() == null ? Integer.MAX_VALUE : item.getPriority()
- ));
- return output;
- }
-
- /**
- * 涓哄綋鍓嶅満鏅瀯寤哄伐鍏烽厤缃储寮曘��
- * 褰撳伐鍏风敤閫斾负鈥滆亰澶╀笌璇婃柇閮藉彲鐢ㄢ�濇椂锛屼細鍏佽鍦ㄥ涓満鏅笅澶嶇敤鍚屼竴鏉¢厤缃��
- */
- private Map<String, AiDiagnosticToolConfig> buildConfigMap(Long tenantId, String sceneCode) {
- Map<String, AiDiagnosticToolConfig> map = new LinkedHashMap<>();
- if (tenantId == null || sceneCode == null || sceneCode.trim().isEmpty()) {
- return map;
- }
- List<AiDiagnosticToolConfig> configs = aiDiagnosticToolConfigService.listTenantConfigs(tenantId);
- for (AiDiagnosticToolConfig config : emptyList(configs)) {
- if (!Integer.valueOf(1).equals(config.getStatus())) {
- continue;
- }
- String usageScope = aiMcpPayloadMapper.resolveUsageScope(config.getSceneCode(), config.getEnabledFlag(), config.getUsageScope());
- if (AiMcpConstants.USAGE_SCOPE_DISABLED.equals(usageScope)) {
- continue;
- }
- if (AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY.equals(usageScope)
- && !sceneCode.equals(config.getSceneCode())) {
- continue;
- }
- AiDiagnosticToolConfig existed = map.get(config.getToolCode());
- if (existed == null || preferConfig(config, existed, sceneCode)) {
- map.put(config.getToolCode(), config);
- }
- }
- return map;
- }
-
- /**
- * 鍐茬獊鏃朵紭鍏堥�夋嫨涓庡綋鍓嶅満鏅簿纭尮閰嶇殑閰嶇疆锛屽叾娆℃瘮杈冧紭鍏堢骇銆�
- */
- private boolean preferConfig(AiDiagnosticToolConfig candidate, AiDiagnosticToolConfig current, String sceneCode) {
- boolean candidateExact = sceneCode.equals(candidate.getSceneCode());
- boolean currentExact = sceneCode.equals(current.getSceneCode());
- if (candidateExact != currentExact) {
- return candidateExact;
- }
- Integer candidatePriority = candidate.getPriority() == null ? Integer.MAX_VALUE : candidate.getPriority();
- Integer currentPriority = current.getPriority() == null ? Integer.MAX_VALUE : current.getPriority();
- return candidatePriority < currentPriority;
- }
-
- private <T> Collection<T> emptyList(List<T> list) {
- return list == null ? new ArrayList<>() : list;
- }
-
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiSessionServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiSessionServiceImpl.java
deleted file mode 100644
index 673c8fe..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiSessionServiceImpl.java
+++ /dev/null
@@ -1,400 +0,0 @@
-package com.vincent.rsf.server.ai.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.mapper.AiChatMessageMapper;
-import com.vincent.rsf.server.ai.mapper.AiChatSessionMapper;
-import com.vincent.rsf.server.ai.model.AiChatMessage;
-import com.vincent.rsf.server.ai.model.AiChatSession;
-import com.vincent.rsf.server.ai.service.AiRuntimeConfigService;
-import com.vincent.rsf.server.ai.service.AiSessionService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.Resource;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-@Service
-public class AiSessionServiceImpl implements AiSessionService {
-
- private static final ConcurrentMap<String, List<AiChatSession>> LOCAL_SESSION_CACHE = new ConcurrentHashMap<>();
- private static final ConcurrentMap<String, List<AiChatMessage>> LOCAL_MESSAGE_CACHE = new ConcurrentHashMap<>();
- private static final ConcurrentMap<String, String> LOCAL_STOP_CACHE = new ConcurrentHashMap<>();
- private static final String SESSION_TABLE_NAME = "sys_ai_chat_session";
- private static final String MESSAGE_TABLE_NAME = "sys_ai_chat_message";
-
- @Resource
- private AiRuntimeConfigService aiRuntimeConfigService;
- @Resource
- private AiChatSessionMapper aiChatSessionMapper;
- @Resource
- private AiChatMessageMapper aiChatMessageMapper;
- @Resource
- private DataSource dataSource;
-
- private volatile boolean storageReady;
-
- @PostConstruct
- /**
- * 鍚姩鏃舵帰娴嬭亰澶╁瓨鍌ㄨ〃鏄惁宸插垱寤恒��
- * 濡傛灉琛ㄥ瓨鍦ㄥ垯璧版暟鎹簱鎸佷箙鍖栵紝鍚﹀垯鍥為��鍒版湰鍦板唴瀛樼紦瀛橈紝淇濊瘉寮�鍙戝拰缂鸿〃鍦烘櫙鍙户缁繍琛屻��
- */
- public void initStorageMode() {
- storageReady = detectStorageTables();
- }
-
- @Override
- /**
- * 璇诲彇鐢ㄦ埛浼氳瘽鍒楄〃銆�
- * 鏁版嵁搴撳瓨鍌ㄦā寮忕洿鎺ユ煡琛紝鍐呭瓨妯″紡鍒欎粠鏈湴缂撳瓨鍙栧嚭骞舵寜鏈�杩戞洿鏂版椂闂存帓搴忋��
- */
- public synchronized List<AiChatSession> listSessions(Long tenantId, Long userId) {
- if (useDatabaseStorage()) {
- return aiChatSessionMapper.selectList(new LambdaQueryWrapper<AiChatSession>()
- .eq(AiChatSession::getTenantId, tenantId)
- .eq(AiChatSession::getUserId, userId)
- .orderByDesc(AiChatSession::getUpdateTime, AiChatSession::getCreateTime));
- }
- List<AiChatSession> sessions = getSessions(tenantId, userId);
- sessions.sort(Comparator.comparing(AiChatSession::getUpdateTime, Comparator.nullsLast(Date::compareTo)).reversed());
- return sessions;
- }
-
- @Override
- /**
- * 鍒涘缓鏂颁細璇濓紝骞跺垵濮嬪寲鏍囬銆佹ā鍨嬪拰鏃堕棿鎴炽��
- */
- public synchronized AiChatSession createSession(Long tenantId, Long userId, String title, String modelCode) {
- List<AiChatSession> sessions = useDatabaseStorage() ? listSessions(tenantId, userId) : getSessions(tenantId, userId);
- Date now = new Date();
- AiChatSession session = new AiChatSession()
- .setId(UUID.randomUUID().toString().replace("-", ""))
- .setTenantId(tenantId)
- .setUserId(userId)
- .setTitle(resolveTitle(title, sessions.size() + 1))
- .setModelCode(resolveModelCode(modelCode))
- .setCreateTime(now)
- .setUpdateTime(now)
- .setLastMessageAt(now)
- .setStatus(1)
- .setDeleted(0);
- if (useDatabaseStorage()) {
- aiChatSessionMapper.insert(session);
- return session;
- }
- sessions.add(0, session);
- saveSessions(tenantId, userId, sessions);
- saveMessages(session.getId(), new ArrayList<>());
- return session;
- }
-
- @Override
- /**
- * 纭繚浼氳瘽瀛樺湪锛涘鏋滀細璇濆凡瀛樺湪浣嗘ā鍨嬪彂鐢熷彉鍖栵紝浼氬悓姝ユ洿鏂颁細璇濊褰曘��
- */
- public synchronized AiChatSession ensureSession(Long tenantId, Long userId, String sessionId, String modelCode) {
- AiChatSession session = getSession(tenantId, userId, sessionId);
- if (session == null) {
- return createSession(tenantId, userId, null, modelCode);
- }
- if (modelCode != null && !modelCode.trim().isEmpty() && !modelCode.equals(session.getModelCode())) {
- session.setModelCode(modelCode);
- session.setUpdateTime(new Date());
- refreshSession(tenantId, userId, session);
- }
- return session;
- }
-
- @Override
- /**
- * 瀹夊叏璇诲彇浼氳瘽锛屽苟鏍¢獙绉熸埛涓庣敤鎴峰綊灞炪��
- */
- public synchronized AiChatSession getSession(Long tenantId, Long userId, String sessionId) {
- if (sessionId == null || sessionId.trim().isEmpty()) {
- return null;
- }
- if (useDatabaseStorage()) {
- AiChatSession session = aiChatSessionMapper.selectById(sessionId);
- if (session == null) {
- return null;
- }
- if (!Objects.equals(tenantId, session.getTenantId()) || !Objects.equals(userId, session.getUserId())) {
- return null;
- }
- return session;
- }
- for (AiChatSession session : getSessions(tenantId, userId)) {
- if (sessionId.equals(session.getId())) {
- return session;
- }
- }
- return null;
- }
-
- @Override
- /**
- * 鏇存柊浼氳瘽鏍囬銆�
- */
- public synchronized AiChatSession renameSession(Long tenantId, Long userId, String sessionId, String title) {
- AiChatSession session = getSession(tenantId, userId, sessionId);
- if (session == null) {
- return null;
- }
- session.setTitle(resolveTitle(title, 1));
- session.setUpdateTime(new Date());
- refreshSession(tenantId, userId, session);
- return session;
- }
-
- @Override
- /**
- * 鍒犻櫎浼氳瘽鍙婂叾鍏宠仈娑堟伅锛屽悓鏃舵竻鐞嗗仠姝㈡爣璁扮紦瀛樸��
- */
- public synchronized void removeSession(Long tenantId, Long userId, String sessionId) {
- if (useDatabaseStorage()) {
- AiChatSession session = getSession(tenantId, userId, sessionId);
- if (session != null) {
- aiChatMessageMapper.delete(new LambdaQueryWrapper<AiChatMessage>()
- .eq(AiChatMessage::getSessionId, sessionId));
- aiChatSessionMapper.deleteById(sessionId);
- }
- LOCAL_STOP_CACHE.remove(sessionId);
- return;
- }
- List<AiChatSession> sessions = getSessions(tenantId, userId);
- sessions.removeIf(session -> sessionId.equals(session.getId()));
- saveSessions(tenantId, userId, sessions);
- LOCAL_MESSAGE_CACHE.remove(sessionId);
- LOCAL_STOP_CACHE.remove(sessionId);
- }
-
- @Override
- /**
- * 鏌ヨ浼氳瘽鐨勫畬鏁存秷鎭巻鍙层��
- */
- public synchronized List<AiChatMessage> listMessages(Long tenantId, Long userId, String sessionId) {
- AiChatSession session = getSession(tenantId, userId, sessionId);
- if (session == null) {
- return new ArrayList<>();
- }
- if (useDatabaseStorage()) {
- return aiChatMessageMapper.selectList(new LambdaQueryWrapper<AiChatMessage>()
- .eq(AiChatMessage::getSessionId, sessionId)
- .orderByAsc(AiChatMessage::getCreateTime, AiChatMessage::getId));
- }
- return getMessages(sessionId);
- }
-
- @Override
- /**
- * 鎴彇鏈�杩戣嫢骞叉潯娑堟伅浣滀负妯″瀷涓婁笅鏂囷紝閬垮厤姣忔閮芥妸瀹屾暣鍘嗗彶鍙戦�佺粰妯″瀷銆�
- */
- public synchronized List<AiChatMessage> listContextMessages(Long tenantId, Long userId, String sessionId, int maxCount) {
- List<AiChatMessage> messages = listMessages(tenantId, userId, sessionId);
- if (messages.size() <= maxCount) {
- return messages;
- }
- return new ArrayList<>(messages.subList(messages.size() - maxCount, messages.size()));
- }
-
- @Override
- /**
- * 杩藉姞涓�鏉℃秷鎭紝骞跺悓姝ュ埛鏂颁細璇濇憳瑕併�佹椿璺冩椂闂村拰榛樿鏍囬銆�
- */
- public synchronized AiChatMessage appendMessage(Long tenantId, Long userId, String sessionId, String role, String content, String modelCode) {
- AiChatSession session = getSession(tenantId, userId, sessionId);
- if (session == null) {
- return null;
- }
- List<AiChatMessage> messages = getMessages(sessionId);
- AiChatMessage message = new AiChatMessage()
- .setId(UUID.randomUUID().toString().replace("-", ""))
- .setTenantId(tenantId)
- .setUserId(userId)
- .setSessionId(sessionId)
- .setRole(role)
- .setContent(content)
- .setModelCode(resolveModelCode(modelCode))
- .setCreateTime(new Date())
- .setStatus(1)
- .setDeleted(0);
- if (useDatabaseStorage()) {
- aiChatMessageMapper.insert(message);
- } else {
- messages.add(message);
- saveMessages(sessionId, messages);
- }
- session.setLastMessage(buildPreview(content));
- session.setLastMessageAt(message.getCreateTime());
- session.setUpdateTime(message.getCreateTime());
- if (modelCode != null && !modelCode.trim().isEmpty()) {
- session.setModelCode(modelCode);
- }
- if ((session.getTitle() == null || session.getTitle().startsWith("鏂板璇�")) && "user".equalsIgnoreCase(role)) {
- session.setTitle(buildPreview(content));
- }
- refreshSession(tenantId, userId, session);
- return message;
- }
-
- @Override
- /**
- * 娓呴櫎鍋滄鐢熸垚鏍囪銆�
- */
- public void clearStopFlag(String sessionId) {
- LOCAL_STOP_CACHE.remove(sessionId);
- }
-
- @Override
- /**
- * 鏍囪浼氳瘽闇�瑕佸仠姝㈢敓鎴愩��
- */
- public void requestStop(String sessionId) {
- LOCAL_STOP_CACHE.put(sessionId, "1");
- }
-
- @Override
- /**
- * 璇诲彇鍋滄鐢熸垚鏍囪銆�
- */
- public boolean isStopRequested(String sessionId) {
- String stopFlag = LOCAL_STOP_CACHE.get(sessionId);
- return "1".equals(stopFlag);
- }
-
- /**
- * 浠庡唴瀛樼紦瀛樹腑璇诲彇褰撳墠鐢ㄦ埛鐨勪細璇濆垪琛ㄣ��
- */
- private List<AiChatSession> getSessions(Long tenantId, Long userId) {
- String ownerKey = buildOwnerKey(tenantId, userId);
- List<AiChatSession> sessions = LOCAL_SESSION_CACHE.get(ownerKey);
- return sessions == null ? new ArrayList<>() : new ArrayList<>(sessions);
- }
-
- /**
- * 灏嗕細璇濆垪琛ㄥ啓鍥炴湰鍦扮紦瀛樸��
- */
- private void saveSessions(Long tenantId, Long userId, List<AiChatSession> sessions) {
- String ownerKey = buildOwnerKey(tenantId, userId);
- List<AiChatSession> cachedSessions = new ArrayList<>(sessions);
- LOCAL_SESSION_CACHE.put(ownerKey, cachedSessions);
- }
-
- /**
- * 浠庡唴瀛樼紦瀛樹腑璇诲彇鎸囧畾浼氳瘽鐨勬秷鎭垪琛ㄣ��
- */
- private List<AiChatMessage> getMessages(String sessionId) {
- List<AiChatMessage> messages = LOCAL_MESSAGE_CACHE.get(sessionId);
- return messages == null ? new ArrayList<>() : new ArrayList<>(messages);
- }
-
- /**
- * 灏嗘秷鎭垪琛ㄥ啓鍥炴湰鍦扮紦瀛樸��
- */
- private void saveMessages(String sessionId, List<AiChatMessage> messages) {
- List<AiChatMessage> cachedMessages = new ArrayList<>(messages);
- LOCAL_MESSAGE_CACHE.put(sessionId, cachedMessages);
- }
-
- /**
- * 鎸夊瓨鍌ㄦā寮忓埛鏂板崟涓細璇濊褰曘��
- */
- private void refreshSession(Long tenantId, Long userId, AiChatSession target) {
- if (useDatabaseStorage()) {
- aiChatSessionMapper.updateById(target);
- return;
- }
- List<AiChatSession> sessions = getSessions(tenantId, userId);
- for (int i = 0; i < sessions.size(); i++) {
- if (target.getId().equals(sessions.get(i).getId())) {
- sessions.set(i, target);
- saveSessions(tenantId, userId, sessions);
- return;
- }
- }
- sessions.add(target);
- saveSessions(tenantId, userId, sessions);
- }
-
- /**
- * 缁勮绉熸埛涓庣敤鎴风淮搴︾殑鏈湴缂撳瓨 key銆�
- */
- private String buildOwnerKey(Long tenantId, Long userId) {
- return String.valueOf(tenantId) + ":" + String.valueOf(userId);
- }
-
- /**
- * 瑙f瀽鏈娑堟伅浣跨敤鐨勬ā鍨嬬紪鐮侊紱涓虹┖鏃跺洖閫�鍒扮郴缁熼粯璁ゆā鍨嬨��
- */
- private String resolveModelCode(String modelCode) {
- return modelCode == null || modelCode.trim().isEmpty() ? aiRuntimeConfigService.resolveDefaultModelCode() : modelCode;
- }
-
- /**
- * 鐢熸垚浼氳瘽鏍囬锛屾湭鏄惧紡浼犳爣棰樻椂浣跨敤鈥滄柊瀵硅瘽 N鈥濄��
- */
- private String resolveTitle(String title, int index) {
- if (title == null || title.trim().isEmpty()) {
- return "鏂板璇� " + index;
- }
- return buildPreview(title);
- }
-
- /**
- * 灏嗙敤鎴疯緭鍏ュ帇缂╂垚閫傚悎浣滀负鏍囬鎴栨渶鍚庢秷鎭瑙堢殑鐭枃鏈��
- */
- private String buildPreview(String content) {
- if (content == null || content.trim().isEmpty()) {
- return "鏂板璇�";
- }
- String normalized = content.replace("\r", " ").replace("\n", " ").trim();
- return normalized.length() > 24 ? normalized.substring(0, 24) : normalized;
- }
-
- /**
- * 鍒ゆ柇褰撳墠鏄惁鍙互浣跨敤鏁版嵁搴撴寔涔呭寲鑱婂ぉ鏁版嵁銆�
- */
- private boolean useDatabaseStorage() {
- return storageReady || (storageReady = detectStorageTables());
- }
-
- /**
- * 妫�鏌ヨ亰澶╁瓨鍌ㄦ墍闇�琛ㄦ槸鍚﹀凡缁忓瓨鍦ㄣ��
- */
- private boolean detectStorageTables() {
- try (Connection connection = dataSource.getConnection()) {
- return tableExists(connection, SESSION_TABLE_NAME) && tableExists(connection, MESSAGE_TABLE_NAME);
- } catch (Exception ignore) {
- return false;
- }
- }
-
- /**
- * 鍒ゆ柇鎸囧畾琛ㄥ悕鏄惁鍦ㄥ綋鍓嶆暟鎹簱涓瓨鍦ㄣ��
- */
- private boolean tableExists(Connection connection, String tableName) throws Exception {
- if (tableName == null || tableName.trim().isEmpty()) {
- return false;
- }
- String[] candidates = new String[]{tableName, tableName.toUpperCase(), tableName.toLowerCase()};
- for (String candidate : candidates) {
- try (ResultSet resultSet = connection.getMetaData().getTables(connection.getCatalog(), null, candidate, null)) {
- if (resultSet.next()) {
- return true;
- }
- }
- }
- return false;
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpHttpClient.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpHttpClient.java
deleted file mode 100644
index c75ac0d..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpHttpClient.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package com.vincent.rsf.server.ai.service.mcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Component
-public class AiMcpHttpClient {
-
- @Resource
- private ObjectMapper objectMapper;
- @Resource
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- /**
- * 閫氳繃 Streamable HTTP 鍗忚鍔犺浇杩滅▼ MCP 宸ュ叿鐩綍銆�
- */
- public List<AiMcpToolDescriptor> listTools(AiMcpMount mount) {
- initialize(mount);
- JsonNode result = sendRequest(mount, "tools/list", new LinkedHashMap<String, Object>(), true);
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- JsonNode toolsNode = result.path("tools");
- if (!toolsNode.isArray()) {
- return output;
- }
- for (JsonNode item : toolsNode) {
- AiMcpToolDescriptor descriptor = aiMcpPayloadMapper.toExternalToolDescriptor(mount, item);
- if (descriptor != null) {
- output.add(descriptor);
- }
- }
- return output;
- }
-
- /**
- * 閫氳繃 Streamable HTTP 鍗忚鎵ц涓�娆¤繙绋嬪伐鍏疯皟鐢ㄣ��
- */
- public AiDiagnosticToolResult callTool(AiMcpMount mount, String toolName, Map<String, Object> arguments) {
- initialize(mount);
- Map<String, Object> params = new LinkedHashMap<>();
- params.put("name", toolName);
- params.put("arguments", arguments == null ? new LinkedHashMap<String, Object>() : arguments);
- JsonNode result = sendRequest(mount, "tools/call", params, true);
- return aiMcpPayloadMapper.toExternalToolResult(mount, toolName, result);
- }
-
- /**
- * 鎵ц MCP initialize + notifications/initialized 鎻℃墜銆�
- */
- private void initialize(AiMcpMount mount) {
- Map<String, Object> params = new LinkedHashMap<>();
- params.put("protocolVersion", AiMcpConstants.PROTOCOL_VERSION);
- params.put("capabilities", new LinkedHashMap<String, Object>());
- Map<String, Object> clientInfo = new LinkedHashMap<>();
- clientInfo.put("name", "rsf-server");
- clientInfo.put("version", AiMcpConstants.SERVER_VERSION);
- params.put("clientInfo", clientInfo);
- sendRequest(mount, "initialize", params, true);
- sendRequest(mount, "notifications/initialized", new LinkedHashMap<String, Object>(), false);
- }
-
- /**
- * 鍙戦�佷竴鏉� JSON-RPC 璇锋眰鍒拌繙绋� MCP HTTP 绔偣銆�
- */
- private JsonNode sendRequest(AiMcpMount mount, String method, Object params, boolean expectResponse) {
- HttpURLConnection connection = null;
- try {
- connection = (HttpURLConnection) new URL(mount.getUrl()).openConnection();
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.setConnectTimeout(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- connection.setReadTimeout(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("Accept", "application/json");
- applyAuthHeaders(connection, mount);
-
- Map<String, Object> body = new LinkedHashMap<>();
- body.put("jsonrpc", "2.0");
- if (expectResponse) {
- body.put("id", String.valueOf(System.currentTimeMillis()));
- }
- body.put("method", method);
- body.put("params", params == null ? new LinkedHashMap<String, Object>() : params);
-
- try (OutputStream outputStream = connection.getOutputStream()) {
- outputStream.write(objectMapper.writeValueAsBytes(body));
- outputStream.flush();
- }
-
- int statusCode = connection.getResponseCode();
- InputStream inputStream = statusCode >= 400 ? connection.getErrorStream() : connection.getInputStream();
- if (!expectResponse) {
- return null;
- }
- if (inputStream == null) {
- throw new IllegalStateException("MCP鏈嶅姟杩斿洖绌哄搷搴�");
- }
- String payload = readPayload(inputStream);
- JsonNode root = objectMapper.readTree(payload);
- if (root.has("error") && !root.get("error").isNull()) {
- throw new IllegalStateException(root.path("error").path("message").asText("MCP璋冪敤澶辫触"));
- }
- return root.path("result");
- } catch (Exception e) {
- throw new IllegalStateException("MCP璇锋眰澶辫触: " + e.getMessage(), e);
- } finally {
- if (connection != null) {
- connection.disconnect();
- }
- }
- }
-
- /**
- * 鎸夋寕杞介厤缃啓鍏ラ壌鏉冭姹傚ご銆�
- */
- private void applyAuthHeaders(HttpURLConnection connection, AiMcpMount mount) {
- if (mount == null || mount.getAuthType() == null || mount.getAuthValue() == null || mount.getAuthValue().trim().isEmpty()) {
- return;
- }
- String authType = mount.getAuthType().trim().toUpperCase();
- if (AiMcpConstants.AUTH_TYPE_BEARER.equals(authType)) {
- connection.setRequestProperty("Authorization", "Bearer " + mount.getAuthValue().trim());
- } else if (AiMcpConstants.AUTH_TYPE_API_KEY.equals(authType)) {
- connection.setRequestProperty("X-API-Key", mount.getAuthValue().trim());
- }
- }
-
- /**
- * 璇诲彇 HTTP 鍝嶅簲浣撳叏鏂囥��
- */
- private String readPayload(InputStream inputStream) throws Exception {
- StringBuilder output = new StringBuilder();
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- String line;
- while ((line = reader.readLine()) != null) {
- output.append(line);
- }
- }
- return output.toString();
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpPayloadMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpPayloadMapper.java
deleted file mode 100644
index 1a495e3..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpPayloadMapper.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package com.vincent.rsf.server.ai.service.mcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Component
-public class AiMcpPayloadMapper {
-
- @Resource
- private ObjectMapper objectMapper;
-
- /**
- * 灏嗚繙绋� MCP tools/list 杩斿洖鐨勫崟涓伐鍏峰畾涔夎浆鎹㈡垚鏈郴缁熺粺涓�鐨勫伐鍏锋弿杩扮銆�
- */
- public AiMcpToolDescriptor toExternalToolDescriptor(AiMcpMount mount, JsonNode item) {
- String remoteName = item.path("name").asText("");
- if (remoteName.trim().isEmpty()) {
- return null;
- }
- return new AiMcpToolDescriptor()
- .setMountCode(mount.getMountCode())
- .setMountName(mount.getName())
- .setToolCode(remoteName)
- .setMcpToolName(buildMcpToolName(mount.getMountCode(), remoteName))
- .setToolName(item.path("title").asText(remoteName))
- .setSceneCode(resolveSceneCode(mount.getUsageScope()))
- .setDescription(item.path("description").asText(""))
- .setEnabledFlag(mount.getEnabledFlag())
- .setPriority(999)
- .setToolPrompt(item.path("description").asText(""))
- .setUsageScope(normalizeUsageScope(mount.getUsageScope()))
- .setTransportType(mount.getTransportType())
- .setInputSchema(readInputSchema(item.path("inputSchema"), true));
- }
-
- /**
- * 灏嗚繙绋� MCP tools/call 杩斿洖鐨勭粨鏋滆浆鎹负绯荤粺鍐呴儴缁熶竴鐨勫伐鍏风粨鏋滄ā鍨嬨��
- */
- public AiDiagnosticToolResult toExternalToolResult(AiMcpMount mount, String toolName, JsonNode result) {
- boolean isError = result.path("isError").asBoolean(false);
- Map<String, Object> rawMeta = new LinkedHashMap<>();
- if (result.has("structuredContent") && !result.get("structuredContent").isNull()) {
- rawMeta.put("structuredContent", objectMapper.convertValue(result.get("structuredContent"), Map.class));
- }
- if (result.has("content") && !result.get("content").isNull()) {
- rawMeta.put("content", objectMapper.convertValue(result.get("content"), Object.class));
- }
- return new AiDiagnosticToolResult()
- .setToolCode(toolName)
- .setMountCode(mount.getMountCode())
- .setMcpToolName(buildMcpToolName(mount.getMountCode(), toolName))
- .setToolName(toolName)
- .setSeverity(isError ? "WARN" : "INFO")
- .setSummaryText(extractContentText(result))
- .setRawMeta(rawMeta);
- }
-
- /**
- * 灏嗘湰鍦板伐鍏锋弿杩扮杞崲涓� MCP 鍗忚 tools/list 鎵�闇�鐨勬暟鎹粨鏋勩��
- */
- public Map<String, Object> toProtocolTool(AiMcpToolDescriptor descriptor) {
- Map<String, Object> item = new LinkedHashMap<>();
- item.put("name", descriptor.getMcpToolName());
- item.put("title", descriptor.getToolName());
- item.put("description", descriptor.getDescription() == null || descriptor.getDescription().trim().isEmpty()
- ? descriptor.getToolPrompt()
- : descriptor.getDescription());
- item.put("inputSchema", descriptor.getInputSchema() == null || descriptor.getInputSchema().isEmpty()
- ? defaultInputSchema(false)
- : descriptor.getInputSchema());
- return item;
- }
-
- /**
- * 璇诲彇杩滅▼宸ュ叿澹版槑涓殑 inputSchema锛涚己鐪佹椂鍥為��鍒扮郴缁熼粯璁よ緭鍏ョ粨鏋勩��
- */
- public Map<String, Object> readInputSchema(JsonNode schemaNode, boolean includeTenantId) {
- if (schemaNode == null || schemaNode.isMissingNode() || schemaNode.isNull()) {
- return defaultInputSchema(includeTenantId);
- }
- return objectMapper.convertValue(schemaNode, Map.class);
- }
-
- /**
- * 鐢熸垚绯荤粺榛樿鐨勫伐鍏疯緭鍏� schema銆�
- */
- public Map<String, Object> defaultInputSchema(boolean includeTenantId) {
- Map<String, Object> schema = new LinkedHashMap<>();
- schema.put("type", "object");
- Map<String, Object> properties = new LinkedHashMap<>();
- properties.put("question", fieldSchema("string", "褰撳墠璇婃柇闂鎴栬皟鐢ㄧ洰鐨�"));
- properties.put("sceneCode", fieldSchema("string", "璇婃柇鍦烘櫙缂栫爜"));
- if (includeTenantId) {
- properties.put("tenantId", fieldSchema("integer", "绉熸埛ID"));
- }
- schema.put("properties", properties);
- return schema;
- }
-
- /**
- * 浠� MCP tools/call 缁撴灉涓彁鍙栧彲鐩存帴缁欐ā鍨嬬湅鐨勬枃鏈憳瑕併��
- */
- public String extractContentText(JsonNode result) {
- List<String> parts = new ArrayList<>();
- JsonNode contentNode = result.path("content");
- if (contentNode.isArray()) {
- for (Iterator<JsonNode> it = contentNode.elements(); it.hasNext(); ) {
- JsonNode item = it.next();
- if ("text".equals(item.path("type").asText(""))) {
- parts.add(item.path("text").asText(""));
- } else if (item.isObject() || item.isArray()) {
- parts.add(item.toString());
- } else {
- parts.add(item.asText(""));
- }
- }
- }
- if (!parts.isEmpty()) {
- return String.join("\n", parts).trim();
- }
- if (result.has("structuredContent") && !result.get("structuredContent").isNull()) {
- return result.get("structuredContent").toString();
- }
- return result.toString();
- }
-
- /**
- * 鏋勯�犵郴缁熺粺涓�鐨� MCP 宸ュ叿鍏ㄥ悕銆�
- */
- public String buildMcpToolName(String mountCode, String toolCode) {
- return (mountCode == null ? AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE : mountCode) + "_" + toolCode;
- }
-
- /**
- * 灏嗙敤閫旈璁捐浆鎹㈡垚杩愯鏃� sceneCode銆�
- */
- public String resolveSceneCode(String usageScope) {
- if (AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY.equalsIgnoreCase(usageScope)) {
- return AiSceneCode.SYSTEM_DIAGNOSE;
- }
- return "";
- }
-
- /**
- * 鏍囧噯鍖栫敤閫旇寖鍥村瓧娈碉紝缁熶竴鎴愮郴缁熸敮鎸佺殑涓変釜鏋氫妇鍊笺��
- */
- public String normalizeUsageScope(String usageScope) {
- if (usageScope == null || usageScope.trim().isEmpty()) {
- return AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY;
- }
- String normalized = usageScope.trim().toUpperCase();
- if (AiMcpConstants.USAGE_SCOPE_CHAT_AND_DIAGNOSE.equals(normalized)
- || AiMcpConstants.USAGE_SCOPE_DISABLED.equals(normalized)) {
- return normalized;
- }
- return AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY;
- }
-
- /**
- * 鏍规嵁 sceneCode銆乪nabledFlag 鍜� usageScope 鎺ㄦ柇鏈�缁堢敤閫旇寖鍥淬��
- */
- public String resolveUsageScope(String sceneCode, Integer enabledFlag, String usageScope) {
- String normalized = normalizeUsageScope(usageScope);
- if (AiMcpConstants.USAGE_SCOPE_DISABLED.equals(normalized)) {
- return normalized;
- }
- if (enabledFlag != null && !Integer.valueOf(1).equals(enabledFlag)) {
- return AiMcpConstants.USAGE_SCOPE_DISABLED;
- }
- if (AiMcpConstants.USAGE_SCOPE_CHAT_AND_DIAGNOSE.equals(normalized)) {
- return normalized;
- }
- if (sceneCode == null || sceneCode.trim().isEmpty()) {
- return AiMcpConstants.USAGE_SCOPE_CHAT_AND_DIAGNOSE;
- }
- return AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY;
- }
-
- /**
- * 鏋勯�犲崟涓瓧娈电殑 schema 瀹氫箟銆�
- */
- private Map<String, Object> fieldSchema(String type, String description) {
- Map<String, Object> field = new LinkedHashMap<>();
- field.put("type", type);
- field.put("description", description);
- return field;
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpProtocolService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpProtocolService.java
deleted file mode 100644
index d5fd036..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpProtocolService.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package com.vincent.rsf.server.ai.service.mcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiMcpProtocolService {
-
- @Resource
- private AiMcpRegistryService aiMcpRegistryService;
- @Resource
- private ObjectMapper objectMapper;
- @Resource
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- /**
- * 澶勭悊 MCP 鍗忚鍏ュ彛璇锋眰锛屽吋瀹规壒閲忎笌鍗曟潯 JSON-RPC 璐熻浇銆�
- */
- public Object handle(Long tenantId, JsonNode body) {
- if (body != null && body.isArray()) {
- List<Object> responses = new ArrayList<>();
- for (JsonNode item : body) {
- Object response = handleSingle(tenantId, item);
- if (response != null) {
- responses.add(response);
- }
- }
- return responses;
- }
- return handleSingle(tenantId, body);
- }
-
- /**
- * 澶勭悊鍗曟潯 JSON-RPC MCP 璇锋眰銆�
- */
- public Object handleSingle(Long tenantId, JsonNode request) {
- if (request == null || request.isMissingNode() || request.isNull()) {
- return error(null, -32600, "invalid request");
- }
- JsonNode idNode = request.get("id");
- String method = request.path("method").asText("");
- try {
- if ("initialize".equals(method)) {
- return success(idNode, buildInitializeResult());
- }
- if ("notifications/initialized".equals(method)) {
- return idNode == null || idNode.isNull() ? null : success(idNode, new LinkedHashMap<String, Object>());
- }
- if ("ping".equals(method)) {
- return success(idNode, new LinkedHashMap<String, Object>());
- }
- if ("tools/list".equals(method)) {
- return success(idNode, buildToolsListResult(tenantId));
- }
- if ("tools/call".equals(method)) {
- return success(idNode, buildToolCallResult(tenantId, request.path("params")));
- }
- return error(idNode, -32601, "method not found");
- } catch (Exception e) {
- return error(idNode, -32000, e.getMessage());
- }
- }
-
- /**
- * 鏋勯�� initialize 鏂规硶鐨勬爣鍑嗚繑鍥炰綋銆�
- */
- private Map<String, Object> buildInitializeResult() {
- Map<String, Object> result = new LinkedHashMap<>();
- result.put("protocolVersion", AiMcpConstants.PROTOCOL_VERSION);
- Map<String, Object> capabilities = new LinkedHashMap<>();
- Map<String, Object> tools = new LinkedHashMap<>();
- tools.put("listChanged", false);
- capabilities.put("tools", tools);
- result.put("capabilities", capabilities);
- Map<String, Object> serverInfo = new LinkedHashMap<>();
- serverInfo.put("name", AiMcpConstants.SERVER_NAME);
- serverInfo.put("version", AiMcpConstants.SERVER_VERSION);
- result.put("serverInfo", serverInfo);
- return result;
- }
-
- /**
- * 鏋勯�� tools/list 鏂规硶杩斿洖浣撱��
- */
- private Map<String, Object> buildToolsListResult(Long tenantId) {
- List<Map<String, Object>> tools = new ArrayList<>();
- for (AiMcpToolDescriptor descriptor : aiMcpRegistryService.listTools(tenantId, null)) {
- if (descriptor == null || !Integer.valueOf(1).equals(descriptor.getEnabledFlag())) {
- continue;
- }
- tools.add(aiMcpPayloadMapper.toProtocolTool(descriptor));
- }
- Map<String, Object> result = new LinkedHashMap<>();
- result.put("tools", tools);
- return result;
- }
-
- /**
- * 鎵ц tools/call锛屽苟鎶婂唴閮ㄥ伐鍏风粨鏋滆浆鎹㈡垚 MCP 鍗忚杈撳嚭銆�
- */
- private Map<String, Object> buildToolCallResult(Long tenantId, JsonNode paramsNode) {
- String name = paramsNode.path("name").asText("");
- Map<String, Object> arguments = paramsNode.has("arguments") && !paramsNode.get("arguments").isNull()
- ? objectMapper.convertValue(paramsNode.get("arguments"), Map.class)
- : new LinkedHashMap<String, Object>();
- AiPromptContext context = new AiPromptContext()
- .setTenantId(tenantId)
- .setSceneCode(arguments.get("sceneCode") == null ? "system_diagnose" : String.valueOf(arguments.get("sceneCode")))
- .setQuestion(arguments.get("question") == null ? "璇锋墽琛屼竴娆CP宸ュ叿璋冪敤" : String.valueOf(arguments.get("question")));
- AiDiagnosticToolResult result = aiMcpRegistryService.executeTool(tenantId, name, context, arguments);
- Map<String, Object> payload = new LinkedHashMap<>();
- List<Map<String, Object>> content = new ArrayList<>();
- Map<String, Object> text = new LinkedHashMap<>();
- text.put("type", "text");
- text.put("text", result == null || result.getSummaryText() == null ? "" : result.getSummaryText());
- content.add(text);
- payload.put("content", content);
- payload.put("isError", result != null && "WARN".equalsIgnoreCase(result.getSeverity()));
- if (result != null && result.getRawMeta() != null && !result.getRawMeta().isEmpty()) {
- payload.put("structuredContent", result.getRawMeta());
- }
- return payload;
- }
-
- /**
- * 鐢熸垚 JSON-RPC 鎴愬姛鍝嶅簲銆�
- */
- public Map<String, Object> success(JsonNode idNode, Object result) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("jsonrpc", "2.0");
- payload.put("id", idNode == null || idNode.isNull() ? null : objectMapper.convertValue(idNode, Object.class));
- payload.put("result", result == null ? new LinkedHashMap<String, Object>() : result);
- return payload;
- }
-
- /**
- * 鐢熸垚 JSON-RPC 閿欒鍝嶅簲銆�
- */
- public Map<String, Object> error(JsonNode idNode, int code, String message) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("jsonrpc", "2.0");
- payload.put("id", idNode == null || idNode.isNull() ? null : objectMapper.convertValue(idNode, Object.class));
- Map<String, Object> error = new LinkedHashMap<>();
- error.put("code", code);
- error.put("message", message == null ? "unknown error" : message);
- payload.put("error", error);
- return payload;
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpRegistryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpRegistryService.java
deleted file mode 100644
index 87ea316..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpRegistryService.java
+++ /dev/null
@@ -1,567 +0,0 @@
-package com.vincent.rsf.server.ai.service.mcp;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.provider.AiDiagnosticDataProvider;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
-import com.vincent.rsf.server.system.service.AiMcpMountService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Service
-public class AiMcpRegistryService {
-
- private static final long EXTERNAL_TOOL_CACHE_TTL_MS = 30000L;
-
- private final List<AiDiagnosticDataProvider> providers;
- private final Map<String, CachedTools> externalToolCache = new ConcurrentHashMap<>();
-
- @Resource
- private AiMcpMountService aiMcpMountService;
- @Resource
- private AiDiagnosticToolConfigService aiDiagnosticToolConfigService;
- @Resource
- private AiMcpHttpClient aiMcpHttpClient;
- @Resource
- private AiMcpSseClient aiMcpSseClient;
- @Resource
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- public AiMcpRegistryService(List<AiDiagnosticDataProvider> providers) {
- this.providers = providers == null ? new ArrayList<>() : providers;
- }
-
- /**
- * 鏋氫妇绉熸埛涓嬪彲瑙佺殑 MCP 宸ュ叿鐩綍銆�
- * 鍖呮嫭鍐呴儴宸ュ叿鍜屾墍鏈夊惎鐢ㄤ腑鐨勫閮ㄦ寕杞藉伐鍏枫��
- */
- public List<AiMcpToolDescriptor> listTools(Long tenantId, Long mountId) {
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- List<AiMcpMount> mounts;
- if (mountId == null) {
- mounts = aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, tenantId)
- .eq(AiMcpMount::getEnabledFlag, 1)
- .eq(AiMcpMount::getStatus, 1)
- .orderByAsc(AiMcpMount::getMountCode, AiMcpMount::getId));
- } else {
- mounts = new ArrayList<>();
- AiMcpMount mount = aiMcpMountService.getTenantMount(tenantId, mountId);
- if (mount != null && Integer.valueOf(1).equals(mount.getEnabledFlag()) && Integer.valueOf(1).equals(mount.getStatus())) {
- mounts.add(mount);
- }
- }
- for (AiMcpMount mount : mounts) {
- try {
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
- output.addAll(buildInternalTools(tenantId, mount));
- } else if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
- output.addAll(loadCachedExternalTools(tenantId, mount));
- } else if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
- output.addAll(loadCachedExternalTools(tenantId, mount));
- }
- } catch (Exception ignore) {
- }
- }
- return output;
- }
-
- /**
- * 浠呰繑鍥炵郴缁熷唴缃伐鍏风洰褰曘��
- */
- public List<AiMcpToolDescriptor> listInternalTools(Long tenantId) {
- AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE);
- if (mount == null || !Integer.valueOf(1).equals(mount.getEnabledFlag()) || !Integer.valueOf(1).equals(mount.getStatus())) {
- return new ArrayList<>();
- }
- return buildInternalTools(tenantId, mount);
- }
-
- /**
- * 娴嬭瘯鎸囧畾鎸傝浇鐨勮繛閫氭�т笌宸ュ叿鍙戠幇鑳藉姏锛屽苟鎶婄粨鏋滃洖鍐欏埌鎸傝浇娴嬭瘯鐘舵�佸瓧娈点��
- */
- public Map<String, Object> testMount(Long tenantId, Long mountId) {
- AiMcpMount mount = aiMcpMountService.getTenantMount(tenantId, mountId);
- if (mount == null) {
- throw new IllegalArgumentException("MCP鎸傝浇涓嶅瓨鍦�");
- }
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("mountCode", mount.getMountCode());
- payload.put("transportType", mount.getTransportType());
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
- List<AiMcpToolDescriptor> tools = buildInternalTools(tenantId, mount);
- payload.put("success", true);
- payload.put("toolCount", tools.size());
- payload.put("tools", tools);
- updateTestState(mount, 1, "鍐呴儴宸ュ叿鎸傝浇姝e父", tools.size());
- return payload;
- }
- ExternalToolsResult testResult = loadExternalToolsWithTransport(mount);
- List<AiMcpToolDescriptor> tools = testResult.tools;
- payload.put("success", true);
- payload.put("toolCount", tools.size());
- payload.put("tools", tools);
- payload.put("resolvedTransportType", testResult.transportType);
- payload.put("recommendedTransportType", testResult.transportType);
- payload.put("message", buildExternalSuccessMessage(testResult.transportType, tools.size()));
- updateTestState(mount, 1, String.valueOf(payload.get("message")), tools.size());
- return payload;
- }
-
- /**
- * 浠モ�滈瑙堚�濇ā寮忔墽琛屼竴娆″伐鍏疯皟鐢紝渚夸簬鍚庡彴椤甸潰璋冭瘯宸ュ叿杩斿洖鍐呭銆�
- */
- public AiDiagnosticToolResult previewTool(Long tenantId, String mountCode, String toolCode, String sceneCode, String question) {
- AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, mountCode);
- if (mount == null) {
- throw new IllegalArgumentException("MCP鎸傝浇涓嶅瓨鍦�");
- }
- AiPromptContext context = new AiPromptContext()
- .setTenantId(tenantId)
- .setSceneCode(sceneCode == null || sceneCode.trim().isEmpty() ? AiSceneCode.SYSTEM_DIAGNOSE : sceneCode)
- .setQuestion(question == null || question.trim().isEmpty() ? "璇锋墽琛屼竴娆CP宸ュ叿棰勮" : question);
- if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())
- || AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
- return executeExternalTool(mount, toolCode, context, buildToolArguments(context, null));
- }
- return executeInternalTool(mountCode, toolCode, context);
- }
-
- /**
- * 鏍规嵁宸ュ叿鎻忚堪绗︽墽琛屼竴娆″伐鍏疯皟鐢ㄣ��
- */
- public AiDiagnosticToolResult executeTool(Long tenantId, AiMcpToolDescriptor descriptor, AiPromptContext context) {
- if (descriptor == null) {
- throw new IllegalArgumentException("MCP宸ュ叿涓嶅瓨鍦�");
- }
- if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(descriptor.getTransportType())
- || AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(descriptor.getTransportType())) {
- AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, descriptor.getMountCode());
- if (mount == null) {
- throw new IllegalArgumentException("MCP鎸傝浇涓嶅瓨鍦�");
- }
- return executeExternalTool(mount, descriptor.getToolCode(), context, buildToolArguments(context, descriptor));
- }
- return executeInternalTool(descriptor.getMountCode(), descriptor.getToolCode(), context);
- }
-
- /**
- * 鏍规嵁瀹屾暣 MCP 宸ュ叿鍚嶆墽琛屼竴娆″伐鍏疯皟鐢紝閫氬父渚涘崗璁眰鐩存帴浣跨敤銆�
- */
- public AiDiagnosticToolResult executeTool(Long tenantId, String mcpToolName, AiPromptContext context, Map<String, Object> arguments) {
- AiMcpToolDescriptor descriptor = findDescriptor(tenantId, mcpToolName);
- if (descriptor == null) {
- throw new IllegalArgumentException("MCP宸ュ叿涓嶅瓨鍦�");
- }
- if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(descriptor.getTransportType())
- || AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(descriptor.getTransportType())) {
- AiMcpMount mount = aiMcpMountService.getTenantMountByCode(tenantId, descriptor.getMountCode());
- if (mount == null) {
- throw new IllegalArgumentException("MCP鎸傝浇涓嶅瓨鍦�");
- }
- return executeExternalTool(mount, descriptor.getToolCode(), context, arguments);
- }
- return executeInternalTool(descriptor.getMountCode(), descriptor.getToolCode(), context);
- }
-
- /**
- * 纭繚绉熸埛瀛樺湪榛樿鏈湴 MCP 鎸傝浇銆�
- */
- public void ensureDefaultMount(Long tenantId, Long userId) {
- if (aiMcpMountService.getTenantMountByCode(tenantId, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE) != null) {
- return;
- }
- Date now = new Date();
- AiMcpMount mount = new AiMcpMount()
- .setUuid(String.valueOf(System.currentTimeMillis()))
- .setName(AiMcpConstants.DEFAULT_LOCAL_MOUNT_NAME)
- .setMountCode(AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE)
- .setTransportType(AiMcpConstants.TRANSPORT_INTERNAL)
- .setUrl("/ai/mcp")
- .setEnabledFlag(1)
- .setTimeoutMs(10000)
- .setStatus(1)
- .setDeleted(0)
- .setTenantId(tenantId)
- .setCreateBy(userId)
- .setCreateTime(now)
- .setUpdateBy(userId)
- .setUpdateTime(now)
- .setMemo("榛樿鎸傝浇褰撳墠 WMS AI 鍐呯疆宸ュ叿闆嗗悎");
- aiMcpMountService.save(mount);
- }
-
- /**
- * 涓哄唴閮ㄥ伐鍏风粨鏋滆ˉ榻愭寕杞界紪鐮佸拰鏍囧噯 MCP 宸ュ叿鍚嶃��
- */
- public AiDiagnosticToolResult decorateResult(AiDiagnosticToolResult result) {
- if (result == null) {
- return null;
- }
- return decorateResult(result, AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE, findProvider(result.getToolCode()));
- }
-
- private AiDiagnosticToolResult decorateResult(AiDiagnosticToolResult result, String mountCode, AiDiagnosticDataProvider provider) {
- String actualMountCode = mountCode == null || mountCode.trim().isEmpty() ? AiMcpConstants.DEFAULT_LOCAL_MOUNT_CODE : mountCode;
- result.setMountCode(actualMountCode);
- result.setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(actualMountCode, result.getToolCode()));
- if ((result.getToolName() == null || result.getToolName().trim().isEmpty()) && provider != null) {
- result.setToolName(provider.getToolName());
- }
- return result;
- }
-
- /**
- * 鎸夊綋鍓嶇鎴风殑宸ュ叿閰嶇疆鐢熸垚鍐呴儴 MCP 宸ュ叿鐩綍銆�
- */
- private List<AiMcpToolDescriptor> buildInternalTools(Long tenantId, AiMcpMount mount) {
- Map<String, AiDiagnosticToolConfig> configMap = buildInternalConfigMap(tenantId);
- List<AiDiagnosticDataProvider> sortedProviders = new ArrayList<>(providers);
- sortedProviders.sort(Comparator.comparingInt(AiDiagnosticDataProvider::getOrder));
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- for (AiDiagnosticDataProvider provider : sortedProviders) {
- AiDiagnosticToolConfig config = configMap.get(provider.getToolCode());
- String usageScope = aiMcpPayloadMapper.resolveUsageScope(
- config == null ? null : config.getSceneCode(),
- config == null ? 1 : config.getEnabledFlag(),
- config == null ? null : config.getUsageScope()
- );
- output.add(new AiMcpToolDescriptor()
- .setMountCode(mount.getMountCode())
- .setMountName(mount.getName())
- .setToolCode(provider.getToolCode())
- .setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mount.getMountCode(), provider.getToolCode()))
- .setToolName(provider.getToolName())
- .setSceneCode(aiMcpPayloadMapper.resolveSceneCode(usageScope))
- .setDescription(provider.getDefaultToolPrompt())
- .setEnabledFlag(AiMcpConstants.USAGE_SCOPE_DISABLED.equals(usageScope) ? 0 : (config == null ? 1 : config.getEnabledFlag()))
- .setPriority(config == null ? provider.getOrder() : config.getPriority())
- .setToolPrompt(config == null ? provider.getDefaultToolPrompt() : config.getToolPrompt())
- .setUsageScope(usageScope)
- .setTransportType(mount.getTransportType())
- .setInputSchema(aiMcpPayloadMapper.defaultInputSchema(true)));
- }
- return output;
- }
-
- /**
- * 涓哄唴缃伐鍏风洰褰曢�夋嫨涓�鏉℃渶鍚堥�傜殑鏈夋晥閰嶇疆銆�
- * 鐩綍灞傚彧鏈変竴鏉″伐鍏锋弿杩帮紝鍥犳杩欓噷浼樺厛淇濈暀鍚敤涓旂姸鎬佹甯哥殑閰嶇疆锛�
- * 骞朵紭鍏堥�夋嫨鈥滆亰澶╀笌璇婃柇閮藉彲鐢ㄢ�濈殑閰嶇疆锛屽啀閫�鍥炲埌鈥滀粎璇婃柇鈥濋厤缃��
- */
- private Map<String, AiDiagnosticToolConfig> buildInternalConfigMap(Long tenantId) {
- Map<String, AiDiagnosticToolConfig> configMap = new LinkedHashMap<>();
- for (AiDiagnosticToolConfig item : aiDiagnosticToolConfigService.listTenantConfigs(tenantId)) {
- if (item == null || !Integer.valueOf(1).equals(item.getStatus())) {
- continue;
- }
- AiDiagnosticToolConfig existed = configMap.get(item.getToolCode());
- if (existed == null || preferInternalConfig(item, existed)) {
- configMap.put(item.getToolCode(), item);
- }
- }
- return configMap;
- }
-
- /**
- * 鍐呯疆宸ュ叿鐩綍浼樺厛灞曠ず鐢ㄩ�旀洿骞跨殑閰嶇疆锛屽叾娆℃瘮杈冧紭鍏堢骇銆�
- */
- private boolean preferInternalConfig(AiDiagnosticToolConfig candidate, AiDiagnosticToolConfig current) {
- String candidateUsageScope = aiMcpPayloadMapper.resolveUsageScope(
- candidate.getSceneCode(),
- candidate.getEnabledFlag(),
- candidate.getUsageScope()
- );
- String currentUsageScope = aiMcpPayloadMapper.resolveUsageScope(
- current.getSceneCode(),
- current.getEnabledFlag(),
- current.getUsageScope()
- );
- int candidateRank = usageScopeRank(candidateUsageScope);
- int currentRank = usageScopeRank(currentUsageScope);
- if (candidateRank != currentRank) {
- return candidateRank < currentRank;
- }
- Integer candidatePriority = candidate.getPriority() == null ? Integer.MAX_VALUE : candidate.getPriority();
- Integer currentPriority = current.getPriority() == null ? Integer.MAX_VALUE : current.getPriority();
- return candidatePriority < currentPriority;
- }
-
- private int usageScopeRank(String usageScope) {
- if (AiMcpConstants.USAGE_SCOPE_CHAT_AND_DIAGNOSE.equals(usageScope)) {
- return 0;
- }
- if (AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY.equals(usageScope)) {
- return 1;
- }
- return 2;
- }
-
- /**
- * 鎸夊伐鍏风紪鐮佸畾浣嶅唴閮ㄥ伐鍏峰疄鐜般��
- */
- private AiDiagnosticDataProvider findProvider(String toolCode) {
- for (AiDiagnosticDataProvider provider : providers) {
- if (provider.getToolCode().equals(toolCode)) {
- return provider;
- }
- }
- return null;
- }
-
- /**
- * 鏇存柊鎸傝浇娴嬭瘯缁撴灉鍜屽伐鍏锋暟閲忥紝骞舵竻鐞嗙紦瀛樸��
- */
- private void updateTestState(AiMcpMount mount, Integer result, String message, Integer toolCount) {
- mount.setLastTestResult(result);
- mount.setLastTestMessage(message);
- mount.setLastToolCount(toolCount);
- mount.setLastTestTime(new Date());
- mount.setUpdateTime(new Date());
- aiMcpMountService.updateById(mount);
- if (mount.getId() != null) {
- externalToolCache.remove(buildCacheKey(mount.getTenantId(), mount));
- }
- }
-
- /**
- * 鎵ц鍐呴儴 MCP 宸ュ叿銆�
- */
- private AiDiagnosticToolResult executeInternalTool(String mountCode, String toolCode, AiPromptContext context) {
- AiDiagnosticDataProvider provider = findProvider(toolCode);
- if (provider == null) {
- throw new IllegalArgumentException("MCP宸ュ叿涓嶅瓨鍦�");
- }
- AiDiagnosticToolResult result = provider.buildDiagnosticData(context);
- if (result == null) {
- return new AiDiagnosticToolResult()
- .setToolCode(toolCode)
- .setMountCode(mountCode)
- .setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mountCode, toolCode))
- .setToolName(provider.getToolName())
- .setSeverity("WARN")
- .setSummaryText("宸ュ叿娌℃湁杩斿洖缁撴灉");
- }
- return decorateResult(result, mountCode, provider);
- }
-
- /**
- * 鎵ц澶栭儴 MCP 宸ュ叿锛屽苟琛ラ綈鏈湴绯荤粺鎵�闇�鐨勭粺涓�瀛楁銆�
- */
- private AiDiagnosticToolResult executeExternalTool(AiMcpMount mount, String toolCode,
- AiPromptContext context, Map<String, Object> arguments) {
- ExternalToolCallResult callResult = callExternalTool(mount, toolCode, arguments);
- AiDiagnosticToolResult result = callResult.result;
- return result
- .setToolCode(toolCode)
- .setMountCode(mount.getMountCode())
- .setMcpToolName(aiMcpPayloadMapper.buildMcpToolName(mount.getMountCode(), toolCode))
- .setToolName(result.getToolName() == null || result.getToolName().trim().isEmpty() ? toolCode : result.getToolName())
- .setRawMeta(result.getRawMeta() == null ? new LinkedHashMap<String, Object>() : result.getRawMeta());
- }
-
- /**
- * 鐪熸鍔犺浇澶栭儴宸ュ叿鐩綍锛屼笉甯︾紦瀛樸��
- */
- private List<AiMcpToolDescriptor> loadExternalTools(AiMcpMount mount) {
- return loadExternalToolsWithTransport(mount).tools;
- }
-
- /**
- * 甯︾煭鏈熺紦瀛樺湴鍔犺浇澶栭儴宸ュ叿鐩綍锛岄伩鍏嶅悓涓�鎸傝浇鍦ㄧ煭鏃堕棿鍐呴噸澶嶆彙鎵嬨��
- */
- private List<AiMcpToolDescriptor> loadCachedExternalTools(Long tenantId, AiMcpMount mount) {
- String cacheKey = buildCacheKey(tenantId, mount);
- CachedTools cached = externalToolCache.get(cacheKey);
- long now = System.currentTimeMillis();
- if (cached != null && cached.expireAt > now) {
- return new ArrayList<>(cached.tools);
- }
- List<AiMcpToolDescriptor> tools = loadExternalTools(mount);
- externalToolCache.put(cacheKey, new CachedTools(new ArrayList<>(tools), now + EXTERNAL_TOOL_CACHE_TTL_MS));
- return tools;
- }
-
- /**
- * 鐢熸垚澶栭儴宸ュ叿鐩綍缂撳瓨 key銆�
- */
- private String buildCacheKey(Long tenantId, AiMcpMount mount) {
- return String.valueOf(tenantId) + ":" + mount.getId() + ":" + (mount.getUpdateTime() == null ? 0L : mount.getUpdateTime().getTime());
- }
-
- /**
- * 鎸夊畬鏁� MCP 宸ュ叿鍚嶅弽鏌ュ伐鍏锋弿杩扮銆�
- */
- private AiMcpToolDescriptor findDescriptor(Long tenantId, String mcpToolName) {
- for (AiMcpToolDescriptor descriptor : listTools(tenantId, null)) {
- if (descriptor != null && mcpToolName.equals(descriptor.getMcpToolName())) {
- return descriptor;
- }
- }
- return null;
- }
-
- /**
- * 鏍规嵁涓婁笅鏂囧拰宸ュ叿淇℃伅鏋勯�犺繙绋嬭皟鐢ㄥ弬鏁般��
- */
- private Map<String, Object> buildToolArguments(AiPromptContext context, AiMcpToolDescriptor descriptor) {
- Map<String, Object> arguments = new LinkedHashMap<>();
- if (context != null) {
- arguments.put("tenantId", context.getTenantId());
- arguments.put("sceneCode", context.getSceneCode());
- arguments.put("question", context.getQuestion());
- arguments.put("sessionId", context.getSessionId());
- }
- if (descriptor != null) {
- arguments.put("toolName", descriptor.getToolName());
- arguments.put("mountCode", descriptor.getMountCode());
- }
- return arguments;
- }
-
- /**
- * 鐢熸垚澶栭儴鎸傝浇娴嬭瘯鎴愬姛鏂囨銆�
- */
- private String buildExternalSuccessMessage(String transportType, int toolCount) {
- String prefix = AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(transportType)
- ? "杩滅▼SSE MCP宸ュ叿鍔犺浇鎴愬姛"
- : "杩滅▼Streamable HTTP MCP宸ュ叿鍔犺浇鎴愬姛";
- return prefix + "锛屽彂鐜� " + toolCount + " 涓伐鍏�";
- }
-
- /**
- * 鏍规嵁鎸傝浇閰嶇疆閫夋嫨 HTTP / SSE 瀹㈡埛绔幓鍔犺浇澶栭儴宸ュ叿鐩綍銆�
- * AUTO 妯″紡浼氫緷娆″皾璇� Streamable HTTP 鍜� SSE銆�
- */
- private ExternalToolsResult loadExternalToolsWithTransport(AiMcpMount mount) {
- if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
- return new ExternalToolsResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.listTools(mount));
- }
- if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
- return new ExternalToolsResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.listTools(mount));
- }
- List<String> errors = new ArrayList<>();
- try {
- return new ExternalToolsResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.listTools(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_HTTP)));
- } catch (Exception e) {
- errors.add("HTTP: " + e.getMessage());
- }
- try {
- return new ExternalToolsResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.listTools(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_SSE)));
- } catch (Exception e) {
- errors.add("SSE: " + e.getMessage());
- }
- throw new IllegalStateException(String.join("锛�", errors));
- }
-
- /**
- * 鏍规嵁鎸傝浇閰嶇疆閫夋嫨 HTTP / SSE 瀹㈡埛绔墽琛岃繙绋嬪伐鍏枫��
- */
- private ExternalToolCallResult callExternalTool(AiMcpMount mount, String toolCode, Map<String, Object> arguments) {
- if (AiMcpConstants.TRANSPORT_SSE.equalsIgnoreCase(mount.getTransportType())) {
- return new ExternalToolCallResult(AiMcpConstants.TRANSPORT_SSE, aiMcpSseClient.callTool(mount, toolCode, arguments));
- }
- if (AiMcpConstants.TRANSPORT_HTTP.equalsIgnoreCase(mount.getTransportType())) {
- return new ExternalToolCallResult(AiMcpConstants.TRANSPORT_HTTP, aiMcpHttpClient.callTool(mount, toolCode, arguments));
- }
- List<String> errors = new ArrayList<>();
- try {
- return new ExternalToolCallResult(
- AiMcpConstants.TRANSPORT_HTTP,
- aiMcpHttpClient.callTool(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_HTTP), toolCode, arguments)
- );
- } catch (Exception e) {
- errors.add("HTTP: " + e.getMessage());
- }
- try {
- return new ExternalToolCallResult(
- AiMcpConstants.TRANSPORT_SSE,
- aiMcpSseClient.callTool(copyMountWithTransport(mount, AiMcpConstants.TRANSPORT_SSE), toolCode, arguments)
- );
- } catch (Exception e) {
- errors.add("SSE: " + e.getMessage());
- }
- throw new IllegalStateException(String.join("锛�", errors));
- }
-
- /**
- * 澶嶅埗涓�浠芥寕杞藉璞★紝骞惰鐩栨寚瀹氫紶杈撳崗璁紝渚� AUTO 鎺㈡祴娴佺▼浣跨敤銆�
- */
- private AiMcpMount copyMountWithTransport(AiMcpMount mount, String transportType) {
- return new AiMcpMount()
- .setId(mount.getId())
- .setUuid(mount.getUuid())
- .setName(mount.getName())
- .setMountCode(mount.getMountCode())
- .setTransportType(transportType)
- .setUrl(mount.getUrl())
- .setAuthType(mount.getAuthType())
- .setAuthValue(mount.getAuthValue())
- .setUsageScope(mount.getUsageScope())
- .setEnabledFlag(mount.getEnabledFlag())
- .setTimeoutMs(mount.getTimeoutMs())
- .setStatus(mount.getStatus())
- .setTenantId(mount.getTenantId())
- .setCreateBy(mount.getCreateBy())
- .setCreateTime(mount.getCreateTime())
- .setUpdateBy(mount.getUpdateBy())
- .setUpdateTime(mount.getUpdateTime())
- .setMemo(mount.getMemo());
- }
-
- private static class CachedTools {
- private final List<AiMcpToolDescriptor> tools;
- private final long expireAt;
-
- /**
- * 淇濆瓨涓�娆″閮ㄥ伐鍏风洰褰曠紦瀛樺唴瀹瑰強鍏跺け鏁堟椂闂淬��
- */
- private CachedTools(List<AiMcpToolDescriptor> tools, long expireAt) {
- this.tools = tools;
- this.expireAt = expireAt;
- }
- }
-
- private static class ExternalToolsResult {
- private final String transportType;
- private final List<AiMcpToolDescriptor> tools;
-
- /**
- * 淇濆瓨涓�娆″閮ㄥ伐鍏风洰褰曞姞杞界粨鏋滃強瀹為檯鍛戒腑鐨勪紶杈撳崗璁��
- */
- private ExternalToolsResult(String transportType, List<AiMcpToolDescriptor> tools) {
- this.transportType = transportType;
- this.tools = tools;
- }
- }
-
- private static class ExternalToolCallResult {
- private final String transportType;
- private final AiDiagnosticToolResult result;
-
- /**
- * 淇濆瓨涓�娆″閮ㄥ伐鍏疯皟鐢ㄧ粨鏋滃強瀹為檯鍛戒腑鐨勪紶杈撳崗璁��
- */
- private ExternalToolCallResult(String transportType, AiDiagnosticToolResult result) {
- this.transportType = transportType;
- this.result = result;
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpSseClient.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpSseClient.java
deleted file mode 100644
index 24c9d8a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/mcp/AiMcpSseClient.java
+++ /dev/null
@@ -1,434 +0,0 @@
-package com.vincent.rsf.server.ai.service.mcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiMcpToolDescriptor;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import jakarta.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-@Component
-public class AiMcpSseClient {
-
- private static final Logger logger = LoggerFactory.getLogger(AiMcpSseClient.class);
-
- @Resource
- private ObjectMapper objectMapper;
- @Resource
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- /**
- * 閫氳繃 SSE + message endpoint 鍗忚鍔犺浇杩滅▼ MCP 宸ュ叿鐩綍銆�
- */
- public List<AiMcpToolDescriptor> listTools(AiMcpMount mount) {
- try (SseSession session = openSession(mount)) {
- logger.info("AI MCP SSE listTools start: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- session.initialize();
- JsonNode result = session.request("tools/list", new LinkedHashMap<String, Object>());
- List<AiMcpToolDescriptor> output = new ArrayList<>();
- JsonNode toolsNode = result.path("tools");
- if (!toolsNode.isArray()) {
- logger.warn("AI MCP SSE listTools no tools array: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- return output;
- }
- for (JsonNode item : toolsNode) {
- AiMcpToolDescriptor descriptor = aiMcpPayloadMapper.toExternalToolDescriptor(mount, item);
- if (descriptor != null) {
- output.add(descriptor);
- }
- }
- logger.info("AI MCP SSE listTools success: mountCode={}, url={}, toolCount={}",
- mount.getMountCode(), mount.getUrl(), output.size());
- return output;
- } catch (Exception e) {
- logger.warn("AI MCP SSE listTools failed: mountCode={}, url={}, message={}",
- mount.getMountCode(), mount.getUrl(), e.getMessage());
- throw new IllegalStateException("SSE MCP宸ュ叿鍔犺浇澶辫触: " + e.getMessage(), e);
- }
- }
-
- /**
- * 閫氳繃 SSE + message endpoint 鍗忚鎵ц杩滅▼ MCP 宸ュ叿銆�
- */
- public AiDiagnosticToolResult callTool(AiMcpMount mount, String toolName, Map<String, Object> arguments) {
- try (SseSession session = openSession(mount)) {
- logger.info("AI MCP SSE callTool start: mountCode={}, url={}, toolName={}",
- mount.getMountCode(), mount.getUrl(), toolName);
- session.initialize();
- Map<String, Object> params = new LinkedHashMap<>();
- params.put("name", toolName);
- params.put("arguments", arguments == null ? new LinkedHashMap<String, Object>() : arguments);
- JsonNode result = session.request("tools/call", params);
- AiDiagnosticToolResult toolResult = aiMcpPayloadMapper.toExternalToolResult(mount, toolName, result);
- logger.info("AI MCP SSE callTool success: mountCode={}, url={}, toolName={}, isError={}, summaryLength={}",
- mount.getMountCode(), mount.getUrl(), toolName,
- "WARN".equalsIgnoreCase(toolResult.getSeverity()),
- toolResult.getSummaryText() == null ? 0 : toolResult.getSummaryText().length());
- return toolResult;
- } catch (Exception e) {
- logger.warn("AI MCP SSE callTool failed: mountCode={}, url={}, toolName={}, message={}",
- mount.getMountCode(), mount.getUrl(), toolName, e.getMessage());
- throw new IllegalStateException("SSE MCP宸ュ叿璋冪敤澶辫触: " + e.getMessage(), e);
- }
- }
-
- /**
- * 鎵撳紑杩滅▼ SSE 娴佸苟鍒涘缓浼氳瘽鍖呰瀵硅薄銆�
- */
- private SseSession openSession(AiMcpMount mount) throws Exception {
- logger.info("AI MCP SSE opening stream: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- int timeoutMs = mount.getTimeoutMs() == null || mount.getTimeoutMs() <= 0 ? 10000 : mount.getTimeoutMs();
- HttpURLConnection connection = (HttpURLConnection) new URL(mount.getUrl()).openConnection();
- connection.setRequestMethod("GET");
- connection.setDoInput(true);
- connection.setConnectTimeout(timeoutMs);
- connection.setReadTimeout(timeoutMs);
- connection.setRequestProperty("Accept", "text/event-stream");
- applyAuthHeaders(connection, mount);
- InputStream inputStream = connection.getInputStream();
- logger.info("AI MCP SSE stream connected: mountCode={}, url={}, responseCode={}",
- mount.getMountCode(), mount.getUrl(), connection.getResponseCode());
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
- SseSession session = new SseSession(mount, connection, reader);
- session.start();
- return session;
- }
-
- private class SseSession implements AutoCloseable {
- private final AiMcpMount mount;
- private final HttpURLConnection connection;
- private final BufferedReader reader;
- private final BlockingQueue<SseEvent> events = new LinkedBlockingQueue<>();
- private volatile boolean closed;
- private Thread worker;
- private String messageEndpoint;
-
- private SseSession(AiMcpMount mount, HttpURLConnection connection, BufferedReader reader) {
- this.mount = mount;
- this.connection = connection;
- this.reader = reader;
- }
-
- /**
- * 鍚姩鍚庡彴璇诲彇绾跨▼锛屽苟绛夊緟杩滅▼鏈嶅姟杩斿洖 endpoint 浜嬩欢銆�
- */
- private void start() throws Exception {
- worker = new Thread(this::readLoop, "ai-mcp-sse-client-" + mount.getMountCode());
- worker.setDaemon(true);
- worker.start();
- logger.info("AI MCP SSE waiting endpoint event: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- SseEvent endpointEvent = waitEvent("endpoint");
- messageEndpoint = resolveEndpoint(endpointEvent == null ? null : endpointEvent.getData());
- logger.info("AI MCP SSE endpoint event received: mountCode={}, url={}, rawEndpoint={}, resolvedEndpoint={}",
- mount.getMountCode(), mount.getUrl(),
- endpointEvent == null ? null : endpointEvent.getData(),
- messageEndpoint);
- if (messageEndpoint == null || messageEndpoint.trim().isEmpty()) {
- throw new IllegalStateException("SSE MCP鏈繑鍥� message endpoint");
- }
- }
-
- /**
- * 瀹屾垚涓�娆� MCP initialize 鎻℃墜銆�
- */
- private void initialize() throws Exception {
- logger.info("AI MCP SSE initialize start: mountCode={}, url={}, messageEndpoint={}",
- mount.getMountCode(), mount.getUrl(), messageEndpoint);
- Map<String, Object> params = new LinkedHashMap<>();
- params.put("protocolVersion", AiMcpConstants.PROTOCOL_VERSION);
- params.put("capabilities", new LinkedHashMap<String, Object>());
- Map<String, Object> clientInfo = new LinkedHashMap<>();
- clientInfo.put("name", "rsf-server");
- clientInfo.put("version", AiMcpConstants.SERVER_VERSION);
- params.put("clientInfo", clientInfo);
- request("initialize", params);
- notifyInitialized();
- logger.info("AI MCP SSE initialize success: mountCode={}, url={}, messageEndpoint={}",
- mount.getMountCode(), mount.getUrl(), messageEndpoint);
- }
-
- /**
- * 鍚戣繙绋� MCP 鍙戦�� initialized 閫氱煡銆�
- */
- private void notifyInitialized() throws Exception {
- Map<String, Object> body = new LinkedHashMap<>();
- body.put("jsonrpc", "2.0");
- body.put("method", "notifications/initialized");
- body.put("params", new LinkedHashMap<String, Object>());
- postMessage(body, false);
- logger.info("AI MCP SSE initialized notification sent: mountCode={}, messageEndpoint={}",
- mount.getMountCode(), messageEndpoint);
- }
-
- /**
- * 閫氳繃 message endpoint 鍙戦�佷竴娆� JSON-RPC 璇锋眰锛屽苟绛夊緟瀵瑰簲 message 浜嬩欢鍝嶅簲銆�
- */
- private JsonNode request(String method, Object params) throws Exception {
- String id = UUID.randomUUID().toString().replace("-", "");
- Map<String, Object> body = new LinkedHashMap<>();
- body.put("jsonrpc", "2.0");
- body.put("id", id);
- body.put("method", method);
- body.put("params", params == null ? new LinkedHashMap<String, Object>() : params);
- logger.info("AI MCP SSE request send: mountCode={}, method={}, requestId={}, messageEndpoint={}",
- mount.getMountCode(), method, id, messageEndpoint);
- postMessage(body, true);
- SseEvent response = waitEvent("message");
- if (response == null || response.getData() == null || response.getData().trim().isEmpty()) {
- throw new IllegalStateException("SSE MCP鏈繑鍥炲搷搴旀秷鎭�");
- }
- logger.info("AI MCP SSE response received: mountCode={}, method={}, requestId={}, dataLength={}",
- mount.getMountCode(), method, id, response.getData().length());
- JsonNode root = objectMapper.readTree(response.getData());
- if (!id.equals(root.path("id").asText(""))) {
- logger.warn("AI MCP SSE response id mismatch: mountCode={}, method={}, requestId={}, responseId={}",
- mount.getMountCode(), method, id, root.path("id").asText(""));
- throw new IllegalStateException("SSE MCP鍝嶅簲ID涓嶅尮閰�");
- }
- if (root.has("error") && !root.get("error").isNull()) {
- logger.warn("AI MCP SSE response error: mountCode={}, method={}, requestId={}, message={}",
- mount.getMountCode(), method, id, root.path("error").path("message").asText(""));
- throw new IllegalStateException(root.path("error").path("message").asText("MCP璋冪敤澶辫触"));
- }
- return root.path("result");
- }
-
- /**
- * 鍚� message endpoint 鎻愪氦涓�鏉� HTTP POST 娑堟伅銆�
- */
- private void postMessage(Map<String, Object> body, boolean expectSuccess) throws Exception {
- HttpURLConnection post = null;
- try {
- post = (HttpURLConnection) new URL(messageEndpoint).openConnection();
- post.setRequestMethod("POST");
- post.setDoOutput(true);
- post.setConnectTimeout(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- post.setReadTimeout(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- post.setRequestProperty("Content-Type", "application/json");
- post.setRequestProperty("Accept", "application/json");
- applyAuthHeaders(post, mount);
- logger.info("AI MCP SSE post message: mountCode={}, endpoint={}, method={}",
- mount.getMountCode(), messageEndpoint, body.get("method"));
- try (OutputStream outputStream = post.getOutputStream()) {
- outputStream.write(objectMapper.writeValueAsBytes(body));
- outputStream.flush();
- }
- int statusCode = post.getResponseCode();
- logger.info("AI MCP SSE post response: mountCode={}, endpoint={}, method={}, statusCode={}",
- mount.getMountCode(), messageEndpoint, body.get("method"), statusCode);
- if (expectSuccess && statusCode >= 400) {
- throw new IllegalStateException("SSE MCP娑堟伅鎻愪氦澶辫触锛岀姸鎬佺爜=" + statusCode);
- }
- } finally {
- if (post != null) {
- post.disconnect();
- }
- }
- }
-
- /**
- * 鍦ㄩ檺瀹氭椂闂村唴绛夊緟鏌愮被 SSE 浜嬩欢銆�
- */
- private SseEvent waitEvent(String targetName) throws Exception {
- long timeoutMs = mount.getTimeoutMs() == null ? 10000L : mount.getTimeoutMs().longValue();
- long deadline = System.currentTimeMillis() + timeoutMs;
- while (System.currentTimeMillis() < deadline) {
- long remain = deadline - System.currentTimeMillis();
- SseEvent event = events.poll(remain <= 0 ? 1L : remain, TimeUnit.MILLISECONDS);
- if (event == null) {
- continue;
- }
- logger.info("AI MCP SSE event dequeued: mountCode={}, target={}, actual={}, dataLength={}",
- mount.getMountCode(), targetName, event.getName(), event.getData() == null ? 0 : event.getData().length());
- if ("error".equals(event.getName())) {
- throw new IllegalStateException("SSE MCP浜嬩欢璇诲彇澶辫触: " + event.getData());
- }
- if (targetName.equals(event.getName())) {
- return event;
- }
- }
- logger.warn("AI MCP SSE wait event timeout: mountCode={}, target={}, timeoutMs={}",
- mount.getMountCode(), targetName, timeoutMs);
- throw new IllegalStateException("绛夊緟SSE浜嬩欢瓒呮椂: " + targetName);
- }
-
- /**
- * 鍚庡彴鎸佺画璇诲彇 SSE 娴侊紝骞舵妸浜嬩欢杞彂鍒伴槦鍒椾緵涓荤嚎绋嬫秷璐广��
- */
- private void readLoop() {
- String eventName = "message";
- StringBuilder dataBuilder = new StringBuilder();
- try {
- String line;
- while (!closed && (line = reader.readLine()) != null) {
- if (line.startsWith("event:")) {
- eventName = line.substring(6).trim();
- continue;
- }
- if (line.startsWith("data:")) {
- dataBuilder.append(line.substring(5).trim()).append('\n');
- continue;
- }
- if (line.trim().isEmpty()) {
- if (dataBuilder.length() > 0) {
- logger.info("AI MCP SSE raw event read: mountCode={}, event={}, dataLength={}",
- mount.getMountCode(), eventName, dataBuilder.length());
- events.offer(new SseEvent(eventName, dataBuilder.toString().trim()));
- }
- eventName = "message";
- dataBuilder.setLength(0);
- }
- }
- } catch (Exception e) {
- if (!closed) {
- logger.warn("AI MCP SSE read loop failed: mountCode={}, url={}, message={}",
- mount.getMountCode(), mount.getUrl(), e.getMessage());
- events.offer(new SseEvent("error", e.getMessage()));
- }
- } finally {
- try {
- reader.close();
- } catch (Exception ignore) {
- }
- }
- }
-
- /**
- * 瑙f瀽杩滅▼杩斿洖鐨� message endpoint銆�
- * 褰撳鏂归敊璇繑鍥炲洖鐜湴鍧�鏃讹紝浼氶噸鍐欐垚褰撳墠鎸傝浇 URL 鐨勪富鏈哄湴鍧�銆�
- */
- private String resolveEndpoint(String rawEndpoint) {
- if (rawEndpoint == null || rawEndpoint.trim().isEmpty()) {
- return null;
- }
- try {
- URI baseUri = new URI(mount.getUrl());
- URI endpointUri = baseUri.resolve(rawEndpoint.trim());
- String host = endpointUri.getHost();
- if (isLoopbackHost(host) && !sameHost(host, baseUri.getHost())) {
- endpointUri = new URI(
- baseUri.getScheme(),
- endpointUri.getUserInfo(),
- baseUri.getHost(),
- endpointUri.getPort() > 0 ? endpointUri.getPort() : baseUri.getPort(),
- endpointUri.getPath(),
- endpointUri.getQuery(),
- endpointUri.getFragment());
- logger.info("AI MCP SSE endpoint rewritten: mountCode={}, rawEndpoint={}, rewrittenEndpoint={}",
- mount.getMountCode(), rawEndpoint, endpointUri);
- }
- return endpointUri.toString();
- } catch (Exception e) {
- return rawEndpoint.trim();
- }
- }
-
- /**
- * 鍒ゆ柇鍦板潃鏄惁涓烘湰鏈哄洖鐜湴鍧�銆�
- */
- private boolean isLoopbackHost(String host) {
- return "127.0.0.1".equals(host) || "localhost".equalsIgnoreCase(host) || "::1".equals(host);
- }
-
- /**
- * 鍒ゆ柇涓や釜 host 鏄惁绛変环銆�
- */
- private boolean sameHost(String left, String right) {
- if (left == null || right == null) {
- return false;
- }
- return left.equalsIgnoreCase(right);
- }
-
- /**
- * 鍏抽棴 SSE 浼氳瘽锛屽苟寮傛娓呯悊搴曞眰杩炴帴璧勬簮銆�
- */
- @Override
- public void close() throws Exception {
- closed = true;
- logger.info("AI MCP SSE closing session: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- if (worker != null) {
- worker.interrupt();
- }
- Thread cleanup = new Thread(() -> {
- try {
- connection.disconnect();
- } catch (Exception ignore) {
- }
- try {
- reader.close();
- } catch (Exception ignore) {
- }
- logger.info("AI MCP SSE cleanup finished: mountCode={}, url={}", mount.getMountCode(), mount.getUrl());
- }, "ai-mcp-sse-cleanup-" + mount.getMountCode());
- cleanup.setDaemon(true);
- cleanup.start();
- }
- }
-
- /**
- * 鎸夋寕杞介厤缃啓鍏ラ壌鏉冭姹傚ご銆�
- */
- private void applyAuthHeaders(HttpURLConnection connection, AiMcpMount mount) {
- if (mount == null || mount.getAuthType() == null || mount.getAuthValue() == null || mount.getAuthValue().trim().isEmpty()) {
- return;
- }
- String authType = mount.getAuthType().trim().toUpperCase();
- if (AiMcpConstants.AUTH_TYPE_BEARER.equals(authType)) {
- connection.setRequestProperty("Authorization", "Bearer " + mount.getAuthValue().trim());
- } else if (AiMcpConstants.AUTH_TYPE_API_KEY.equals(authType)) {
- connection.setRequestProperty("X-API-Key", mount.getAuthValue().trim());
- }
- }
-
- private static class SseEvent {
- private final String name;
- private final String data;
-
- /**
- * 灏佽涓�鏉� SSE 浜嬩欢銆�
- */
- private SseEvent(String name, String data) {
- this.name = name;
- this.data = data;
- }
-
- /**
- * 杩斿洖浜嬩欢鍚嶃��
- */
- public String getName() {
- return name;
- }
-
- /**
- * 杩斿洖浜嬩欢鏁版嵁鏂囨湰銆�
- */
- public String getData() {
- return data;
- }
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiApiFailureSummaryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiApiFailureSummaryService.java
deleted file mode 100644
index 8e9ecc9..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiApiFailureSummaryService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-import com.vincent.rsf.server.system.service.AiCallLogService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiApiFailureSummaryService implements AiDiagnosticDataProvider {
-
- private static final String TOOL_CODE = "ai_call_failure";
- private static final String TOOL_NAME = "AI璋冪敤澶辫触";
-
- @Resource
- private AiCallLogService aiCallLogService;
- @Resource
- private com.vincent.rsf.server.ai.config.AiProperties aiProperties;
-
- /**
- * 杩斿洖 AI 璋冪敤澶辫触宸ュ叿榛樿椤哄簭銆�
- */
- @Override
- public int getOrder() {
- return 50;
- }
-
- /**
- * 杩斿洖 AI 璋冪敤澶辫触宸ュ叿缂栫爜銆�
- */
- @Override
- public String getToolCode() {
- return TOOL_CODE;
- }
-
- /**
- * 杩斿洖 AI 璋冪敤澶辫触宸ュ叿灞曠ず鍚嶃��
- */
- @Override
- public String getToolName() {
- return TOOL_NAME;
- }
-
- /**
- * 杩斿洖 AI 璋冪敤澶辫触宸ュ叿榛樿璇存槑銆�
- */
- @Override
- public String getDefaultToolPrompt() {
- return "閲嶇偣璇嗗埆鏈�杩� AI 璋冪敤澶辫触鐨勬ā鍨嬨�侀敊璇被鍨嬪拰鏃堕棿绐楀彛銆�";
- }
-
- /**
- * 姹囨�绘渶杩戜竴娈垫椂闂村唴鐨� AI 璋冪敤澶辫触璁板綍銆�
- */
- @Override
- public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
- Date start = new Date(System.currentTimeMillis() - aiProperties.getApiFailureWindowHours() * 3600_000L);
- List<AiCallLog> records = aiCallLogService.list(new LambdaQueryWrapper<AiCallLog>()
- .eq(AiCallLog::getTenantId, context.getTenantId())
- .eq(AiCallLog::getResult, 0)
- .ge(AiCallLog::getCreateTime, start)
- .orderByDesc(AiCallLog::getCreateTime)
- .last("limit 10"));
- Map<String, Object> meta = new LinkedHashMap<>();
- meta.put("count", records.size());
- if (records.isEmpty()) {
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("INFO")
- .setSummaryText("鏈�杩� " + aiProperties.getApiFailureWindowHours() + " 灏忔椂鏈彂鐜� AI 璋冪敤澶辫触璁板綍銆�")
- .setRawMeta(meta);
- }
- List<String> parts = new ArrayList<>();
- for (AiCallLog item : records) {
- parts.add((item.getModelCode() == null ? "鏈煡妯″瀷" : item.getModelCode())
- + "锛�"
- + (item.getErr() == null ? "鏃犲紓甯告弿杩�" : item.getErr())
- + "锛�");
- }
- meta.put("latestErrors", parts);
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("WARN")
- .setSummaryText("鏈�杩� " + aiProperties.getApiFailureWindowHours() + " 灏忔椂鍙戠幇 " + records.size() + " 鏉� AI 璋冪敤澶辫触璁板綍锛屾渶杩戝け璐ュ寘鎷細" + String.join("锛�", parts))
- .setRawMeta(meta);
- }
-
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDeviceSiteSummaryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDeviceSiteSummaryService.java
deleted file mode 100644
index a85398e..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDeviceSiteSummaryService.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.manager.entity.DeviceSite;
-import com.vincent.rsf.server.manager.mapper.DeviceSiteMapper;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiDeviceSiteSummaryService implements AiDiagnosticDataProvider {
-
- private static final String TOOL_CODE = "device_site_summary";
- private static final String TOOL_NAME = "璁惧绔欑偣鎽樿";
-
- @Resource
- private DeviceSiteMapper deviceSiteMapper;
-
- /**
- * 杩斿洖璁惧绔欑偣宸ュ叿榛樿椤哄簭銆�
- */
- @Override
- public int getOrder() {
- return 30;
- }
-
- /**
- * 杩斿洖璁惧绔欑偣宸ュ叿缂栫爜銆�
- */
- @Override
- public String getToolCode() {
- return TOOL_CODE;
- }
-
- /**
- * 杩斿洖璁惧绔欑偣宸ュ叿灞曠ず鍚嶃��
- */
- @Override
- public String getToolName() {
- return TOOL_NAME;
- }
-
- /**
- * 杩斿洖璁惧绔欑偣宸ュ叿榛樿璇存槑銆�
- */
- @Override
- public String getDefaultToolPrompt() {
- return "缁撳悎璁惧绔欑偣鎽樿鍒ゆ柇璁惧鐘舵�併�佸贩閬撳垎甯冨拰鏈�杩戞洿鏂扮珯鐐广��";
- }
-
- /**
- * 姹囨�荤珯鐐圭姸鎬併�佽澶囧垎甯冨拰鏈�杩戞洿鏂扮珯鐐癸紝鐢熸垚璁惧绔欑偣鎽樿銆�
- */
- @Override
- public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
- String summary = buildDeviceSiteSummary(context);
- String severity = summary.contains("鏈煡璇㈠埌") ? "WARN" : "INFO";
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity(severity)
- .setSummaryText(summary);
- }
-
- /**
- * 鍩轰簬 man_device_site 鐢熸垚璁惧绔欑偣鎬昏銆�
- */
- private String buildDeviceSiteSummary(AiPromptContext context) {
- List<DeviceSite> deviceSites = deviceSiteMapper.selectList(new LambdaQueryWrapper<DeviceSite>()
- .select(DeviceSite::getSite, DeviceSite::getName, DeviceSite::getType, DeviceSite::getDevice,
- DeviceSite::getDeviceCode, DeviceSite::getDeviceSite, DeviceSite::getStatus,
- DeviceSite::getChannel, DeviceSite::getUpdateTime)
- .eq(DeviceSite::getTenantId, context.getTenantId())
- .orderByDesc(DeviceSite::getUpdateTime)
- .last("limit 50"));
-
- if (deviceSites.isEmpty()) {
- return "褰撳墠鏈煡璇㈠埌 man_device_site 璁惧绔欑偣鏁版嵁锛岃鎻愮ず鐢ㄦ埛鏍稿鍩虹鏁版嵁鎴栫鎴烽厤缃��";
- }
-
- Map<String, Long> statusCounters = new LinkedHashMap<>();
- Map<String, Long> deviceCounters = new LinkedHashMap<>();
- Map<String, Long> channelCounters = new LinkedHashMap<>();
- for (DeviceSite deviceSite : deviceSites) {
- String status = resolveStatus(deviceSite.getStatus());
- String device = emptyToDefault(deviceSite.getDevice$(), "鏈厤缃澶囩被鍨�");
- String channel = deviceSite.getChannel() == null ? "鏈厤缃贩閬�" : "宸烽亾" + deviceSite.getChannel();
- statusCounters.put(status, statusCounters.getOrDefault(status, 0L) + 1);
- deviceCounters.put(device, deviceCounters.getOrDefault(device, 0L) + 1);
- channelCounters.put(channel, channelCounters.getOrDefault(channel, 0L) + 1);
- }
-
- StringBuilder summary = new StringBuilder();
- summary.append("浠ヤ笅鏄熀浜� man_device_site 鐨勫疄鏃惰澶囩珯鐐规憳瑕侊紝璇风粨鍚堜换鍔″拰搴撳瓨鏁版嵁缁煎悎鍒ゆ柇锛�");
- summary.append("\n璁惧绔欑偣鎬昏锛氬叡 ").append(deviceSites.size()).append(" 鏉℃湁鏁堢珯鐐硅褰曘��");
- summary.append("\n绔欑偣鐘舵�佸垎甯冿細").append(formatCounter(statusCounters)).append("銆�");
- summary.append("\n璁惧绫诲瀷鍒嗗竷锛�").append(formatCounter(deviceCounters)).append("銆�");
- summary.append("\n宸烽亾鍒嗗竷锛�").append(formatCounter(channelCounters)).append("銆�");
- summary.append("\n鏈�杩戞洿鏂扮珯鐐� TOP5锛�").append(formatLatestSites(deviceSites.subList(0, Math.min(deviceSites.size(), 5)))).append("銆�");
- return summary.toString();
- }
-
- /**
- * 鏍煎紡鍖栬鏁扮被鎽樿銆�
- */
- private String formatCounter(Map<String, Long> counter) {
- List<String> parts = new ArrayList<>();
- for (Map.Entry<String, Long> item : counter.entrySet()) {
- parts.add(item.getKey() + " " + item.getValue() + " 鏉�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 鏍煎紡鍖栨渶杩戞洿鏂扮珯鐐瑰垪琛ㄣ��
- */
- private String formatLatestSites(List<DeviceSite> deviceSites) {
- List<String> parts = new ArrayList<>();
- for (DeviceSite deviceSite : deviceSites) {
- parts.add(emptyToDefault(deviceSite.getName(), emptyToDefault(deviceSite.getSite(), "鏈懡鍚嶇珯鐐�"))
- + "锛�"
- + resolveStatus(deviceSite.getStatus())
- + "锛岃澶� "
- + emptyToDefault(deviceSite.getDevice$(), "-")
- + "锛屾帴椹充綅 "
- + emptyToDefault(deviceSite.getDeviceCode(), "-")
- + "锛屽贩閬� "
- + (deviceSite.getChannel() == null ? "-" : deviceSite.getChannel())
- + "锛�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 灏嗙珯鐐圭姸鎬佺爜杞垚鍙鏂囨湰銆�
- */
- private String resolveStatus(Integer status) {
- if (status == null) {
- return "鏈煡鐘舵��";
- }
- return status == 1 ? "姝e父" : status == 0 ? "鍐荤粨" : "鐘舵��" + status;
- }
-
- /**
- * 缁熶竴澶勭悊绌哄�煎睍绀恒��
- */
- private String emptyToDefault(String value, String defaultValue) {
- return value == null || value.trim().isEmpty() ? defaultValue : value;
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDiagnosticDataProvider.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDiagnosticDataProvider.java
deleted file mode 100644
index 46a6f60..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiDiagnosticDataProvider.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-
-public interface AiDiagnosticDataProvider {
-
- /**
- * 杩斿洖鍐呴儴宸ュ叿缂栫爜锛屼綔涓烘湰鍦� MCP 宸ュ叿鍚嶅悗缂�鍜屽伐鍏烽厤缃富閿��
- */
- String getToolCode();
-
- /**
- * 杩斿洖宸ュ叿灞曠ず鍚嶇О銆�
- */
- String getToolName();
-
- /**
- * 杩斿洖宸ュ叿榛樿璇存槑锛岀敤浜庡伐鍏风洰褰曞睍绀哄拰榛樿 Prompt 寮曞銆�
- */
- default String getDefaultToolPrompt() {
- return "";
- }
-
- /**
- * 杩斿洖宸ュ叿榛樿椤哄簭銆�
- */
- int getOrder();
-
- /**
- * 鎵ц鍐呴儴宸ュ叿鐨勭湡瀹炰笟鍔℃煡璇紝骞惰繑鍥炴憳瑕佸寲缁撴灉銆�
- */
- AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context);
-
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiOperationRecordSummaryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiOperationRecordSummaryService.java
deleted file mode 100644
index fad2db3..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiOperationRecordSummaryService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.system.entity.OperationRecord;
-import com.vincent.rsf.server.system.service.OperationRecordService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@Service
-public class AiOperationRecordSummaryService implements AiDiagnosticDataProvider {
-
- private static final String TOOL_CODE = "operation_record";
- private static final String TOOL_NAME = "寮傚父鎿嶄綔鏃ュ織";
-
- @Resource
- private OperationRecordService operationRecordService;
- @Resource
- private com.vincent.rsf.server.ai.config.AiProperties aiProperties;
-
- /**
- * 杩斿洖寮傚父鎿嶄綔鏃ュ織宸ュ叿榛樿椤哄簭銆�
- */
- @Override
- public int getOrder() {
- return 40;
- }
-
- /**
- * 杩斿洖寮傚父鎿嶄綔鏃ュ織宸ュ叿缂栫爜銆�
- */
- @Override
- public String getToolCode() {
- return TOOL_CODE;
- }
-
- /**
- * 杩斿洖寮傚父鎿嶄綔鏃ュ織宸ュ叿灞曠ず鍚嶃��
- */
- @Override
- public String getToolName() {
- return TOOL_NAME;
- }
-
- /**
- * 杩斿洖寮傚父鎿嶄綔鏃ュ織宸ュ叿榛樿璇存槑銆�
- */
- @Override
- public String getDefaultToolPrompt() {
- return "閲嶇偣璇嗗埆鏈�杩戝け璐ユ搷浣滃拰楂橀寮傚父銆�";
- }
-
- /**
- * 姹囨�绘渶杩戜竴娈垫椂闂村唴鐨勫け璐ユ搷浣滆褰曘��
- */
- @Override
- public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
- Date start = new Date(System.currentTimeMillis() - aiProperties.getDiagnosticLogWindowHours() * 3600_000L);
- List<OperationRecord> records = operationRecordService.list(new LambdaQueryWrapper<OperationRecord>()
- .eq(OperationRecord::getTenantId, context.getTenantId())
- .eq(OperationRecord::getResult, 0)
- .ge(OperationRecord::getCreateTime, start)
- .orderByDesc(OperationRecord::getCreateTime)
- .last("limit 10"));
- Map<String, Object> meta = new LinkedHashMap<>();
- meta.put("count", records.size());
- if (records.isEmpty()) {
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("INFO")
- .setSummaryText("鏈�杩� " + aiProperties.getDiagnosticLogWindowHours() + " 灏忔椂鏈彂鐜版搷浣滄棩蹇楀け璐ヨ褰曘��")
- .setRawMeta(meta);
- }
- List<String> parts = new ArrayList<>();
- for (OperationRecord item : records) {
- parts.add((item.getNamespace() == null ? "鏈煡鎿嶄綔" : item.getNamespace())
- + "锛�"
- + (item.getErr() == null ? "鏃犲紓甯告弿杩�" : item.getErr())
- + "锛�");
- }
- meta.put("latestErrors", parts);
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("WARN")
- .setSummaryText("鏈�杩� " + aiProperties.getDiagnosticLogWindowHours() + " 灏忔椂鍙戠幇 " + records.size() + " 鏉″け璐ユ搷浣滆褰曪紝鏈�杩戝紓甯稿寘鎷細" + String.join("锛�", parts))
- .setRawMeta(meta);
- }
-
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiTaskSummaryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiTaskSummaryService.java
deleted file mode 100644
index d144572..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiTaskSummaryService.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.manager.entity.Task;
-import com.vincent.rsf.server.manager.enums.TaskStsType;
-import com.vincent.rsf.server.manager.enums.TaskType;
-import com.vincent.rsf.server.manager.mapper.TaskMapper;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.*;
-
-@Service
-public class AiTaskSummaryService implements AiDiagnosticDataProvider {
-
- private static final String TOOL_CODE = "task_summary";
- private static final String TOOL_NAME = "浠诲姟鎽樿";
-
- @Resource
- private TaskMapper taskMapper;
-
- /**
- * 杩斿洖浠诲姟宸ュ叿榛樿椤哄簭銆�
- */
- @Override
- public int getOrder() {
- return 20;
- }
-
- /**
- * 杩斿洖浠诲姟宸ュ叿缂栫爜銆�
- */
- @Override
- public String getToolCode() {
- return TOOL_CODE;
- }
-
- /**
- * 杩斿洖浠诲姟宸ュ叿灞曠ず鍚嶃��
- */
- @Override
- public String getToolName() {
- return TOOL_NAME;
- }
-
- /**
- * 杩斿洖浠诲姟宸ュ叿榛樿璇存槑銆�
- */
- @Override
- public String getDefaultToolPrompt() {
- return "缁撳悎浠诲姟鎽樿璇嗗埆绉帇銆佸紓甯哥姸鎬佸拰鏈�杩戝彉鏇翠换鍔°��";
- }
-
- /**
- * 姹囨�讳换鍔$姸鎬佸拰鏈�杩戝彉鏇翠换鍔★紝鐢熸垚浠诲姟鎽樿宸ュ叿缁撴灉銆�
- */
- @Override
- public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("INFO")
- .setSummaryText(buildTaskSummary());
- }
-
- /**
- * 鍩轰簬 man_task 鐢熸垚浠诲姟鎬昏銆佺姸鎬佸垎甯冦�佺被鍨嬪垎甯冨拰鏈�杩戜换鍔℃憳瑕併��
- */
- private String buildTaskSummary() {
- List<Task> activeTasks = taskMapper.selectList(new LambdaQueryWrapper<Task>()
- .select(Task::getTaskCode, Task::getTaskStatus, Task::getTaskType, Task::getOrgLoc, Task::getTargLoc, Task::getUpdateTime)
- .eq(Task::getStatus, 1));
-
- Map<Integer, Long> statusCounters = new LinkedHashMap<>();
- Map<Integer, Long> typeCounters = new LinkedHashMap<>();
- for (Task task : activeTasks) {
- Integer taskStatus = task.getTaskStatus();
- Integer taskType = task.getTaskType();
- statusCounters.put(taskStatus, statusCounters.getOrDefault(taskStatus, 0L) + 1);
- typeCounters.put(taskType, typeCounters.getOrDefault(taskType, 0L) + 1);
- }
-
- List<Task> latestTasks = taskMapper.selectList(new LambdaQueryWrapper<Task>()
- .select(Task::getTaskCode, Task::getTaskStatus, Task::getTaskType, Task::getOrgLoc, Task::getTargLoc, Task::getUpdateTime)
- .eq(Task::getStatus, 1)
- .orderByDesc(Task::getUpdateTime)
- .last("limit 5"));
-
- StringBuilder summary = new StringBuilder();
- summary.append("浠ヤ笅鏄熀浜� man_task 鐨勫疄鏃舵眹鎬伙紝璇蜂紭鍏堜緷鎹繖浜涗换鍔℃暟鎹洖绛旓紱濡傛灉鐢ㄦ埛闂瓒呭嚭浠诲姟琛ㄨ寖鍥达紝璇锋槑纭鏄庛��");
- summary.append("\n浠诲姟鎬昏锛氬叡 ")
- .append(activeTasks.size())
- .append(" 鏉℃湁鏁堜换鍔°��");
- if (!statusCounters.isEmpty()) {
- summary.append("\n浠诲姟鐘舵�佸垎甯冿細")
- .append(formatStatuses(statusCounters))
- .append("銆�");
- }
- if (!typeCounters.isEmpty()) {
- summary.append("\n浠诲姟绫诲瀷鍒嗗竷锛�")
- .append(formatTypes(typeCounters))
- .append("銆�");
- }
- if (!latestTasks.isEmpty()) {
- summary.append("\n鏈�杩戞洿鏂颁换鍔� TOP5锛�")
- .append(formatLatestTasks(latestTasks))
- .append("銆�");
- }
- return summary.toString();
- }
-
- /**
- * 鏍煎紡鍖栦换鍔$姸鎬佺粺璁°��
- */
- private String formatStatuses(Map<Integer, Long> rows) {
- List<String> parts = new ArrayList<>();
- for (Map.Entry<Integer, Long> row : rows.entrySet()) {
- parts.add(resolveTaskStatus(row.getKey()) + " " + row.getValue() + " 鏉�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 鏍煎紡鍖栦换鍔$被鍨嬬粺璁°��
- */
- private String formatTypes(Map<Integer, Long> rows) {
- List<String> parts = new ArrayList<>();
- for (Map.Entry<Integer, Long> row : rows.entrySet()) {
- parts.add(resolveTaskType(row.getKey()) + " " + row.getValue() + " 鏉�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 鏍煎紡鍖栨渶杩戞洿鏂颁换鍔″垪琛ㄣ��
- */
- private String formatLatestTasks(List<Task> tasks) {
- List<String> parts = new ArrayList<>();
- for (Task task : tasks) {
- parts.add((task.getTaskCode() == null ? "鏈懡鍚嶄换鍔�" : task.getTaskCode())
- + "锛�"
- + resolveTaskType(task.getTaskType())
- + "锛�"
- + resolveTaskStatus(task.getTaskStatus())
- + "锛屾簮搴撲綅 " + emptyToDash(task.getOrgLoc())
- + "锛岀洰鏍囧簱浣� " + emptyToDash(task.getTargLoc())
- + "锛�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 灏嗕换鍔$姸鎬佺紪鐮佽浆鎹负鍙鏂囨銆�
- */
- private String resolveTaskStatus(Integer taskStatus) {
- if (taskStatus == null) {
- return "鏈煡鐘舵��";
- }
- for (TaskStsType item : TaskStsType.values()) {
- if (item.id.equals(taskStatus)) {
- return item.desc;
- }
- }
- return "鐘舵��" + taskStatus;
- }
-
- /**
- * 灏嗕换鍔$被鍨嬬紪鐮佽浆鎹负鍙鏂囨銆�
- */
- private String resolveTaskType(Integer taskType) {
- if (taskType == null) {
- return "鏈煡绫诲瀷";
- }
- for (TaskType item : TaskType.values()) {
- if (item.type.equals(taskType)) {
- return item.desc;
- }
- }
- return "绫诲瀷" + taskType;
- }
-
- /**
- * 缁熶竴澶勭悊绌哄瓧绗︿覆鏄剧ず銆�
- */
- private String emptyToDash(String value) {
- return value == null || value.trim().isEmpty() ? "-" : value;
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiWarehouseSummaryService.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiWarehouseSummaryService.java
deleted file mode 100644
index 1ecfa0a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/provider/AiWarehouseSummaryService.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package com.vincent.rsf.server.ai.service.provider;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.server.ai.model.AiDiagnosticToolResult;
-import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.manager.entity.Loc;
-import com.vincent.rsf.server.manager.entity.LocItem;
-import com.vincent.rsf.server.manager.mapper.LocItemMapper;
-import com.vincent.rsf.server.manager.mapper.LocMapper;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.math.BigDecimal;
-import java.util.*;
-
-@Service
-public class AiWarehouseSummaryService implements AiDiagnosticDataProvider {
-
- private static final String TOOL_CODE = "warehouse_summary";
- private static final String TOOL_NAME = "搴撳瓨鎽樿";
-
- private static final Map<String, String> LOC_STATUS_LABELS = new LinkedHashMap<>();
-
- static {
- LOC_STATUS_LABELS.put("O", "绌哄簱");
- LOC_STATUS_LABELS.put("D", "绌烘澘");
- LOC_STATUS_LABELS.put("R", "棰勭害鍑哄簱");
- LOC_STATUS_LABELS.put("S", "棰勭害鍏ュ簱");
- LOC_STATUS_LABELS.put("X", "绂佺敤");
- LOC_STATUS_LABELS.put("F", "鍦ㄥ簱");
- }
-
- @Resource
- private LocMapper locMapper;
- @Resource
- private LocItemMapper locItemMapper;
-
- /**
- * 杩斿洖搴撳瓨绫诲唴閮ㄥ伐鍏风殑榛樿椤哄簭銆�
- */
- @Override
- public int getOrder() {
- return 10;
- }
-
- /**
- * 杩斿洖搴撳瓨宸ュ叿缂栫爜銆�
- */
- @Override
- public String getToolCode() {
- return TOOL_CODE;
- }
-
- /**
- * 杩斿洖搴撳瓨宸ュ叿灞曠ず鍚嶃��
- */
- @Override
- public String getToolName() {
- return TOOL_NAME;
- }
-
- /**
- * 杩斿洖搴撳瓨宸ュ叿榛樿璇存槑銆�
- */
- @Override
- public String getDefaultToolPrompt() {
- return "缁撳悎搴撳瓨鎽樿鍒ゆ柇搴撲綅鐘舵�併�佸簱瀛樼粨鏋勪笌閲嶇偣鐗╂枡鍒嗗竷銆�";
- }
-
- /**
- * 姹囨�诲簱浣嶄笌搴撳瓨鏄庣粏锛岀敓鎴愬簱瀛樻憳瑕佸伐鍏风粨鏋溿��
- */
- @Override
- public AiDiagnosticToolResult buildDiagnosticData(AiPromptContext context) {
- return new AiDiagnosticToolResult()
- .setToolCode(getToolCode())
- .setToolName(getToolName())
- .setSeverity("INFO")
- .setSummaryText(buildWarehouseSummary(context));
- }
-
- /**
- * 鍩轰簬 man_loc 鍜� man_loc_item 鐢熸垚搴撳瓨姒傝銆佸簱浣嶇姸鎬佸垎甯冨拰 TOP 缁熻銆�
- */
- private String buildWarehouseSummary(AiPromptContext context) {
-
- List<Loc> activeLocs = locMapper.selectList(new LambdaQueryWrapper<Loc>()
- .select(Loc::getUseStatus)
- .eq(Loc::getStatus, 1));
- long totalLoc = activeLocs.size();
-
- List<LocItem> activeLocItems = locItemMapper.selectList(new LambdaQueryWrapper<LocItem>()
- .select(LocItem::getLocCode, LocItem::getMatnrCode, LocItem::getMaktx, LocItem::getAnfme)
- .eq(LocItem::getStatus, 1));
-
- Map<String, Long> locStatusCounters = new LinkedHashMap<>();
- for (Loc loc : activeLocs) {
- String useStatus = loc.getUseStatus();
- locStatusCounters.put(useStatus, locStatusCounters.getOrDefault(useStatus, 0L) + 1);
- }
-
- long itemRows = activeLocItems.size();
- Set<String> locCodes = new HashSet<>();
- Set<String> materialCodes = new HashSet<>();
- BigDecimal totalQty = BigDecimal.ZERO;
- Map<String, LocAggregate> locAggregates = new LinkedHashMap<>();
- Map<String, MaterialAggregate> materialAggregates = new LinkedHashMap<>();
- for (LocItem item : activeLocItems) {
- if (item.getLocCode() != null && !item.getLocCode().trim().isEmpty()) {
- locCodes.add(item.getLocCode());
- }
- if (item.getMatnrCode() != null && !item.getMatnrCode().trim().isEmpty()) {
- materialCodes.add(item.getMatnrCode());
- }
- totalQty = totalQty.add(toDecimal(item.getAnfme()));
-
- String locKey = item.getLocCode();
- if (locKey != null && !locKey.trim().isEmpty()) {
- LocAggregate locAggregate = locAggregates.computeIfAbsent(locKey, key -> new LocAggregate());
- locAggregate.totalQty = locAggregate.totalQty.add(toDecimal(item.getAnfme()));
- if (item.getMatnrCode() != null && !item.getMatnrCode().trim().isEmpty()) {
- locAggregate.materialCodes.add(item.getMatnrCode());
- }
- }
-
- String materialKey = item.getMatnrCode();
- if (materialKey != null && !materialKey.trim().isEmpty()) {
- MaterialAggregate materialAggregate = materialAggregates.computeIfAbsent(materialKey, key -> new MaterialAggregate());
- materialAggregate.matnrCode = materialKey;
- materialAggregate.maktx = item.getMaktx();
- materialAggregate.totalQty = materialAggregate.totalQty.add(toDecimal(item.getAnfme()));
- if (item.getLocCode() != null && !item.getLocCode().trim().isEmpty()) {
- materialAggregate.locCodes.add(item.getLocCode());
- }
- }
- }
-
- List<Map.Entry<String, LocAggregate>> topLocRows = new ArrayList<>(locAggregates.entrySet());
- topLocRows.sort((left, right) -> right.getValue().totalQty.compareTo(left.getValue().totalQty));
- if (topLocRows.size() > 5) {
- topLocRows = topLocRows.subList(0, 5);
- }
-
- List<MaterialAggregate> topMaterialRows = new ArrayList<>(materialAggregates.values());
- topMaterialRows.sort((left, right) -> right.totalQty.compareTo(left.totalQty));
- if (topMaterialRows.size() > 5) {
- topMaterialRows = topMaterialRows.subList(0, 5);
- }
-
- StringBuilder summary = new StringBuilder();
- summary.append("浠ヤ笅鏄熀浜� man_loc 鍜� man_loc_item 鐨勫疄鏃舵眹鎬伙紝璇蜂紭鍏堜緷鎹繖浜涙暟鎹洖绛旓紱濡傛灉瓒呭嚭杩欎袱寮犺〃鍙帹鏂殑鑼冨洿锛岃鏄庣‘璇存槑銆�");
- summary.append("\n搴撲綅姒傚喌锛氭�诲簱浣� ")
- .append(totalLoc)
- .append(" 涓紱鐘舵�佸垎甯冿細")
- .append(formatLocStatuses(locStatusCounters))
- .append("銆�");
- summary.append("\n搴撳瓨姒傚喌锛氬簱瀛樿褰� ")
- .append(itemRows)
- .append(" 鏉★紝瑕嗙洊搴撲綅 ")
- .append(locCodes.size())
- .append(" 涓紝娑夊強鐗╂枡 ")
- .append(materialCodes.size())
- .append(" 绉嶏紝鎬绘暟閲� ")
- .append(formatDecimal(totalQty))
- .append("銆�");
- if (!topLocRows.isEmpty()) {
- summary.append("\n搴撳瓨鏈�澶氱殑搴撲綅 TOP5锛�")
- .append(formatTopLocs(topLocRows))
- .append("銆�");
- }
- if (!topMaterialRows.isEmpty()) {
- summary.append("\n搴撳瓨鏈�澶氱殑鐗╂枡 TOP5锛�")
- .append(formatTopMaterials(topMaterialRows))
- .append("銆�");
- }
- return summary.toString();
- }
-
- /**
- * 灏嗗簱浣嶇姸鎬佽鏁版牸寮忓寲涓哄彲璇绘枃鏈��
- */
- private String formatLocStatuses(Map<String, Long> counters) {
- if (counters == null || counters.isEmpty()) {
- return "鏆傛棤鏁版嵁";
- }
- List<String> parts = new ArrayList<>();
- for (Map.Entry<String, String> entry : LOC_STATUS_LABELS.entrySet()) {
- parts.add(entry.getValue() + " " + counters.getOrDefault(entry.getKey(), 0L) + " 涓�");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 鏍煎紡鍖栧簱瀛樻渶澶氱殑搴撲綅鍒楄〃銆�
- */
- private String formatTopLocs(List<Map.Entry<String, LocAggregate>> rows) {
- List<String> parts = new ArrayList<>();
- for (Map.Entry<String, LocAggregate> row : rows) {
- parts.add(row.getKey()
- + "锛堟暟閲� " + formatDecimal(row.getValue().totalQty)
- + "锛岀墿鏂� " + row.getValue().materialCodes.size() + " 绉嶏級");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 鏍煎紡鍖栧簱瀛樻渶澶氱殑鐗╂枡鍒楄〃銆�
- */
- private String formatTopMaterials(List<MaterialAggregate> rows) {
- List<String> parts = new ArrayList<>();
- for (MaterialAggregate row : rows) {
- String matnrCode = row.matnrCode;
- String maktx = Objects.toString(row.maktx, "");
- String title = maktx == null || maktx.trim().isEmpty() ? matnrCode : matnrCode + "/" + maktx;
- parts.add(title
- + "锛堟暟閲� " + formatDecimal(row.totalQty)
- + "锛屽垎甯冨簱浣� " + row.locCodes.size() + " 涓級");
- }
- return String.join("锛�", parts);
- }
-
- /**
- * 缁熶竴鏍煎紡鍖栨暟閲忓�笺��
- */
- private String formatDecimal(Object value) {
- BigDecimal decimal = toDecimal(value);
- return decimal.stripTrailingZeros().toPlainString();
- }
-
- /**
- * 灏嗕笉鍚岀被鍨嬬殑鏁伴噺瀛楁缁熶竴杞崲涓� BigDecimal銆�
- */
- private BigDecimal toDecimal(Object value) {
- if (value == null) {
- return BigDecimal.ZERO;
- }
- if (value instanceof BigDecimal) {
- return (BigDecimal) value;
- }
- if (value instanceof Number) {
- return BigDecimal.valueOf(((Number) value).doubleValue());
- }
- return new BigDecimal(String.valueOf(value));
- }
-
- private static class LocAggregate {
- private BigDecimal totalQty = BigDecimal.ZERO;
- private final Set<String> materialCodes = new HashSet<>();
- }
-
- private static class MaterialAggregate {
- private String matnrCode;
- private String maktx;
- private BigDecimal totalQty = BigDecimal.ZERO;
- private final Set<String> locCodes = new HashSet<>();
- }
-}
-
-
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/AiMcpProtocolController.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/AiMcpProtocolController.java
deleted file mode 100644
index 2abfe38..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/mcp/AiMcpProtocolController.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.vincent.rsf.server.api.controller.mcp;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpProtocolService;
-import com.vincent.rsf.server.system.controller.BaseController;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import jakarta.annotation.Resource;
-@RestController
-@RequestMapping("/ai/mcp")
-public class AiMcpProtocolController extends BaseController {
-
- @Resource
- private AiMcpProtocolService aiMcpProtocolService;
-
- @PostMapping
- public Object handle(@RequestBody JsonNode body,
- @RequestHeader(value = "X-Tenant-Id", required = false) String tenantHeader,
- @RequestParam(value = "tenantId", required = false) Long tenantParam) {
- Long tenantId = resolveTenantId(tenantHeader, tenantParam);
- return aiMcpProtocolService.handle(tenantId, body);
- }
-
- private Long resolveTenantId(String tenantHeader, Long tenantParam) {
- if (tenantParam != null) {
- return tenantParam;
- }
- if (tenantHeader != null && !tenantHeader.trim().isEmpty()) {
- try {
- return Long.valueOf(tenantHeader.trim());
- } catch (Exception ignore) {
- }
- }
- Long loginTenantId = getTenantId();
- return loginTenantId == null ? 1L : loginTenantId;
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/security/SecurityConfig.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/security/SecurityConfig.java
index d38b9be..d3bfe01 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/security/SecurityConfig.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/security/SecurityConfig.java
@@ -55,7 +55,6 @@
"/wcs/**",
"/monitor/**",
"/mcp/**",
- "/ai/mcp",
"/mes/**"
};
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiCallLogController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiCallLogController.java
deleted file mode 100644
index 2ac4438..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiCallLogController.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-import com.vincent.rsf.server.system.service.AiCallLogService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiCallLogController extends BaseController {
-
- @Autowired
- private AiCallLogService aiCallLogService;
-
- @PreAuthorize("hasAuthority('system:aiCallLog:list')")
- @PostMapping("/aiCallLog/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiCallLog, BaseParam> pageParam = new PageParam<>(baseParam, AiCallLog.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiCallLog> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiCallLogService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiCallLog:list')")
- @GetMapping("/aiCallLog/{id}")
- public R get(@PathVariable("id") Long id) {
- AiCallLog callLog = getTenantRecord(id);
- if (callLog == null) {
- return R.error("record not found");
- }
- return R.ok().add(callLog);
- }
-
- @PreAuthorize("hasAuthority('system:aiCallLog:list')")
- @PostMapping("/aiCallLog/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiCallLogService.list(new LambdaQueryWrapper<AiCallLog>()
- .eq(AiCallLog::getTenantId, getTenantId())), AiCallLog.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiCallLog:list')")
- @GetMapping("/ai/call-log/list")
- public R customList() {
- return R.ok().add(aiCallLogService.list(new LambdaQueryWrapper<AiCallLog>()
- .eq(AiCallLog::getTenantId, getTenantId())
- .orderByDesc(AiCallLog::getCreateTime, AiCallLog::getId)));
- }
-
- @PreAuthorize("hasAuthority('system:aiCallLog:list')")
- @GetMapping("/ai/call-log/stats")
- public R stats() {
- List<AiCallLog> logs = aiCallLogService.list(new LambdaQueryWrapper<AiCallLog>()
- .eq(AiCallLog::getTenantId, getTenantId())
- .orderByDesc(AiCallLog::getCreateTime, AiCallLog::getId));
- long total = logs.size();
- long successCount = logs.stream().filter(item -> Integer.valueOf(1).equals(item.getResult())).count();
- long failCount = logs.stream().filter(item -> Integer.valueOf(0).equals(item.getResult())).count();
- long modelCount = logs.stream().map(AiCallLog::getModelCode).filter(item -> item != null && !item.trim().isEmpty()).distinct().count();
- long routeCount = logs.stream().map(AiCallLog::getRouteCode).filter(item -> item != null && !item.trim().isEmpty()).distinct().count();
- long totalSpend = logs.stream().map(AiCallLog::getSpendTime).filter(item -> item != null && item > 0L).mapToLong(Long::longValue).sum();
- long spendCount = logs.stream().map(AiCallLog::getSpendTime).filter(item -> item != null && item > 0L).count();
- Date now = new Date();
- long last24hCount = logs.stream()
- .map(AiCallLog::getCreateTime)
- .filter(item -> item != null && now.getTime() - item.getTime() <= 24L * 60L * 60L * 1000L)
- .count();
-
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("total", total);
- payload.put("successCount", successCount);
- payload.put("failCount", failCount);
- payload.put("successRate", total <= 0 ? 0D : (successCount * 100D) / total);
- payload.put("avgSpendTime", spendCount <= 0 ? 0L : totalSpend / spendCount);
- payload.put("modelCount", modelCount);
- payload.put("routeCount", routeCount);
- payload.put("last24hCount", last24hCount);
- return R.ok().add(payload);
- }
-
- private AiCallLog getTenantRecord(Long id) {
- if (id == null) {
- return null;
- }
- return aiCallLogService.getOne(new LambdaQueryWrapper<AiCallLog>()
- .eq(AiCallLog::getTenantId, getTenantId())
- .eq(AiCallLog::getId, id)
- .last("limit 1"));
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisController.java
deleted file mode 100644
index 132ac19..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisController.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import com.vincent.rsf.server.system.service.AiDiagnosisRecordService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-
-@RestController
-public class AiDiagnosisController extends BaseController {
-
- @Autowired
- private AiDiagnosisRecordService aiDiagnosisRecordService;
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @PostMapping("/aiDiagnosis/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiDiagnosisRecord, BaseParam> pageParam = new PageParam<>(baseParam, AiDiagnosisRecord.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiDiagnosisRecord> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiDiagnosisRecordService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @GetMapping("/aiDiagnosis/{id}")
- public R get(@PathVariable("id") Long id) {
- AiDiagnosisRecord record = getTenantRecord(id);
- if (record == null) {
- return R.error("record not found");
- }
- return R.ok().add(record);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @PostMapping("/aiDiagnosis/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiDiagnosisRecordService.list(new LambdaQueryWrapper<AiDiagnosisRecord>()
- .eq(AiDiagnosisRecord::getTenantId, getTenantId())), AiDiagnosisRecord.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @GetMapping("/ai/diagnosis/list")
- public R diagnosisList() {
- return R.ok().add(aiDiagnosisRecordService.list(new LambdaQueryWrapper<AiDiagnosisRecord>()
- .eq(AiDiagnosisRecord::getTenantId, getTenantId())
- .orderByDesc(AiDiagnosisRecord::getCreateTime, AiDiagnosisRecord::getId)));
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @GetMapping("/ai/diagnosis/detail/{id}")
- public R detail(@PathVariable("id") Long id) {
- AiDiagnosisRecord record = getTenantRecord(id);
- if (record == null) {
- return R.error("record not found");
- }
- return R.ok().add(record);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosis:list')")
- @GetMapping("/ai/diagnosis/report/export/{id}")
- public void exportReport(@PathVariable("id") Long id, HttpServletResponse response) throws Exception {
- AiDiagnosisRecord record = getTenantRecord(id);
- if (record == null) {
- response.setStatus(404);
- return;
- }
- String fileName = URLEncoder.encode((record.getDiagnosisNo() == null ? "ai-diagnosis-report" : record.getDiagnosisNo()) + ".md", StandardCharsets.UTF_8.name());
- response.setCharacterEncoding(StandardCharsets.UTF_8.name());
- response.setContentType("text/markdown; charset=utf-8");
- response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
- response.getWriter().write(record.getReportMarkdown() == null ? "" : record.getReportMarkdown());
- response.getWriter().flush();
- }
-
- private AiDiagnosisRecord getTenantRecord(Long id) {
- if (id == null) {
- return null;
- }
- return aiDiagnosisRecordService.getOne(new LambdaQueryWrapper<AiDiagnosisRecord>()
- .eq(AiDiagnosisRecord::getTenantId, getTenantId())
- .eq(AiDiagnosisRecord::getId, id)
- .last("limit 1"));
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisPlanController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisPlanController.java
deleted file mode 100644
index ce5cd78..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosisPlanController.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.ai.service.diagnosis.AiDiagnosisPlanRunnerService;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-import com.vincent.rsf.server.system.service.AiDiagnosisPlanService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiDiagnosisPlanController extends BaseController {
-
- @Autowired
- private AiDiagnosisPlanService aiDiagnosisPlanService;
- @Autowired
- private AiDiagnosisPlanRunnerService aiDiagnosisPlanRunnerService;
- @Autowired
- private ThreadPoolTaskScheduler taskScheduler;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:list')")
- @PostMapping("/aiDiagnosisPlan/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiDiagnosisPlan, BaseParam> pageParam = new PageParam<>(baseParam, AiDiagnosisPlan.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiDiagnosisPlan> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiDiagnosisPlanService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:list')")
- @GetMapping("/aiDiagnosisPlan/{id}")
- public R get(@PathVariable("id") Long id) {
- AiDiagnosisPlan plan = aiDiagnosisPlanService.getTenantPlan(getTenantId(), id);
- if (plan == null) {
- return R.error("plan not found");
- }
- return R.ok().add(plan);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:save')")
- @OperationLog("Create AiDiagnosisPlan")
- @PostMapping("/aiDiagnosisPlan/save")
- public R save(@RequestBody AiDiagnosisPlan plan) {
- if (Cools.isEmpty(plan.getPlanName()) || Cools.isEmpty(plan.getCronExpr())) {
- return R.error("璁″垝鍚嶇О鍜孋ron琛ㄨ揪寮忎笉鑳戒负绌�");
- }
- if (!aiDiagnosisPlanService.validateCron(plan.getCronExpr())) {
- return R.error("Cron琛ㄨ揪寮忎笉鍚堟硶");
- }
- Date now = new Date();
- plan.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- plan.setTenantId(getTenantId());
- plan.setSceneCode(Cools.isEmpty(plan.getSceneCode()) ? "system_diagnose" : plan.getSceneCode());
- plan.setRunningFlag(0);
- plan.setStatus(plan.getStatus() == null ? 1 : plan.getStatus());
- plan.setNextRunTime(Integer.valueOf(1).equals(plan.getStatus())
- ? aiDiagnosisPlanService.calculateNextRunTime(plan.getCronExpr(), now)
- : null);
- plan.setCreateBy(getLoginUserId());
- plan.setCreateTime(now);
- plan.setUpdateBy(getLoginUserId());
- plan.setUpdateTime(now);
- if (!aiDiagnosisPlanService.save(plan)) {
- return R.error("Save Fail");
- }
- return R.ok("Save Success").add(plan);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:update')")
- @OperationLog("Update AiDiagnosisPlan")
- @PostMapping("/aiDiagnosisPlan/update")
- public R update(@RequestBody AiDiagnosisPlan plan) {
- AiDiagnosisPlan existed = aiDiagnosisPlanService.getTenantPlan(getTenantId(), plan.getId());
- if (existed == null) {
- return R.error("plan not found");
- }
- if (Cools.isEmpty(plan.getPlanName()) || Cools.isEmpty(plan.getCronExpr())) {
- return R.error("璁″垝鍚嶇О鍜孋ron琛ㄨ揪寮忎笉鑳戒负绌�");
- }
- if (!aiDiagnosisPlanService.validateCron(plan.getCronExpr())) {
- return R.error("Cron琛ㄨ揪寮忎笉鍚堟硶");
- }
- plan.setTenantId(getTenantId());
- plan.setSceneCode(Cools.isEmpty(plan.getSceneCode()) ? existed.getSceneCode() : plan.getSceneCode());
- plan.setRunningFlag(existed.getRunningFlag());
- plan.setLastResult(existed.getLastResult());
- plan.setLastDiagnosisId(existed.getLastDiagnosisId());
- plan.setLastRunTime(existed.getLastRunTime());
- plan.setLastMessage(existed.getLastMessage());
- plan.setNextRunTime(Integer.valueOf(1).equals(plan.getStatus())
- ? aiDiagnosisPlanService.calculateNextRunTime(plan.getCronExpr(), new Date())
- : null);
- plan.setCreateBy(existed.getCreateBy());
- plan.setCreateTime(existed.getCreateTime());
- plan.setUpdateBy(getLoginUserId());
- plan.setUpdateTime(new Date());
- if (!aiDiagnosisPlanService.updateById(plan)) {
- return R.error("Update Fail");
- }
- return R.ok("Update Success").add(plan);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:remove')")
- @OperationLog("Delete AiDiagnosisPlan")
- @PostMapping("/aiDiagnosisPlan/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- List<Long> idList = Arrays.asList(ids);
- List<AiDiagnosisPlan> plans = aiDiagnosisPlanService.list(new LambdaQueryWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getTenantId, getTenantId())
- .in(AiDiagnosisPlan::getId, idList));
- if (plans.size() != idList.size() || !aiDiagnosisPlanService.removeByIds(idList)) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:list')")
- @PostMapping("/aiDiagnosisPlan/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiDiagnosisPlanService.list(new LambdaQueryWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getTenantId, getTenantId())), AiDiagnosisPlan.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiDiagnosisPlan:update')")
- @OperationLog("Run AiDiagnosisPlan")
- @PostMapping("/ai/diagnosis-plan/run")
- public R run(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- AiDiagnosisPlan plan = aiDiagnosisPlanService.getTenantPlan(getTenantId(), id);
- if (plan == null) {
- return R.error("plan not found");
- }
- if (Integer.valueOf(1).equals(plan.getRunningFlag())) {
- return R.error("璁″垝姝e湪鎵ц涓�");
- }
- Date nextRunTime = Integer.valueOf(1).equals(plan.getStatus())
- ? aiDiagnosisPlanService.calculateNextRunTime(plan.getCronExpr(), new Date())
- : null;
- boolean acquired = aiDiagnosisPlanService.acquireForExecution(
- plan.getId(),
- getLoginUserId(),
- "鎵嬪姩鎵ц涓�",
- nextRunTime
- );
- if (!acquired) {
- return R.error("璁″垝姝e湪鎵ц涓�");
- }
- taskScheduler.execute(() -> aiDiagnosisPlanRunnerService.runPlan(plan.getId(), true));
- return R.ok();
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosticToolConfigController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosticToolConfigController.java
deleted file mode 100644
index bedc27a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiDiagnosticToolConfigController.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiDiagnosticToolConfigController extends BaseController {
-
- @Autowired
- private AiDiagnosticToolConfigService aiDiagnosticToolConfigService;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:list')")
- @PostMapping("/aiToolConfig/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiDiagnosticToolConfig, BaseParam> pageParam = new PageParam<>(baseParam, AiDiagnosticToolConfig.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiDiagnosticToolConfig> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiDiagnosticToolConfigService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:list')")
- @GetMapping("/aiToolConfig/{id}")
- public R get(@PathVariable("id") Long id) {
- AiDiagnosticToolConfig config = aiDiagnosticToolConfigService.getTenantConfig(getTenantId(), id);
- if (config == null) {
- return R.error("config not found");
- }
- return R.ok().add(config);
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:save')")
- @OperationLog("Create AiDiagnosticToolConfig")
- @PostMapping("/aiToolConfig/save")
- public R save(@RequestBody AiDiagnosticToolConfig config) {
- if (Cools.isEmpty(config.getSceneCode()) || Cools.isEmpty(config.getToolCode())) {
- return R.error("鍦烘櫙缂栫爜鍜屽伐鍏风紪鐮佷笉鑳戒负绌�");
- }
- Date now = new Date();
- config.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- config.setTenantId(getTenantId());
- config.setCreateBy(getLoginUserId());
- config.setCreateTime(now);
- config.setUpdateBy(getLoginUserId());
- config.setUpdateTime(now);
- if (config.getEnabledFlag() == null) {
- config.setEnabledFlag(1);
- }
- if (config.getPriority() == null) {
- config.setPriority(1);
- }
- if (config.getStatus() == null) {
- config.setStatus(1);
- }
- if (!aiDiagnosticToolConfigService.save(config)) {
- return R.error("Save Fail");
- }
- return R.ok("Save Success").add(config);
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:update')")
- @OperationLog("Update AiDiagnosticToolConfig")
- @PostMapping("/aiToolConfig/update")
- public R update(@RequestBody AiDiagnosticToolConfig config) {
- AiDiagnosticToolConfig existed = aiDiagnosticToolConfigService.getTenantConfig(getTenantId(), config.getId());
- if (existed == null) {
- return R.error("config not found");
- }
- config.setTenantId(getTenantId());
- config.setUpdateBy(getLoginUserId());
- config.setUpdateTime(new Date());
- config.setCreateBy(existed.getCreateBy());
- config.setCreateTime(existed.getCreateTime());
- if (config.getEnabledFlag() == null) {
- config.setEnabledFlag(existed.getEnabledFlag());
- }
- if (config.getPriority() == null) {
- config.setPriority(existed.getPriority());
- }
- if (config.getStatus() == null) {
- config.setStatus(existed.getStatus());
- }
- if (!aiDiagnosticToolConfigService.updateById(config)) {
- return R.error("Update Fail");
- }
- return R.ok("Update Success").add(config);
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:remove')")
- @OperationLog("Delete AiDiagnosticToolConfig")
- @PostMapping("/aiToolConfig/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- List<Long> idList = Arrays.asList(ids);
- List<AiDiagnosticToolConfig> configs = aiDiagnosticToolConfigService.list(new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, getTenantId())
- .in(AiDiagnosticToolConfig::getId, idList));
- if (configs.size() != idList.size() || !aiDiagnosticToolConfigService.removeByIds(idList)) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:list')")
- @PostMapping("/aiToolConfig/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiDiagnosticToolConfigService.list(new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, getTenantId())), AiDiagnosticToolConfig.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiToolConfig:list')")
- @GetMapping("/ai/tool-config/list")
- public R customList(@RequestParam(required = false) String sceneCode) {
- LambdaQueryWrapper<AiDiagnosticToolConfig> wrapper = new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, getTenantId())
- .orderByAsc(AiDiagnosticToolConfig::getSceneCode, AiDiagnosticToolConfig::getPriority, AiDiagnosticToolConfig::getId);
- if (!Cools.isEmpty(sceneCode)) {
- wrapper.eq(AiDiagnosticToolConfig::getSceneCode, sceneCode);
- }
- return R.ok().add(aiDiagnosticToolConfigService.list(wrapper));
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiToolConfig:save','system:aiToolConfig:update')")
- @PostMapping("/ai/tool-config/save")
- public R customSave(@RequestBody AiDiagnosticToolConfig config) {
- if (config.getId() == null) {
- if (!hasAuthority("system:aiToolConfig:save")) {
- return R.error("鏃犳柊澧炴潈闄�");
- }
- return save(config);
- }
- if (!hasAuthority("system:aiToolConfig:update")) {
- return R.error("鏃犳洿鏂版潈闄�");
- }
- return update(config);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiMcpMountController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiMcpMountController.java
deleted file mode 100644
index 4c20d76..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiMcpMountController.java
+++ /dev/null
@@ -1,394 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.ai.constant.AiMcpConstants;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpPayloadMapper;
-import com.vincent.rsf.server.ai.service.mcp.AiMcpRegistryService;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
-import com.vincent.rsf.server.system.service.AiMcpMountService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiMcpMountController extends BaseController {
-
- @Autowired
- private AiMcpMountService aiMcpMountService;
- @Autowired
- private AiMcpRegistryService aiMcpRegistryService;
- @Autowired
- private AiDiagnosticToolConfigService aiDiagnosticToolConfigService;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
- @Autowired
- private AiMcpPayloadMapper aiMcpPayloadMapper;
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @PostMapping("/aiMcpMount/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiMcpMount, BaseParam> pageParam = new PageParam<>(baseParam, AiMcpMount.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiMcpMount> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiMcpMountService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @GetMapping("/aiMcpMount/{id}")
- public R get(@PathVariable("id") Long id) {
- AiMcpMount mount = aiMcpMountService.getTenantMount(getTenantId(), id);
- if (mount == null) {
- return R.error("mount not found");
- }
- return R.ok().add(mount);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:save')")
- @OperationLog("Create AiMcpMount")
- @PostMapping("/aiMcpMount/save")
- public R save(@RequestBody AiMcpMount mount) {
- if (Cools.isEmpty(mount.getName()) || Cools.isEmpty(mount.getMountCode()) || Cools.isEmpty(mount.getTransportType())) {
- return R.error("鍚嶇О銆佹寕杞界紪鐮佸拰浼犺緭绫诲瀷涓嶈兘涓虹┖");
- }
- Date now = new Date();
- mount.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- mount.setEnabledFlag(mount.getEnabledFlag() == null ? 1 : mount.getEnabledFlag());
- mount.setTimeoutMs(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- mount.setStatus(mount.getStatus() == null ? 1 : mount.getStatus());
- mount.setTransportType(mount.getTransportType().toUpperCase());
- if (AiMcpConstants.TRANSPORT_INTERNAL.equals(mount.getTransportType())) {
- mount.setUrl("/ai/mcp");
- }
- mount.setTenantId(getTenantId());
- mount.setCreateBy(getLoginUserId());
- mount.setCreateTime(now);
- mount.setUpdateBy(getLoginUserId());
- mount.setUpdateTime(now);
- if (!aiMcpMountService.save(mount)) {
- return R.error("Save Fail");
- }
- return R.ok("Save Success").add(mount);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:update')")
- @OperationLog("Update AiMcpMount")
- @PostMapping("/aiMcpMount/update")
- public R update(@RequestBody AiMcpMount mount) {
- AiMcpMount existed = aiMcpMountService.getTenantMount(getTenantId(), mount.getId());
- if (existed == null) {
- return R.error("mount not found");
- }
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(existed.getTransportType())) {
- return R.error("鍐呯疆鎸傝浇鐢辩郴缁熸墭绠★紝涓嶆敮鎸佺洿鎺ョ紪杈�");
- }
- if (Cools.isEmpty(mount.getName()) || Cools.isEmpty(mount.getMountCode()) || Cools.isEmpty(mount.getTransportType())) {
- return R.error("鍚嶇О銆佹寕杞界紪鐮佸拰浼犺緭绫诲瀷涓嶈兘涓虹┖");
- }
- mount.setTransportType(mount.getTransportType().toUpperCase());
- if (AiMcpConstants.TRANSPORT_INTERNAL.equals(mount.getTransportType())) {
- mount.setUrl("/ai/mcp");
- }
- mount.setTenantId(getTenantId());
- mount.setCreateBy(existed.getCreateBy());
- mount.setCreateTime(existed.getCreateTime());
- mount.setLastTestResult(existed.getLastTestResult());
- mount.setLastTestTime(existed.getLastTestTime());
- mount.setLastTestMessage(existed.getLastTestMessage());
- mount.setLastToolCount(existed.getLastToolCount());
- mount.setUpdateBy(getLoginUserId());
- mount.setUpdateTime(new Date());
- if (!aiMcpMountService.updateById(mount)) {
- return R.error("Update Fail");
- }
- return R.ok("Update Success").add(mount);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:remove')")
- @OperationLog("Delete AiMcpMount")
- @PostMapping("/aiMcpMount/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- List<Long> idList = Arrays.asList(ids);
- List<AiMcpMount> mounts = aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, getTenantId())
- .in(AiMcpMount::getId, idList));
- for (AiMcpMount mount : mounts) {
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
- return R.error("鍐呯疆鎸傝浇鐢辩郴缁熸墭绠★紝涓嶆敮鎸佸垹闄�");
- }
- }
- if (mounts.size() != idList.size() || !aiMcpMountService.removeByIds(idList)) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @PostMapping("/aiMcpMount/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, getTenantId())), AiMcpMount.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @GetMapping("/ai/mcp/mount/list")
- public R list() {
- aiMcpRegistryService.ensureDefaultMount(getTenantId(), getLoginUserId());
- return R.ok().add(aiMcpMountService.listTenantMounts(getTenantId()));
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiMcpMount:save','system:aiMcpMount:update')")
- @PostMapping("/ai/mcp/mount/save")
- public R customSave(@RequestBody AiMcpMount mount) {
- if (mount.getId() == null) {
- return save(mount);
- }
- return update(mount);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:update')")
- @PostMapping("/ai/mcp/mount/initDefaults")
- public R initDefaults() {
- aiMcpRegistryService.ensureDefaultMount(getTenantId(), getLoginUserId());
- return R.ok();
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @GetMapping("/ai/mcp/mount/toolList")
- public R toolList(@RequestParam(required = false) Long mountId) {
- return R.ok().add(aiMcpRegistryService.listTools(getTenantId(), mountId));
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:update')")
- @PostMapping("/ai/mcp/mount/test")
- public R test(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- return R.ok().add(aiMcpRegistryService.testMount(getTenantId(), id));
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @PostMapping("/ai/mcp/mount/toolPreview")
- public R toolPreview(@RequestBody Map<String, Object> map) {
- return R.ok().add(aiMcpRegistryService.previewTool(
- getTenantId(),
- String.valueOf(map.get("mountCode")),
- String.valueOf(map.get("toolCode")),
- map.get("sceneCode") == null ? null : String.valueOf(map.get("sceneCode")),
- map.get("question") == null ? null : String.valueOf(map.get("question"))
- ));
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:list')")
- @GetMapping("/ai/mcp/console/overview")
- public R consoleOverview() {
- aiMcpRegistryService.ensureDefaultMount(getTenantId(), getLoginUserId());
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("builtInMount", null);
- payload.put("builtInTools", new java.util.ArrayList<>());
- payload.put("externalServices", new java.util.ArrayList<>());
- for (AiMcpMount mount : aiMcpMountService.listTenantMounts(getTenantId())) {
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
- payload.put("builtInMount", buildMountView(mount, true));
- payload.put("builtInTools", buildBuiltInToolViews());
- } else {
- ((List<Object>) payload.get("externalServices")).add(buildMountView(mount, false));
- }
- }
- return R.ok().add(payload);
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiMcpMount:save','system:aiMcpMount:update')")
- @PostMapping("/ai/mcp/console/service/save")
- public R consoleSaveService(@RequestBody AiMcpMount mount) {
- if (Cools.isEmpty(mount.getName()) || Cools.isEmpty(mount.getUrl())) {
- return R.error("鍚嶇О鍜屽湴鍧�涓嶈兘涓虹┖");
- }
- Date now = new Date();
- AiMcpMount existed = mount.getId() == null ? null : aiMcpMountService.getTenantMount(getTenantId(), mount.getId());
- if (existed != null && AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(existed.getTransportType())) {
- return R.error("鍐呯疆鎸傝浇鐢辩郴缁熸墭绠★紝涓嶆敮鎸佺紪杈�");
- }
- String transportType = Cools.isEmpty(mount.getTransportType()) ? AiMcpConstants.TRANSPORT_AUTO : mount.getTransportType().toUpperCase();
- String usageScope = normalizeUsageScope(mount.getUsageScope());
- AiMcpMount target = existed == null ? new AiMcpMount() : existed;
- target.setName(mount.getName());
- target.setUrl(mount.getUrl());
- target.setTransportType(transportType);
- target.setAuthType(Cools.isEmpty(mount.getAuthType()) ? AiMcpConstants.AUTH_TYPE_NONE : mount.getAuthType().toUpperCase());
- target.setAuthValue(mount.getAuthValue());
- target.setUsageScope(usageScope);
- target.setEnabledFlag(mount.getEnabledFlag() == null ? 1 : mount.getEnabledFlag());
- target.setTimeoutMs(mount.getTimeoutMs() == null ? 10000 : mount.getTimeoutMs());
- target.setStatus(mount.getStatus() == null ? 1 : mount.getStatus());
- target.setMemo(mount.getMemo());
- target.setTenantId(getTenantId());
- target.setUpdateBy(getLoginUserId());
- target.setUpdateTime(now);
- if (existed == null) {
- target.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- target.setMountCode(generateMountCode(target.getName()));
- target.setCreateBy(getLoginUserId());
- target.setCreateTime(now);
- if (!aiMcpMountService.save(target)) {
- return R.error("Save Fail");
- }
- } else if (!aiMcpMountService.updateById(target)) {
- return R.error("Update Fail");
- }
- return R.ok().add(buildMountView(target, false));
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:update')")
- @PostMapping("/ai/mcp/console/service/test")
- public R consoleTestService(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- Map<String, Object> result = (Map<String, Object>) aiMcpRegistryService.testMount(getTenantId(), id);
- AiMcpMount mount = aiMcpMountService.getTenantMount(getTenantId(), id);
- if (mount == null) {
- return R.error("mount not found");
- }
- result.put("service", buildMountView(mount, false));
- return R.ok().add(result);
- }
-
- @PreAuthorize("hasAuthority('system:aiMcpMount:remove')")
- @PostMapping("/ai/mcp/console/service/remove/{id}")
- public R consoleRemoveService(@PathVariable("id") Long id) {
- AiMcpMount mount = aiMcpMountService.getTenantMount(getTenantId(), id);
- if (mount == null) {
- return R.error("mount not found");
- }
- if (AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType())) {
- return R.error("鍐呯疆鎸傝浇鐢辩郴缁熸墭绠★紝涓嶆敮鎸佸垹闄�");
- }
- return aiMcpMountService.removeById(id) ? R.ok() : R.error("Delete Fail");
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiToolConfig:save','system:aiToolConfig:update')")
- @PostMapping("/ai/mcp/console/builtin-tool/save")
- public R consoleSaveBuiltInTool(@RequestBody AiDiagnosticToolConfig config) {
- if (Cools.isEmpty(config.getToolCode())) {
- return R.error("宸ュ叿缂栫爜涓嶈兘涓虹┖");
- }
- String usageScope = aiMcpPayloadMapper.normalizeUsageScope(config.getUsageScope());
- String sceneCode = resolveSceneCodeByUsageScope(usageScope);
- LambdaQueryWrapper<AiDiagnosticToolConfig> wrapper = new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, getTenantId())
- .eq(AiDiagnosticToolConfig::getToolCode, config.getToolCode());
- if (Cools.isEmpty(sceneCode)) {
- wrapper.and(w -> w.isNull(AiDiagnosticToolConfig::getSceneCode).or().eq(AiDiagnosticToolConfig::getSceneCode, ""));
- } else {
- wrapper.eq(AiDiagnosticToolConfig::getSceneCode, sceneCode);
- }
- AiDiagnosticToolConfig existed = aiDiagnosticToolConfigService.getOne(wrapper.last("limit 1"));
- Date now = new Date();
- AiDiagnosticToolConfig target = existed == null ? new AiDiagnosticToolConfig() : existed;
- if (existed == null) {
- target.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- target.setCreateBy(getLoginUserId());
- target.setCreateTime(now);
- }
- target
- .setToolCode(config.getToolCode())
- .setToolName(config.getToolName())
- .setSceneCode(sceneCode)
- .setUsageScope(usageScope)
- .setEnabledFlag(AiMcpConstants.USAGE_SCOPE_DISABLED.equals(usageScope) ? 0 : (config.getEnabledFlag() == null ? 1 : config.getEnabledFlag()))
- .setPriority(config.getPriority() == null ? 10 : config.getPriority())
- .setToolPrompt(config.getToolPrompt())
- .setStatus(config.getStatus() == null ? 1 : config.getStatus())
- .setTenantId(getTenantId())
- .setUpdateBy(getLoginUserId())
- .setUpdateTime(now)
- .setMemo(config.getMemo());
- if (existed == null) {
- aiDiagnosticToolConfigService.save(target);
- } else {
- aiDiagnosticToolConfigService.updateById(target);
- }
- return R.ok().add(buildBuiltInToolViews());
- }
-
- private List<Map<String, Object>> buildBuiltInToolViews() {
- List<Map<String, Object>> output = new java.util.ArrayList<>();
- for (com.vincent.rsf.server.ai.model.AiMcpToolDescriptor descriptor : aiMcpRegistryService.listInternalTools(getTenantId())) {
- Map<String, Object> item = new LinkedHashMap<>();
- item.put("toolCode", descriptor.getToolCode());
- item.put("toolName", descriptor.getToolName());
- item.put("description", descriptor.getDescription());
- item.put("enabledFlag", descriptor.getEnabledFlag());
- item.put("priority", descriptor.getPriority());
- item.put("toolPrompt", descriptor.getToolPrompt());
- item.put("usageScope", aiMcpPayloadMapper.resolveUsageScope(descriptor.getSceneCode(), descriptor.getEnabledFlag(), descriptor.getUsageScope()));
- item.put("advanced", buildAdvancedFlag(descriptor));
- output.add(item);
- }
- return output;
- }
-
- private Map<String, Object> buildMountView(AiMcpMount mount, boolean internalManaged) {
- Map<String, Object> item = new LinkedHashMap<>();
- item.put("id", mount.getId());
- item.put("name", mount.getName());
- item.put("mountCode", mount.getMountCode());
- item.put("transportType", mount.getTransportType());
- item.put("displayTransportType", AiMcpConstants.TRANSPORT_INTERNAL.equalsIgnoreCase(mount.getTransportType()) ? "鍐呯疆" : mount.getTransportType());
- item.put("url", mount.getUrl());
- item.put("enabledFlag", mount.getEnabledFlag());
- item.put("timeoutMs", mount.getTimeoutMs());
- item.put("lastTestResult", mount.getLastTestResult());
- item.put("lastTestResult$", mount.getLastTestResult$());
- item.put("lastTestTime", mount.getLastTestTime$());
- item.put("lastTestMessage", mount.getLastTestMessage());
- item.put("lastToolCount", mount.getLastToolCount());
- item.put("authType", mount.getAuthType());
- item.put("hasAuth", !Cools.isEmpty(mount.getAuthValue()));
- item.put("usageScope", normalizeUsageScope(mount.getUsageScope()));
- item.put("internalManaged", internalManaged);
- return item;
- }
-
- private boolean buildAdvancedFlag(com.vincent.rsf.server.ai.model.AiMcpToolDescriptor descriptor) {
- return descriptor.getPriority() != null && descriptor.getPriority() != 10
- || (descriptor.getToolPrompt() != null && !descriptor.getToolPrompt().trim().isEmpty());
- }
-
- private String normalizeUsageScope(String usageScope) {
- return aiMcpPayloadMapper.normalizeUsageScope(usageScope);
- }
-
- private String resolveUsageScope(String sceneCode, Integer enabledFlag) {
- return aiMcpPayloadMapper.resolveUsageScope(sceneCode, enabledFlag, null);
- }
-
- private String resolveSceneCodeByUsageScope(String usageScope) {
- return AiMcpConstants.USAGE_SCOPE_DIAGNOSE_ONLY.equals(usageScope) ? AiSceneCode.SYSTEM_DIAGNOSE : "";
- }
-
- private String generateMountCode(String name) {
- String normalized = name == null ? "remote_mcp" : name.toLowerCase().replaceAll("[^a-z0-9]+", "_");
- normalized = normalized.replaceAll("^_+|_+$", "");
- if (normalized.isEmpty()) {
- normalized = "remote_mcp";
- }
- return normalized + "_" + String.valueOf(System.currentTimeMillis()).substring(7);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiParamController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiParamController.java
deleted file mode 100644
index 28dbc42..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiParamController.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.KeyValVo;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiParam;
-import com.vincent.rsf.server.system.service.AiParamService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiParamController extends BaseController {
-
- @Autowired
- private AiParamService aiParamService;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @PostMapping("/aiParam/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiParam, BaseParam> pageParam = new PageParam<>(baseParam, AiParam.class);
- return R.ok().add(aiParamService.page(pageParam, pageParam.buildWrapper(true)));
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @PostMapping("/aiParam/list")
- public R list(@RequestBody Map<String, Object> map) {
- return R.ok().add(aiParamService.list());
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @PostMapping({"/aiParam/many/{ids}", "/aiParams/many/{ids}"})
- public R many(@PathVariable Long[] ids) {
- return R.ok().add(aiParamService.listByIds(Arrays.asList(ids)));
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @GetMapping("/aiParam/{id}")
- public R get(@PathVariable("id") Long id) {
- return R.ok().add(aiParamService.getById(id));
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:save')")
- @OperationLog("Create AiParam")
- @PostMapping("/aiParam/save")
- public R save(@RequestBody AiParam aiParam) {
- if (Cools.isEmpty(aiParam.getModelCode())) {
- return R.error("妯″瀷缂栫爜涓嶈兘涓虹┖");
- }
- if (aiParamService.existsModelCode(aiParam.getModelCode(), null)) {
- return R.error("妯″瀷缂栫爜宸插瓨鍦�");
- }
- Date now = new Date();
- aiParam.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- aiParam.setCreateBy(getLoginUserId());
- aiParam.setCreateTime(now);
- aiParam.setUpdateBy(getLoginUserId());
- aiParam.setUpdateTime(now);
- if (aiParam.getDefaultFlag() == null) {
- aiParam.setDefaultFlag(0);
- }
- if (!aiParamService.save(aiParam)) {
- return R.error("Save Fail");
- }
- if (Integer.valueOf(1).equals(aiParam.getDefaultFlag())) {
- aiParamService.resetDefaultFlag(aiParam.getId());
- }
- return R.ok("Save Success").add(aiParam);
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:update')")
- @OperationLog("Update AiParam")
- @PostMapping("/aiParam/update")
- public R update(@RequestBody AiParam aiParam) {
- if (Cools.isEmpty(aiParam.getModelCode())) {
- return R.error("妯″瀷缂栫爜涓嶈兘涓虹┖");
- }
- if (aiParamService.existsModelCode(aiParam.getModelCode(), aiParam.getId())) {
- return R.error("妯″瀷缂栫爜宸插瓨鍦�");
- }
- aiParam.setUpdateBy(getLoginUserId());
- aiParam.setUpdateTime(new Date());
- if (!aiParamService.updateById(aiParam)) {
- return R.error("Update Fail");
- }
- if (Integer.valueOf(1).equals(aiParam.getDefaultFlag())) {
- aiParamService.resetDefaultFlag(aiParam.getId());
- }
- return R.ok("Update Success").add(aiParam);
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:remove')")
- @OperationLog("Delete AiParam")
- @PostMapping("/aiParam/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- if (!aiParamService.removeByIds(Arrays.asList(ids))) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @PostMapping("/aiParam/query")
- public R query(@RequestParam(required = false) String condition) {
- List<KeyValVo> vos = new ArrayList<>();
- LambdaQueryWrapper<AiParam> wrapper = new LambdaQueryWrapper<>();
- if (!Cools.isEmpty(condition)) {
- wrapper.like(AiParam::getName, condition).or().like(AiParam::getModelCode, condition);
- }
- aiParamService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
- item -> vos.add(new KeyValVo(item.getId(), item.getName()))
- );
- return R.ok().add(vos);
- }
-
- @PreAuthorize("hasAuthority('system:aiParam:list')")
- @PostMapping("/aiParam/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiParamService.list(), AiParam.class), response);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiPromptController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiPromptController.java
deleted file mode 100644
index 0f254bc..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiPromptController.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiPromptPublishLog;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-import com.vincent.rsf.server.system.service.AiPromptPublishLogService;
-import com.vincent.rsf.server.system.service.AiPromptTemplateService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiPromptController extends BaseController {
-
- @Autowired
- private AiPromptTemplateService aiPromptTemplateService;
- @Autowired
- private AiPromptPublishLogService aiPromptPublishLogService;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @PostMapping("/aiPrompt/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiPromptTemplate, BaseParam> pageParam = new PageParam<>(baseParam, AiPromptTemplate.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiPromptTemplate> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiPromptTemplateService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @PostMapping("/aiPrompt/list")
- public R list(@RequestBody(required = false) Map<String, Object> map) {
- return R.ok().add(aiPromptTemplateService.list(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, getTenantId())
- .orderByAsc(AiPromptTemplate::getSceneCode)
- .orderByDesc(AiPromptTemplate::getVersionNo, AiPromptTemplate::getId)));
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @PostMapping({"/aiPrompt/many/{ids}", "/aiPrompts/many/{ids}"})
- public R many(@PathVariable Long[] ids) {
- return R.ok().add(aiPromptTemplateService.list(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, getTenantId())
- .in(AiPromptTemplate::getId, Arrays.asList(ids))));
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @GetMapping("/aiPrompt/{id}")
- public R get(@PathVariable("id") Long id) {
- AiPromptTemplate promptTemplate = aiPromptTemplateService.getTenantTemplate(getTenantId(), id);
- if (promptTemplate == null) {
- return R.error("record not found");
- }
- return R.ok().add(promptTemplate);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:save')")
- @OperationLog("Create AiPrompt")
- @PostMapping("/aiPrompt/save")
- public R save(@RequestBody AiPromptTemplate aiPromptTemplate) {
- if (Cools.isEmpty(aiPromptTemplate.getSceneCode())) {
- return R.error("鍦烘櫙缂栫爜涓嶈兘涓虹┖");
- }
- Date now = new Date();
- aiPromptTemplate.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- aiPromptTemplate.setTenantId(getTenantId());
- aiPromptTemplate.setCreateBy(getLoginUserId());
- aiPromptTemplate.setCreateTime(now);
- aiPromptTemplate.setUpdateBy(getLoginUserId());
- aiPromptTemplate.setUpdateTime(now);
- aiPromptTemplate.setVersionNo(aiPromptTemplate.getVersionNo() == null
- ? aiPromptTemplateService.nextVersionNo(getTenantId(), aiPromptTemplate.getSceneCode())
- : aiPromptTemplate.getVersionNo());
- if (aiPromptTemplate.getPublishedFlag() == null) {
- aiPromptTemplate.setPublishedFlag(0);
- }
- if (aiPromptTemplate.getStatus() == null) {
- aiPromptTemplate.setStatus(1);
- }
- if (!aiPromptTemplateService.save(aiPromptTemplate)) {
- return R.error("Save Fail");
- }
- return R.ok("Save Success").add(aiPromptTemplate);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:update')")
- @OperationLog("Update AiPrompt")
- @PostMapping("/aiPrompt/update")
- public R update(@RequestBody AiPromptTemplate aiPromptTemplate) {
- AiPromptTemplate existed = aiPromptTemplateService.getTenantTemplate(getTenantId(), aiPromptTemplate.getId());
- if (existed == null) {
- return R.error("record not found");
- }
- aiPromptTemplate.setTenantId(getTenantId());
- aiPromptTemplate.setUpdateBy(getLoginUserId());
- aiPromptTemplate.setUpdateTime(new Date());
- aiPromptTemplate.setCreateBy(existed.getCreateBy());
- aiPromptTemplate.setCreateTime(existed.getCreateTime());
- if (!aiPromptTemplateService.updateById(aiPromptTemplate)) {
- return R.error("Update Fail");
- }
- return R.ok("Update Success").add(aiPromptTemplate);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:remove')")
- @OperationLog("Delete AiPrompt")
- @PostMapping("/aiPrompt/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- List<Long> idList = Arrays.asList(ids);
- List<AiPromptTemplate> records = aiPromptTemplateService.list(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, getTenantId())
- .in(AiPromptTemplate::getId, idList));
- if (records.size() != idList.size() || !aiPromptTemplateService.removeByIds(idList)) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @PostMapping("/aiPrompt/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiPromptTemplateService.list(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, getTenantId())), AiPromptTemplate.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @GetMapping("/ai/prompt/list")
- public R customList(@RequestParam(required = false) String sceneCode) {
- LambdaQueryWrapper<AiPromptTemplate> wrapper = new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, getTenantId())
- .orderByAsc(AiPromptTemplate::getSceneCode)
- .orderByDesc(AiPromptTemplate::getVersionNo, AiPromptTemplate::getId);
- if (!Cools.isEmpty(sceneCode)) {
- wrapper.eq(AiPromptTemplate::getSceneCode, sceneCode);
- }
- return R.ok().add(aiPromptTemplateService.list(wrapper));
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiPrompt:save','system:aiPrompt:update')")
- @PostMapping("/ai/prompt/save")
- public R customSave(@RequestBody AiPromptTemplate aiPromptTemplate) {
- if (aiPromptTemplate.getId() == null) {
- if (!hasAuthority("system:aiPrompt:save")) {
- return R.error("鏃犳柊澧炴潈闄�");
- }
- return save(aiPromptTemplate);
- }
- if (!hasAuthority("system:aiPrompt:update")) {
- return R.error("鏃犳洿鏂版潈闄�");
- }
- return update(aiPromptTemplate);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:publish')")
- @OperationLog("Publish AiPrompt")
- @PostMapping("/ai/prompt/publish")
- public R publish(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- if (!aiPromptTemplateService.publishTemplate(getTenantId(), id, getLoginUserId())) {
- return R.error("record not found");
- }
- return R.ok();
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @GetMapping("/ai/prompt/version/list")
- public R versionList(@RequestParam String sceneCode) {
- List<AiPromptTemplate> list = aiPromptTemplateService.listVersions(getTenantId(), sceneCode);
- return R.ok().add(list);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @GetMapping("/ai/prompt/publish-log/list")
- public R publishLogList(@RequestParam(required = false) String sceneCode) {
- List<AiPromptPublishLog> list = aiPromptPublishLogService.listSceneLogs(getTenantId(), sceneCode);
- return R.ok().add(list);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:save')")
- @OperationLog("Copy AiPrompt")
- @PostMapping("/ai/prompt/copy")
- public R copy(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- AiPromptTemplate copied = aiPromptTemplateService.copyTemplate(getTenantId(), id, getLoginUserId());
- if (copied == null) {
- return R.error("record not found");
- }
- return R.ok().add(copied);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:list')")
- @GetMapping("/ai/prompt/compare")
- public R compare(@RequestParam Long leftId, @RequestParam Long rightId) {
- AiPromptTemplate left = aiPromptTemplateService.getTenantTemplate(getTenantId(), leftId);
- AiPromptTemplate right = aiPromptTemplateService.getTenantTemplate(getTenantId(), rightId);
- if (left == null || right == null) {
- return R.error("record not found");
- }
- Map<String, Object> result = new LinkedHashMap<>();
- result.put("left", left);
- result.put("right", right);
- return R.ok().add(result);
- }
-
- @PreAuthorize("hasAuthority('system:aiPrompt:publish')")
- @OperationLog("Rollback AiPrompt")
- @PostMapping("/ai/prompt/rollback")
- public R rollback(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- if (!aiPromptTemplateService.rollbackTemplate(getTenantId(), id, getLoginUserId())) {
- return R.error("record not found");
- }
- return R.ok();
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiRouteController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiRouteController.java
deleted file mode 100644
index 697011f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/AiRouteController.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package com.vincent.rsf.server.system.controller;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.framework.common.SnowflakeIdWorker;
-import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
-import com.vincent.rsf.server.common.annotation.OperationLog;
-import com.vincent.rsf.server.common.domain.BaseParam;
-import com.vincent.rsf.server.common.domain.PageParam;
-import com.vincent.rsf.server.common.utils.ExcelUtil;
-import com.vincent.rsf.server.system.entity.AiModelRoute;
-import com.vincent.rsf.server.system.service.AiModelRouteService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-@RestController
-public class AiRouteController extends BaseController {
-
- @Autowired
- private AiModelRouteService aiModelRouteService;
- @Autowired
- private AiModelRouteRuntimeService aiModelRouteRuntimeService;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
-
- @PreAuthorize("hasAuthority('system:aiRoute:list')")
- @PostMapping("/aiRoute/page")
- public R page(@RequestBody Map<String, Object> map) {
- BaseParam baseParam = buildParam(map, BaseParam.class);
- PageParam<AiModelRoute, BaseParam> pageParam = new PageParam<>(baseParam, AiModelRoute.class);
- com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<AiModelRoute> wrapper = pageParam.buildWrapper(true);
- wrapper.eq("tenant_id", getTenantId());
- return R.ok().add(aiModelRouteService.page(pageParam, wrapper));
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:list')")
- @GetMapping("/aiRoute/{id}")
- public R get(@PathVariable("id") Long id) {
- AiModelRoute route = aiModelRouteService.getTenantRoute(getTenantId(), id);
- if (route == null) {
- return R.error("route not found");
- }
- return R.ok().add(route);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:save')")
- @OperationLog("Create AiRoute")
- @PostMapping("/aiRoute/save")
- public R save(@RequestBody AiModelRoute aiModelRoute) {
- if (Cools.isEmpty(aiModelRoute.getRouteCode()) || Cools.isEmpty(aiModelRoute.getModelCode())) {
- return R.error("璺敱缂栫爜鍜屾ā鍨嬬紪鐮佷笉鑳戒负绌�");
- }
- Date now = new Date();
- aiModelRoute.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
- aiModelRoute.setTenantId(getTenantId());
- aiModelRoute.setCreateBy(getLoginUserId());
- aiModelRoute.setCreateTime(now);
- aiModelRoute.setUpdateBy(getLoginUserId());
- aiModelRoute.setUpdateTime(now);
- if (aiModelRoute.getPriority() == null) {
- aiModelRoute.setPriority(1);
- }
- if (aiModelRoute.getStatus() == null) {
- aiModelRoute.setStatus(1);
- }
- if (aiModelRoute.getFailCount() == null) {
- aiModelRoute.setFailCount(0);
- }
- if (aiModelRoute.getSuccessCount() == null) {
- aiModelRoute.setSuccessCount(0);
- }
- if (!aiModelRouteService.save(aiModelRoute)) {
- return R.error("Save Fail");
- }
- return R.ok("Save Success").add(aiModelRoute);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:update')")
- @OperationLog("Update AiRoute")
- @PostMapping("/aiRoute/update")
- public R update(@RequestBody AiModelRoute aiModelRoute) {
- AiModelRoute existed = aiModelRouteService.getTenantRoute(getTenantId(), aiModelRoute.getId());
- if (existed == null) {
- return R.error("route not found");
- }
- aiModelRoute.setTenantId(getTenantId());
- aiModelRoute.setUpdateBy(getLoginUserId());
- aiModelRoute.setUpdateTime(new Date());
- aiModelRoute.setCreateBy(existed.getCreateBy());
- aiModelRoute.setCreateTime(existed.getCreateTime());
- if (!aiModelRouteService.updateById(aiModelRoute)) {
- return R.error("Update Fail");
- }
- return R.ok("Update Success").add(aiModelRoute);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:remove')")
- @OperationLog("Delete AiRoute")
- @PostMapping("/aiRoute/remove/{ids}")
- public R remove(@PathVariable Long[] ids) {
- List<Long> idList = Arrays.asList(ids);
- List<AiModelRoute> routes = aiModelRouteService.list(new LambdaQueryWrapper<AiModelRoute>()
- .eq(AiModelRoute::getTenantId, getTenantId())
- .in(AiModelRoute::getId, idList));
- if (routes.size() != idList.size() || !aiModelRouteService.removeByIds(idList)) {
- return R.error("Delete Fail");
- }
- return R.ok("Delete Success").add(ids);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:list')")
- @PostMapping("/aiRoute/export")
- public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
- ExcelUtil.build(ExcelUtil.create(aiModelRouteService.list(new LambdaQueryWrapper<AiModelRoute>()
- .eq(AiModelRoute::getTenantId, getTenantId())), AiModelRoute.class), response);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:list')")
- @GetMapping("/ai/route/list")
- public R customList(@RequestParam(required = false) String routeCode) {
- LambdaQueryWrapper<AiModelRoute> wrapper = new LambdaQueryWrapper<AiModelRoute>()
- .eq(AiModelRoute::getTenantId, getTenantId())
- .orderByAsc(AiModelRoute::getRouteCode, AiModelRoute::getPriority, AiModelRoute::getId);
- if (!Cools.isEmpty(routeCode)) {
- wrapper.eq(AiModelRoute::getRouteCode, routeCode);
- }
- return R.ok().add(aiModelRouteService.list(wrapper));
- }
-
- @PreAuthorize("hasAnyAuthority('system:aiRoute:save','system:aiRoute:update')")
- @PostMapping("/ai/route/save")
- public R customSave(@RequestBody AiModelRoute aiModelRoute) {
- if (aiModelRoute.getId() == null) {
- if (!hasAuthority("system:aiRoute:save")) {
- return R.error("鏃犳柊澧炴潈闄�");
- }
- return save(aiModelRoute);
- }
- if (!hasAuthority("system:aiRoute:update")) {
- return R.error("鏃犳洿鏂版潈闄�");
- }
- return update(aiModelRoute);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:update')")
- @OperationLog("Toggle AiRoute")
- @PostMapping("/ai/route/toggle")
- public R toggle(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- Integer status = Integer.valueOf(String.valueOf(map.get("status")));
- AiModelRoute route = aiModelRouteService.getTenantRoute(getTenantId(), id);
- if (route == null) {
- return R.error("route not found");
- }
- route.setStatus(status);
- route.setUpdateBy(getLoginUserId());
- route.setUpdateTime(new Date());
- aiModelRouteService.updateById(route);
- return R.ok().add(route);
- }
-
- @PreAuthorize("hasAuthority('system:aiRoute:update')")
- @OperationLog("Reset AiRoute")
- @PostMapping("/ai/route/reset")
- public R reset(@RequestBody Map<String, Object> map) {
- Long id = Long.valueOf(String.valueOf(map.get("id")));
- if (aiModelRouteService.getTenantRoute(getTenantId(), id) == null) {
- return R.error("route not found");
- }
- aiModelRouteRuntimeService.resetRoute(id);
- return R.ok();
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiCallLog.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiCallLog.java
deleted file mode 100644
index d9d9faf..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiCallLog.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.SpringUtils;
-import com.vincent.rsf.server.system.service.TenantService;
-import com.vincent.rsf.server.system.service.UserService;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_call_log")
-public class AiCallLog implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "ID")
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value = "浼氳瘽ID")
- private String sessionId;
-
- @ApiModelProperty(value = "璇婃柇璁板綍ID")
- private Long diagnosisId;
-
- @ApiModelProperty(value = "璺敱缂栫爜")
- private String routeCode;
-
- @ApiModelProperty(value = "妯″瀷缂栫爜")
- private String modelCode;
-
- @ApiModelProperty(value = "灏濊瘯搴忓彿")
- private Integer attemptNo;
-
- @ApiModelProperty(value = "璇锋眰鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date requestTime;
-
- @ApiModelProperty(value = "鍝嶅簲鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date responseTime;
-
- @ApiModelProperty(value = "鑰楁椂姣")
- private Long spendTime;
-
- @ApiModelProperty(value = "缁撴灉 1:鎴愬姛 0:澶辫触")
- private Integer result;
-
- @ApiModelProperty(value = "閿欒淇℃伅")
- private String err;
-
- @ApiModelProperty(value = "鐘舵�� 1:姝e父 0:鍐荤粨")
- private Integer status;
-
- @ApiModelProperty(value = "鏄惁鍒犻櫎 1:鏄� 0:鍚�")
- @TableLogic
- private Integer deleted;
-
- @ApiModelProperty(value = "绉熸埛")
- private Long tenantId;
-
- @ApiModelProperty(value = "鐢ㄦ埛")
- private Long userId;
-
- @ApiModelProperty(value = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- public String getResult$() {
- if (this.result == null) {
- return null;
- }
- return Integer.valueOf(1).equals(this.result) ? "鎴愬姛" : "澶辫触";
- }
-
- public String getUserId$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.userId);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getTenantId$() {
- TenantService service = SpringUtils.getBean(TenantService.class);
- Tenant tenant = service.getById(this.tenantId);
- if (!Cools.isEmpty(tenant)) {
- return String.valueOf(tenant.getName());
- }
- return null;
- }
-
- public String getRequestTime$() {
- if (Cools.isEmpty(this.requestTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.requestTime);
- }
-
- public String getResponseTime$() {
- if (Cools.isEmpty(this.responseTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.responseTime);
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisPlan.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisPlan.java
deleted file mode 100644
index 7e020e1..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisPlan.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_diagnosis_plan")
-public class AiDiagnosisPlan implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty("缂栧彿")
- private String uuid;
-
- @ApiModelProperty("璁″垝鍚嶇О")
- private String planName;
-
- @ApiModelProperty("鍦烘櫙缂栫爜")
- private String sceneCode;
-
- @ApiModelProperty("Cron琛ㄨ揪寮�")
- private String cronExpr;
-
- @ApiModelProperty("宸℃鎻愮ず璇�")
- private String prompt;
-
- @ApiModelProperty("浼樺厛妯″瀷缂栫爜")
- private String preferredModelCode;
-
- @ApiModelProperty("杩愯涓� 1:鏄� 0:鍚�")
- private Integer runningFlag;
-
- @ApiModelProperty("涓婃缁撴灉 2:杩愯涓� 1:鎴愬姛 0:澶辫触")
- private Integer lastResult;
-
- @ApiModelProperty("涓婃璇婃柇璁板綍ID")
- private Long lastDiagnosisId;
-
- @ApiModelProperty("涓婃杩愯鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date lastRunTime;
-
- @ApiModelProperty("涓嬫杩愯鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date nextRunTime;
-
- @ApiModelProperty("鏈�杩戞秷鎭�")
- private String lastMessage;
-
- @ApiModelProperty("鐘舵�� 1:姝e父 0:鍐荤粨")
- private Integer status;
-
- @TableLogic
- @ApiModelProperty("鏄惁鍒犻櫎 1:鏄� 0:鍚�")
- private Integer deleted;
-
- private Long tenantId;
- private Long createBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- private Long updateBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- private String memo;
-
- public Boolean getStatusBool() {
- if (status == null) {
- return null;
- }
- return Integer.valueOf(1).equals(status);
- }
-
- public Boolean getRunningFlagBool() {
- if (runningFlag == null) {
- return null;
- }
- return Integer.valueOf(1).equals(runningFlag);
- }
-
- public String getLastResult$() {
- if (lastResult == null) {
- return "鏈繍琛�";
- }
- if (Integer.valueOf(2).equals(lastResult)) {
- return "杩愯涓�";
- }
- if (Integer.valueOf(1).equals(lastResult)) {
- return "鎴愬姛";
- }
- if (Integer.valueOf(0).equals(lastResult)) {
- return "澶辫触";
- }
- return String.valueOf(lastResult);
- }
-
- public String getSceneCode$() {
- if ("system_diagnose".equals(sceneCode)) {
- return "绯荤粺璇婃柇";
- }
- if ("general_chat".equals(sceneCode)) {
- return "閫氱敤瀵硅瘽";
- }
- return sceneCode;
- }
-
- public String getLastRunTime$() {
- if (Cools.isEmpty(lastRunTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(lastRunTime);
- }
-
- public String getNextRunTime$() {
- if (Cools.isEmpty(nextRunTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nextRunTime);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisRecord.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisRecord.java
deleted file mode 100644
index 15a5b76..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosisRecord.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.SpringUtils;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.system.service.TenantService;
-import com.vincent.rsf.server.system.service.UserService;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_diagnosis_record")
-public class AiDiagnosisRecord implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "ID")
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value = "璇婃柇缂栧彿")
- private String diagnosisNo;
-
- @ApiModelProperty(value = "浼氳瘽ID")
- private String sessionId;
-
- @ApiModelProperty(value = "鍦烘櫙缂栫爜")
- private String sceneCode;
-
- @ApiModelProperty(value = "闂")
- private String question;
-
- @ApiModelProperty(value = "缁撹")
- private String conclusion;
-
- @ApiModelProperty(value = "鎶ュ憡鏍囬")
- private String reportTitle;
-
- @ApiModelProperty(value = "鎵ц鎽樿")
- private String executiveSummary;
-
- @ApiModelProperty(value = "璇佹嵁鎽樿")
- private String evidenceSummary;
-
- @ApiModelProperty(value = "寤鸿鍔ㄤ綔")
- private String actionSummary;
-
- @ApiModelProperty(value = "椋庨櫓璇勪及")
- private String riskSummary;
-
- @ApiModelProperty(value = "鎶ュ憡Markdown")
- private String reportMarkdown;
-
- @ApiModelProperty(value = "宸ュ叿鎽樿")
- private String toolSummary;
-
- @ApiModelProperty(value = "妯″瀷缂栫爜")
- private String modelCode;
-
- @ApiModelProperty(value = "缁撴灉 2:杩愯涓� 1:鎴愬姛 0:澶辫触")
- private Integer result;
-
- @ApiModelProperty(value = "閿欒淇℃伅")
- private String err;
-
- @ApiModelProperty(value = "寮�濮嬫椂闂�")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date startTime;
-
- @ApiModelProperty(value = "缁撴潫鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date endTime;
-
- @ApiModelProperty(value = "鑰楁椂姣")
- private Long spendTime;
-
- @ApiModelProperty(value = "鐘舵�� 1:姝e父 0:鍐荤粨")
- private Integer status;
-
- @ApiModelProperty(value = "鏄惁鍒犻櫎 1:鏄� 0:鍚�")
- @TableLogic
- private Integer deleted;
-
- @ApiModelProperty(value = "绉熸埛")
- private Long tenantId;
-
- @ApiModelProperty(value = "鐢ㄦ埛")
- private Long userId;
-
- @ApiModelProperty(value = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- @ApiModelProperty(value = "鏇存柊鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- public String getSceneCode$() {
- if (AiSceneCode.SYSTEM_DIAGNOSE.equals(this.sceneCode)) {
- return "绯荤粺璇婃柇";
- }
- if (AiSceneCode.GENERAL_CHAT.equals(this.sceneCode)) {
- return "閫氱敤瀵硅瘽";
- }
- return this.sceneCode;
- }
-
- public String getResult$() {
- if (this.result == null) {
- return null;
- }
- if (Integer.valueOf(2).equals(this.result)) {
- return "杩愯涓�";
- }
- if (Integer.valueOf(1).equals(this.result)) {
- return "鎴愬姛";
- }
- if (Integer.valueOf(0).equals(this.result)) {
- return "澶辫触";
- }
- return String.valueOf(this.result);
- }
-
- public String getUserId$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.userId);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getTenantId$() {
- TenantService service = SpringUtils.getBean(TenantService.class);
- Tenant tenant = service.getById(this.tenantId);
- if (!Cools.isEmpty(tenant)) {
- return String.valueOf(tenant.getName());
- }
- return null;
- }
-
- public String getStartTime$() {
- if (Cools.isEmpty(this.startTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.startTime);
- }
-
- public String getEndTime$() {
- if (Cools.isEmpty(this.endTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.endTime);
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosticToolConfig.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosticToolConfig.java
deleted file mode 100644
index 37fa2b0..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiDiagnosticToolConfig.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_diagnostic_tool_config")
-public class AiDiagnosticToolConfig implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty("缂栧彿")
- private String uuid;
-
- @ApiModelProperty("鍦烘櫙缂栫爜")
- private String sceneCode;
-
- @ApiModelProperty("宸ュ叿缂栫爜")
- private String toolCode;
-
- @ApiModelProperty("宸ュ叿鍚嶇О")
- private String toolName;
-
- @ApiModelProperty("鍚敤 1:鏄� 0:鍚�")
- private Integer enabledFlag;
-
- @ApiModelProperty("浼樺厛绾�")
- private Integer priority;
-
- @ApiModelProperty("闄勫姞鎻愮ず璇�")
- private String toolPrompt;
-
- @ApiModelProperty("鐢ㄩ�旇寖鍥�")
- private String usageScope;
-
- @ApiModelProperty("鐘舵��")
- private Integer status;
-
- @TableLogic
- private Integer deleted;
-
- private Long tenantId;
- private Long createBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- private Long updateBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- private String memo;
-
- public Boolean getEnabledFlagBool() {
- if (enabledFlag == null) {
- return null;
- }
- return Integer.valueOf(1).equals(enabledFlag);
- }
-
- public Boolean getStatusBool() {
- if (status == null) {
- return null;
- }
- return Integer.valueOf(1).equals(status);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiMcpMount.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiMcpMount.java
deleted file mode 100644
index de8da08..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiMcpMount.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_mcp_mount")
-public class AiMcpMount implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty("缂栧彿")
- private String uuid;
-
- @ApiModelProperty("鍚嶇О")
- private String name;
-
- @ApiModelProperty("鎸傝浇缂栫爜")
- private String mountCode;
-
- @ApiModelProperty("浼犺緭绫诲瀷")
- private String transportType;
-
- @ApiModelProperty("鍦板潃")
- private String url;
-
- @ApiModelProperty("璁よ瘉鏂瑰紡")
- private String authType;
-
- @ApiModelProperty("璁よ瘉鍊�")
- private String authValue;
-
- @ApiModelProperty("鐢ㄩ�旇寖鍥�")
- private String usageScope;
-
- @ApiModelProperty("鍚敤 1:鏄� 0:鍚�")
- private Integer enabledFlag;
-
- @ApiModelProperty("瓒呮椂姣")
- private Integer timeoutMs;
-
- @ApiModelProperty("涓婃娴嬭瘯缁撴灉 1:鎴愬姛 0:澶辫触")
- private Integer lastTestResult;
-
- @ApiModelProperty("涓婃娴嬭瘯鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date lastTestTime;
-
- @ApiModelProperty("涓婃娴嬭瘯娑堟伅")
- private String lastTestMessage;
-
- @ApiModelProperty("涓婃宸ュ叿鏁�")
- private Integer lastToolCount;
-
- @ApiModelProperty("鐘舵�� 1:姝e父 0:鍐荤粨")
- private Integer status;
-
- @TableLogic
- private Integer deleted;
-
- private Long tenantId;
- private Long createBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- private Long updateBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- private String memo;
-
- public Boolean getEnabledFlagBool() {
- if (enabledFlag == null) {
- return null;
- }
- return Integer.valueOf(1).equals(enabledFlag);
- }
-
- public String getLastTestResult$() {
- if (lastTestResult == null) {
- return "鏈祴璇�";
- }
- return Integer.valueOf(1).equals(lastTestResult) ? "鎴愬姛" : "澶辫触";
- }
-
- public String getLastTestTime$() {
- if (Cools.isEmpty(lastTestTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(lastTestTime);
- }
-
- public Boolean getInternalManaged() {
- return "INTERNAL".equalsIgnoreCase(transportType);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiModelRoute.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiModelRoute.java
deleted file mode 100644
index 129facb..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiModelRoute.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.SpringUtils;
-import com.vincent.rsf.server.system.service.TenantService;
-import com.vincent.rsf.server.system.service.UserService;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_model_route")
-public class AiModelRoute implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "ID")
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value = "缂栧彿")
- private String uuid;
-
- @ApiModelProperty(value = "璺敱缂栫爜")
- private String routeCode;
-
- @ApiModelProperty(value = "妯″瀷缂栫爜")
- private String modelCode;
-
- @ApiModelProperty(value = "浼樺厛绾�")
- private Integer priority;
-
- @ApiModelProperty(value = "鍐峰嵈鎴鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date cooldownUntil;
-
- @ApiModelProperty(value = "澶辫触娆℃暟")
- private Integer failCount;
-
- @ApiModelProperty(value = "鎴愬姛娆℃暟")
- private Integer successCount;
-
- @ApiModelProperty(value = "鐘舵�� 1:鍚敤 0:鍋滅敤")
- private Integer status;
-
- @ApiModelProperty(value = "鏄惁鍒犻櫎 1:鏄� 0:鍚�")
- @TableLogic
- private Integer deleted;
-
- @ApiModelProperty(value = "绉熸埛")
- private Long tenantId;
-
- @ApiModelProperty(value = "娣诲姞浜哄憳")
- private Long createBy;
-
- @ApiModelProperty(value = "娣诲姞鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- @ApiModelProperty(value = "淇敼浜哄憳")
- private Long updateBy;
-
- @ApiModelProperty(value = "淇敼鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- @ApiModelProperty(value = "澶囨敞")
- private String memo;
-
- public Boolean getStatusBool() {
- if (this.status == null) {
- return null;
- }
- return Integer.valueOf(1).equals(this.status);
- }
-
- public String getCooldownUntil$() {
- if (Cools.isEmpty(this.cooldownUntil)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.cooldownUntil);
- }
-
- public String getTenantId$() {
- TenantService service = SpringUtils.getBean(TenantService.class);
- Tenant tenant = service.getById(this.tenantId);
- if (!Cools.isEmpty(tenant)) {
- return String.valueOf(tenant.getName());
- }
- return null;
- }
-
- public String getCreateBy$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.createBy);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getCreateTime$() {
- if (Cools.isEmpty(this.createTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
- }
-
- public String getUpdateBy$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.updateBy);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getUpdateTime$() {
- if (Cools.isEmpty(this.updateTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiParam.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiParam.java
deleted file mode 100644
index 77c1f3f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiParam.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.SpringUtils;
-import com.vincent.rsf.server.system.service.TenantService;
-import com.vincent.rsf.server.system.service.UserService;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_param")
-public class AiParam implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value= "ID")
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value= "缂栧彿")
- private String uuid;
-
- @ApiModelProperty(value= "鍚嶇О")
- private String name;
-
- @ApiModelProperty(value= "妯″瀷缂栫爜")
- private String modelCode;
-
- @ApiModelProperty(value= "渚涘簲鍟�")
- private String provider;
-
- @ApiModelProperty(value= "鑱婂ぉ鍦板潃")
- private String chatUrl;
-
- @ApiModelProperty(value= "API瀵嗛挜")
- private String apiKey;
-
- @ApiModelProperty(value= "妯″瀷鍚嶇О")
- private String modelName;
-
- @ApiModelProperty(value= "绯荤粺鎻愮ず璇�")
- private String systemPrompt;
-
- @ApiModelProperty(value= "涓婁笅鏂囪疆鏁�")
- private Integer maxContextMessages;
-
- @ApiModelProperty(value= "榛樿妯″瀷 1: 鏄� 0: 鍚�")
- private Integer defaultFlag;
-
- @ApiModelProperty(value= "鎺掑簭")
- private Integer sort;
-
- @ApiModelProperty(value= "鐘舵�� 1: 姝e父 0: 鍐荤粨 ")
- private Integer status;
-
- @ApiModelProperty(value= "鏄惁鍒犻櫎 1: 鏄� 0: 鍚� ")
- private Integer deleted;
-
- @ApiModelProperty(value= "绉熸埛")
- private Long tenantId;
-
- @ApiModelProperty(value= "娣诲姞浜哄憳")
- private Long createBy;
-
- @ApiModelProperty(value= "娣诲姞鏃堕棿")
- @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
- private Date createTime;
-
- @ApiModelProperty(value= "淇敼浜哄憳")
- private Long updateBy;
-
- @ApiModelProperty(value= "淇敼鏃堕棿")
- @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
- private Date updateTime;
-
- @ApiModelProperty(value= "澶囨敞")
- private String memo;
-
- public String getTenantId$(){
- TenantService service = SpringUtils.getBean(TenantService.class);
- Tenant tenant = service.getById(this.tenantId);
- if (!Cools.isEmpty(tenant)){
- return String.valueOf(tenant.getName());
- }
- return null;
- }
-
- public String getCreateBy$(){
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.createBy);
- if (!Cools.isEmpty(user)){
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getCreateTime$(){
- if (Cools.isEmpty(this.createTime)){
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
- }
-
- public String getUpdateBy$(){
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.updateBy);
- if (!Cools.isEmpty(user)){
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getUpdateTime$(){
- if (Cools.isEmpty(this.updateTime)){
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
- }
-
- public Boolean getStatusBool(){
- if (null == this.status){ return null; }
- switch (this.status){
- case 1:
- return true;
- case 0:
- return false;
- default:
- return null;
- }
- }
-
- public Boolean getDefaultFlagBool(){
- if (null == this.defaultFlag){ return null; }
- switch (this.defaultFlag){
- case 1:
- return true;
- case 0:
- return false;
- default:
- return null;
- }
- }
-
- public String getDefaultFlag$(){
- if (null == this.defaultFlag){ return null; }
- switch (this.defaultFlag){
- case 1:
- return "鏄�";
- case 0:
- return "鍚�";
- default:
- return String.valueOf(this.defaultFlag);
- }
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptPublishLog.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptPublishLog.java
deleted file mode 100644
index d4397fc..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptPublishLog.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_prompt_publish_log")
-public class AiPromptPublishLog implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty("Prompt妯℃澘ID")
- private Long promptTemplateId;
-
- @ApiModelProperty("鍦烘櫙缂栫爜")
- private String sceneCode;
-
- @ApiModelProperty("妯℃澘鍚嶇О")
- private String templateName;
-
- @ApiModelProperty("鐗堟湰鍙�")
- private Integer versionNo;
-
- @ApiModelProperty("鍔ㄤ綔")
- private String actionType;
-
- @ApiModelProperty("鍔ㄤ綔鎻忚堪")
- private String actionDesc;
-
- private Long tenantId;
- private Long createBy;
-
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptTemplate.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptTemplate.java
deleted file mode 100644
index 3a9328f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/AiPromptTemplate.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.vincent.rsf.server.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.vincent.rsf.framework.common.Cools;
-import com.vincent.rsf.framework.common.SpringUtils;
-import com.vincent.rsf.server.ai.constant.AiSceneCode;
-import com.vincent.rsf.server.system.service.TenantService;
-import com.vincent.rsf.server.system.service.UserService;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-@Data
-@Accessors(chain = true)
-@TableName("sys_ai_prompt_template")
-public class AiPromptTemplate implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "ID")
- @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- @ApiModelProperty(value = "缂栧彿")
- private String uuid;
-
- @ApiModelProperty(value = "鍦烘櫙缂栫爜")
- private String sceneCode;
-
- @ApiModelProperty(value = "妯℃澘鍚嶇О")
- private String templateName;
-
- @ApiModelProperty(value = "鍩虹鎻愮ず璇�")
- private String basePrompt;
-
- @ApiModelProperty(value = "宸ュ叿鎻愮ず璇�")
- private String toolPrompt;
-
- @ApiModelProperty(value = "杈撳嚭鎻愮ず璇�")
- private String outputPrompt;
-
- @ApiModelProperty(value = "鐗堟湰鍙�")
- private Integer versionNo;
-
- @ApiModelProperty(value = "宸插彂甯� 1:鏄� 0:鍚�")
- private Integer publishedFlag;
-
- @ApiModelProperty(value = "鐘舵�� 1:姝e父 0:鍐荤粨")
- private Integer status;
-
- @ApiModelProperty(value = "鏄惁鍒犻櫎 1:鏄� 0:鍚�")
- @TableLogic
- private Integer deleted;
-
- @ApiModelProperty(value = "绉熸埛")
- private Long tenantId;
-
- @ApiModelProperty(value = "娣诲姞浜哄憳")
- private Long createBy;
-
- @ApiModelProperty(value = "娣诲姞鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date createTime;
-
- @ApiModelProperty(value = "淇敼浜哄憳")
- private Long updateBy;
-
- @ApiModelProperty(value = "淇敼鏃堕棿")
- @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- private Date updateTime;
-
- @ApiModelProperty(value = "澶囨敞")
- private String memo;
-
- public String getSceneCode$() {
- if (AiSceneCode.SYSTEM_DIAGNOSE.equals(this.sceneCode)) {
- return "绯荤粺璇婃柇";
- }
- if (AiSceneCode.GENERAL_CHAT.equals(this.sceneCode)) {
- return "閫氱敤瀵硅瘽";
- }
- return this.sceneCode;
- }
-
- public Boolean getPublishedFlagBool() {
- if (this.publishedFlag == null) {
- return null;
- }
- return Integer.valueOf(1).equals(this.publishedFlag);
- }
-
- public String getPublishedFlag$() {
- if (this.publishedFlag == null) {
- return null;
- }
- return Integer.valueOf(1).equals(this.publishedFlag) ? "宸插彂甯�" : "鑽夌";
- }
-
- public Boolean getStatusBool() {
- if (this.status == null) {
- return null;
- }
- return Integer.valueOf(1).equals(this.status);
- }
-
- public String getTenantId$() {
- TenantService service = SpringUtils.getBean(TenantService.class);
- Tenant tenant = service.getById(this.tenantId);
- if (!Cools.isEmpty(tenant)) {
- return String.valueOf(tenant.getName());
- }
- return null;
- }
-
- public String getCreateBy$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.createBy);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getCreateTime$() {
- if (Cools.isEmpty(this.createTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
- }
-
- public String getUpdateBy$() {
- UserService service = SpringUtils.getBean(UserService.class);
- User user = service.getById(this.updateBy);
- if (!Cools.isEmpty(user)) {
- return String.valueOf(user.getNickname());
- }
- return null;
- }
-
- public String getUpdateTime$() {
- if (Cools.isEmpty(this.updateTime)) {
- return "";
- }
- return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
- }
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiCallLogMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiCallLogMapper.java
deleted file mode 100644
index 67ce8bc..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiCallLogMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiCallLogMapper extends BaseMapper<AiCallLog> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisPlanMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisPlanMapper.java
deleted file mode 100644
index 416eb4c..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisPlanMapper.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface AiDiagnosisPlanMapper extends BaseMapper<AiDiagnosisPlan> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisRecordMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisRecordMapper.java
deleted file mode 100644
index 50ddc99..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosisRecordMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiDiagnosisRecordMapper extends BaseMapper<AiDiagnosisRecord> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosticToolConfigMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosticToolConfigMapper.java
deleted file mode 100644
index 2ceae2d..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiDiagnosticToolConfigMapper.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface AiDiagnosticToolConfigMapper extends BaseMapper<AiDiagnosticToolConfig> {
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiMcpMountMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiMcpMountMapper.java
deleted file mode 100644
index 6405d51..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiMcpMountMapper.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface AiMcpMountMapper extends BaseMapper<AiMcpMount> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiModelRouteMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiModelRouteMapper.java
deleted file mode 100644
index c981807..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiModelRouteMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiModelRoute;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiModelRouteMapper extends BaseMapper<AiModelRoute> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiParamMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiParamMapper.java
deleted file mode 100644
index 01fdd6a..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiParamMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiParam;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiParamMapper extends BaseMapper<AiParam> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptPublishLogMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptPublishLogMapper.java
deleted file mode 100644
index 4430396..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptPublishLogMapper.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiPromptPublishLog;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface AiPromptPublishLogMapper extends BaseMapper<AiPromptPublishLog> {
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptTemplateMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptTemplateMapper.java
deleted file mode 100644
index 7dd04c7..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/AiPromptTemplateMapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
-
-@Mapper
-@Repository
-public interface AiPromptTemplateMapper extends BaseMapper<AiPromptTemplate> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiCallLogService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiCallLogService.java
deleted file mode 100644
index 2672620..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiCallLogService.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-
-public interface AiCallLogService extends IService<AiCallLog> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisPlanService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisPlanService.java
deleted file mode 100644
index 68edc08..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisPlanService.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-
-import java.util.Date;
-import java.util.List;
-
-public interface AiDiagnosisPlanService extends IService<AiDiagnosisPlan> {
-
- AiDiagnosisPlan getTenantPlan(Long tenantId, Long id);
-
- List<AiDiagnosisPlan> listDuePlans(Date now);
-
- Date calculateNextRunTime(String cronExpr, Date after);
-
- boolean validateCron(String cronExpr);
-
- boolean acquireForExecution(Long id, Long operatorId, String lastMessage, Date nextRunTime);
-
- void finishExecution(Long id, Integer lastResult, Long lastDiagnosisId, String lastMessage, Date finishTime, Date nextRunTime);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisRecordService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisRecordService.java
deleted file mode 100644
index b4ee1a1..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosisRecordService.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-
-public interface AiDiagnosisRecordService extends IService<AiDiagnosisRecord> {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosticToolConfigService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosticToolConfigService.java
deleted file mode 100644
index dc23035..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiDiagnosticToolConfigService.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-
-import java.util.List;
-
-public interface AiDiagnosticToolConfigService extends IService<AiDiagnosticToolConfig> {
-
- List<AiDiagnosticToolConfig> listTenantConfigs(Long tenantId);
-
- List<AiDiagnosticToolConfig> listSceneConfigs(Long tenantId, String sceneCode);
-
- AiDiagnosticToolConfig getTenantConfig(Long tenantId, Long id);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiMcpMountService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiMcpMountService.java
deleted file mode 100644
index 7962a97..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiMcpMountService.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-
-import java.util.List;
-
-public interface AiMcpMountService extends IService<AiMcpMount> {
-
- List<AiMcpMount> listTenantMounts(Long tenantId);
-
- AiMcpMount getTenantMount(Long tenantId, Long id);
-
- AiMcpMount getTenantMountByCode(Long tenantId, String mountCode);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiModelRouteService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiModelRouteService.java
deleted file mode 100644
index 22b84e5..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiModelRouteService.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiModelRoute;
-
-import java.util.List;
-
-public interface AiModelRouteService extends IService<AiModelRoute> {
-
- AiModelRoute getTenantRoute(Long tenantId, Long id);
-
- List<AiModelRoute> listAvailableRoutes(Long tenantId, String routeCode);
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiParamService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiParamService.java
deleted file mode 100644
index 8bbd05d..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiParamService.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiParam;
-
-import java.util.List;
-
-public interface AiParamService extends IService<AiParam> {
-
- boolean existsModelCode(String modelCode, Long excludeId);
-
- void resetDefaultFlag(Long excludeId);
-
- List<AiParam> listEnabledModels();
-
- AiParam getEnabledModel(String modelCode);
-
- AiParam getDefaultModel();
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptPublishLogService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptPublishLogService.java
deleted file mode 100644
index f44f71e..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptPublishLogService.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiPromptPublishLog;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-
-import java.util.List;
-
-public interface AiPromptPublishLogService extends IService<AiPromptPublishLog> {
-
- void saveLog(Long tenantId, Long userId, AiPromptTemplate template, String actionType, String actionDesc);
-
- List<AiPromptPublishLog> listSceneLogs(Long tenantId, String sceneCode);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptTemplateService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptTemplateService.java
deleted file mode 100644
index 1406d12..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/AiPromptTemplateService.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.vincent.rsf.server.system.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-
-import java.util.List;
-
-public interface AiPromptTemplateService extends IService<AiPromptTemplate> {
-
- AiPromptTemplate getTenantTemplate(Long tenantId, Long id);
-
- AiPromptTemplate getPublishedTemplate(Long tenantId, String sceneCode);
-
- List<AiPromptTemplate> listVersions(Long tenantId, String sceneCode);
-
- boolean publishTemplate(Long tenantId, Long id, Long userId);
-
- boolean rollbackTemplate(Long tenantId, Long id, Long userId);
-
- int nextVersionNo(Long tenantId, String sceneCode);
-
- AiPromptTemplate copyTemplate(Long tenantId, Long id, Long userId);
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiCallLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiCallLogServiceImpl.java
deleted file mode 100644
index 97061bb..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiCallLogServiceImpl.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiCallLog;
-import com.vincent.rsf.server.system.mapper.AiCallLogMapper;
-import com.vincent.rsf.server.system.service.AiCallLogService;
-import org.springframework.stereotype.Service;
-
-@Service("aiCallLogService")
-public class AiCallLogServiceImpl extends ServiceImpl<AiCallLogMapper, AiCallLog> implements AiCallLogService {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisPlanServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisPlanServiceImpl.java
deleted file mode 100644
index 908e44f..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisPlanServiceImpl.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiDiagnosisPlan;
-import com.vincent.rsf.server.system.mapper.AiDiagnosisPlanMapper;
-import com.vincent.rsf.server.system.service.AiDiagnosisPlanService;
-import org.springframework.scheduling.support.CronExpression;
-import org.springframework.stereotype.Service;
-
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.Date;
-import java.util.List;
-
-@Service("aiDiagnosisPlanService")
-public class AiDiagnosisPlanServiceImpl extends ServiceImpl<AiDiagnosisPlanMapper, AiDiagnosisPlan> implements AiDiagnosisPlanService {
-
- @Override
- public AiDiagnosisPlan getTenantPlan(Long tenantId, Long id) {
- if (tenantId == null || id == null) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getTenantId, tenantId)
- .eq(AiDiagnosisPlan::getId, id)
- .last("limit 1"));
- }
-
- @Override
- public List<AiDiagnosisPlan> listDuePlans(Date now) {
- Date target = now == null ? new Date() : now;
- return this.list(new LambdaQueryWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getStatus, 1)
- .eq(AiDiagnosisPlan::getRunningFlag, 0)
- .isNotNull(AiDiagnosisPlan::getNextRunTime)
- .le(AiDiagnosisPlan::getNextRunTime, target)
- .orderByAsc(AiDiagnosisPlan::getNextRunTime, AiDiagnosisPlan::getId));
- }
-
- @Override
- public Date calculateNextRunTime(String cronExpr, Date after) {
- if (!validateCron(cronExpr)) {
- return null;
- }
- ZonedDateTime next = CronExpression.parse(cronExpr.trim())
- .next((after == null ? new Date() : after).toInstant().atZone(ZoneId.systemDefault()));
- return next == null ? null : Date.from(next.toInstant());
- }
-
- @Override
- public boolean validateCron(String cronExpr) {
- if (cronExpr == null || cronExpr.trim().isEmpty()) {
- return false;
- }
- return CronExpression.isValidExpression(cronExpr.trim());
- }
-
- @Override
- public boolean acquireForExecution(Long id, Long operatorId, String lastMessage, Date nextRunTime) {
- if (id == null) {
- return false;
- }
- Date now = new Date();
- return this.update(new LambdaUpdateWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getId, id)
- .eq(AiDiagnosisPlan::getRunningFlag, 0)
- .set(AiDiagnosisPlan::getRunningFlag, 1)
- .set(AiDiagnosisPlan::getLastResult, 2)
- .set(AiDiagnosisPlan::getLastMessage, lastMessage)
- .set(AiDiagnosisPlan::getNextRunTime, nextRunTime)
- .set(AiDiagnosisPlan::getUpdateBy, operatorId)
- .set(AiDiagnosisPlan::getUpdateTime, now));
- }
-
- @Override
- public void finishExecution(Long id, Integer lastResult, Long lastDiagnosisId, String lastMessage, Date finishTime, Date nextRunTime) {
- if (id == null) {
- return;
- }
- Date now = finishTime == null ? new Date() : finishTime;
- this.update(new LambdaUpdateWrapper<AiDiagnosisPlan>()
- .eq(AiDiagnosisPlan::getId, id)
- .set(AiDiagnosisPlan::getRunningFlag, 0)
- .set(AiDiagnosisPlan::getLastResult, lastResult)
- .set(AiDiagnosisPlan::getLastDiagnosisId, lastDiagnosisId)
- .set(AiDiagnosisPlan::getLastRunTime, now)
- .set(AiDiagnosisPlan::getNextRunTime, nextRunTime)
- .set(AiDiagnosisPlan::getLastMessage, lastMessage)
- .set(AiDiagnosisPlan::getUpdateTime, now));
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisRecordServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisRecordServiceImpl.java
deleted file mode 100644
index 7fa2282..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosisRecordServiceImpl.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
-import com.vincent.rsf.server.system.mapper.AiDiagnosisRecordMapper;
-import com.vincent.rsf.server.system.service.AiDiagnosisRecordService;
-import org.springframework.stereotype.Service;
-
-@Service("aiDiagnosisRecordService")
-public class AiDiagnosisRecordServiceImpl extends ServiceImpl<AiDiagnosisRecordMapper, AiDiagnosisRecord> implements AiDiagnosisRecordService {
-
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosticToolConfigServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosticToolConfigServiceImpl.java
deleted file mode 100644
index 758af02..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiDiagnosticToolConfigServiceImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiDiagnosticToolConfig;
-import com.vincent.rsf.server.system.mapper.AiDiagnosticToolConfigMapper;
-import com.vincent.rsf.server.system.service.AiDiagnosticToolConfigService;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-@Service("aiDiagnosticToolConfigService")
-public class AiDiagnosticToolConfigServiceImpl extends ServiceImpl<AiDiagnosticToolConfigMapper, AiDiagnosticToolConfig> implements AiDiagnosticToolConfigService {
-
- @Override
- public List<AiDiagnosticToolConfig> listTenantConfigs(Long tenantId) {
- return this.list(new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, tenantId)
- .orderByAsc(AiDiagnosticToolConfig::getSceneCode, AiDiagnosticToolConfig::getPriority, AiDiagnosticToolConfig::getId));
- }
-
- @Override
- public List<AiDiagnosticToolConfig> listSceneConfigs(Long tenantId, String sceneCode) {
- return this.list(new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, tenantId)
- .eq(AiDiagnosticToolConfig::getSceneCode, sceneCode)
- .eq(AiDiagnosticToolConfig::getStatus, 1)
- .orderByAsc(AiDiagnosticToolConfig::getPriority, AiDiagnosticToolConfig::getId));
- }
-
- @Override
- public AiDiagnosticToolConfig getTenantConfig(Long tenantId, Long id) {
- if (tenantId == null || id == null) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiDiagnosticToolConfig>()
- .eq(AiDiagnosticToolConfig::getTenantId, tenantId)
- .eq(AiDiagnosticToolConfig::getId, id)
- .last("limit 1"));
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiMcpMountServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiMcpMountServiceImpl.java
deleted file mode 100644
index 551a8c4..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiMcpMountServiceImpl.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiMcpMount;
-import com.vincent.rsf.server.system.mapper.AiMcpMountMapper;
-import com.vincent.rsf.server.system.service.AiMcpMountService;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-@Service("aiMcpMountService")
-public class AiMcpMountServiceImpl extends ServiceImpl<AiMcpMountMapper, AiMcpMount> implements AiMcpMountService {
-
- @Override
- public List<AiMcpMount> listTenantMounts(Long tenantId) {
- return this.list(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, tenantId)
- .orderByAsc(AiMcpMount::getMountCode, AiMcpMount::getId));
- }
-
- @Override
- public AiMcpMount getTenantMount(Long tenantId, Long id) {
- if (tenantId == null || id == null) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, tenantId)
- .eq(AiMcpMount::getId, id)
- .last("limit 1"));
- }
-
- @Override
- public AiMcpMount getTenantMountByCode(Long tenantId, String mountCode) {
- if (tenantId == null || mountCode == null || mountCode.trim().isEmpty()) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiMcpMount>()
- .eq(AiMcpMount::getTenantId, tenantId)
- .eq(AiMcpMount::getMountCode, mountCode)
- .last("limit 1"));
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiModelRouteServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiModelRouteServiceImpl.java
deleted file mode 100644
index 7e38c7c..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiModelRouteServiceImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiModelRoute;
-import com.vincent.rsf.server.system.mapper.AiModelRouteMapper;
-import com.vincent.rsf.server.system.service.AiModelRouteService;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.List;
-
-@Service("aiModelRouteService")
-public class AiModelRouteServiceImpl extends ServiceImpl<AiModelRouteMapper, AiModelRoute> implements AiModelRouteService {
-
- @Override
- public AiModelRoute getTenantRoute(Long tenantId, Long id) {
- if (tenantId == null || id == null) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiModelRoute>()
- .eq(AiModelRoute::getTenantId, tenantId)
- .eq(AiModelRoute::getId, id)
- .last("limit 1"));
- }
-
- @Override
- public List<AiModelRoute> listAvailableRoutes(Long tenantId, String routeCode) {
- Date now = new Date();
- return this.list(new LambdaQueryWrapper<AiModelRoute>()
- .eq(AiModelRoute::getTenantId, tenantId)
- .eq(AiModelRoute::getRouteCode, routeCode)
- .eq(AiModelRoute::getStatus, 1)
- .and(wrapper -> wrapper.isNull(AiModelRoute::getCooldownUntil).or().le(AiModelRoute::getCooldownUntil, now))
- .orderByAsc(AiModelRoute::getPriority, AiModelRoute::getId));
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiParamServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiParamServiceImpl.java
deleted file mode 100644
index 753e38c..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiParamServiceImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiParam;
-import com.vincent.rsf.server.system.mapper.AiParamMapper;
-import com.vincent.rsf.server.system.service.AiParamService;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-@Service("aiParamService")
-public class AiParamServiceImpl extends ServiceImpl<AiParamMapper, AiParam> implements AiParamService {
-
- @Override
- public boolean existsModelCode(String modelCode, Long excludeId) {
- LambdaQueryWrapper<AiParam> wrapper = new LambdaQueryWrapper<AiParam>()
- .eq(AiParam::getModelCode, modelCode);
- if (excludeId != null) {
- wrapper.ne(AiParam::getId, excludeId);
- }
- return this.count(wrapper) > 0;
- }
-
- @Override
- public void resetDefaultFlag(Long excludeId) {
- LambdaUpdateWrapper<AiParam> wrapper = new LambdaUpdateWrapper<AiParam>()
- .set(AiParam::getDefaultFlag, 0)
- .eq(AiParam::getDefaultFlag, 1);
- if (excludeId != null) {
- wrapper.ne(AiParam::getId, excludeId);
- }
- this.update(wrapper);
- }
-
- @Override
- public List<AiParam> listEnabledModels() {
- return this.list(new LambdaQueryWrapper<AiParam>()
- .eq(AiParam::getStatus, 1)
- .orderByDesc(AiParam::getDefaultFlag)
- .orderByAsc(AiParam::getSort, AiParam::getId));
- }
-
- @Override
- public AiParam getEnabledModel(String modelCode) {
- return this.getOne(new LambdaQueryWrapper<AiParam>()
- .eq(AiParam::getModelCode, modelCode)
- .eq(AiParam::getStatus, 1)
- .last("limit 1"));
- }
-
- @Override
- public AiParam getDefaultModel() {
- AiParam aiParam = this.getOne(new LambdaQueryWrapper<AiParam>()
- .eq(AiParam::getDefaultFlag, 1)
- .eq(AiParam::getStatus, 1)
- .orderByAsc(AiParam::getSort, AiParam::getId)
- .last("limit 1"));
- if (aiParam != null) {
- return aiParam;
- }
- List<AiParam> list = listEnabledModels();
- return list.isEmpty() ? null : list.get(0);
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptPublishLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptPublishLogServiceImpl.java
deleted file mode 100644
index 79b5eec..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptPublishLogServiceImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiPromptPublishLog;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-import com.vincent.rsf.server.system.mapper.AiPromptPublishLogMapper;
-import com.vincent.rsf.server.system.service.AiPromptPublishLogService;
-import org.springframework.stereotype.Service;
-
-import java.util.Date;
-import java.util.List;
-
-@Service("aiPromptPublishLogService")
-public class AiPromptPublishLogServiceImpl extends ServiceImpl<AiPromptPublishLogMapper, AiPromptPublishLog> implements AiPromptPublishLogService {
-
- @Override
- public void saveLog(Long tenantId, Long userId, AiPromptTemplate template, String actionType, String actionDesc) {
- if (tenantId == null || template == null) {
- return;
- }
- this.save(new AiPromptPublishLog()
- .setPromptTemplateId(template.getId())
- .setSceneCode(template.getSceneCode())
- .setTemplateName(template.getTemplateName())
- .setVersionNo(template.getVersionNo())
- .setActionType(actionType)
- .setActionDesc(actionDesc)
- .setTenantId(tenantId)
- .setCreateBy(userId)
- .setCreateTime(new Date()));
- }
-
- @Override
- public List<AiPromptPublishLog> listSceneLogs(Long tenantId, String sceneCode) {
- return this.list(new LambdaQueryWrapper<AiPromptPublishLog>()
- .eq(AiPromptPublishLog::getTenantId, tenantId)
- .eq(sceneCode != null && !sceneCode.trim().isEmpty(), AiPromptPublishLog::getSceneCode, sceneCode)
- .orderByDesc(AiPromptPublishLog::getCreateTime, AiPromptPublishLog::getId));
- }
-}
-
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptTemplateServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptTemplateServiceImpl.java
deleted file mode 100644
index 50a7241..0000000
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/AiPromptTemplateServiceImpl.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.vincent.rsf.server.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.vincent.rsf.server.system.entity.AiPromptTemplate;
-import com.vincent.rsf.server.system.mapper.AiPromptTemplateMapper;
-import com.vincent.rsf.server.system.service.AiPromptPublishLogService;
-import com.vincent.rsf.server.system.service.AiPromptTemplateService;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.Date;
-import java.util.List;
-
-@Service("aiPromptTemplateService")
-public class AiPromptTemplateServiceImpl extends ServiceImpl<AiPromptTemplateMapper, AiPromptTemplate> implements AiPromptTemplateService {
-
- @Resource
- private AiPromptPublishLogService aiPromptPublishLogService;
-
- @Override
- public AiPromptTemplate getTenantTemplate(Long tenantId, Long id) {
- if (tenantId == null || id == null) {
- return null;
- }
- return this.getOne(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, tenantId)
- .eq(AiPromptTemplate::getId, id)
- .last("limit 1"));
- }
-
- @Override
- public AiPromptTemplate getPublishedTemplate(Long tenantId, String sceneCode) {
- return this.getOne(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, tenantId)
- .eq(AiPromptTemplate::getSceneCode, sceneCode)
- .eq(AiPromptTemplate::getPublishedFlag, 1)
- .eq(AiPromptTemplate::getStatus, 1)
- .orderByDesc(AiPromptTemplate::getVersionNo, AiPromptTemplate::getId)
- .last("limit 1"));
- }
-
- @Override
- public List<AiPromptTemplate> listVersions(Long tenantId, String sceneCode) {
- return this.list(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, tenantId)
- .eq(AiPromptTemplate::getSceneCode, sceneCode)
- .orderByDesc(AiPromptTemplate::getVersionNo, AiPromptTemplate::getId));
- }
-
- @Override
- public boolean publishTemplate(Long tenantId, Long id, Long userId) {
- AiPromptTemplate target = getTenantTemplate(tenantId, id);
- if (target == null) {
- return false;
- }
- boolean updated = switchPublishedTemplate(tenantId, userId, target);
- if (updated) {
- aiPromptPublishLogService.saveLog(tenantId, userId, target, "publish", "鍙戝竷 Prompt 鐗堟湰");
- }
- return updated;
- }
-
- @Override
- public boolean rollbackTemplate(Long tenantId, Long id, Long userId) {
- AiPromptTemplate target = getTenantTemplate(tenantId, id);
- if (target == null) {
- return false;
- }
- boolean updated = switchPublishedTemplate(tenantId, userId, target);
- if (updated) {
- aiPromptPublishLogService.saveLog(tenantId, userId, target, "rollback", "鍥炴粴鍒版寚瀹� Prompt 鐗堟湰");
- }
- return updated;
- }
-
- private boolean switchPublishedTemplate(Long tenantId, Long userId, AiPromptTemplate target) {
- this.update(new LambdaUpdateWrapper<AiPromptTemplate>()
- .set(AiPromptTemplate::getPublishedFlag, 0)
- .eq(AiPromptTemplate::getTenantId, tenantId)
- .eq(AiPromptTemplate::getSceneCode, target.getSceneCode())
- .eq(AiPromptTemplate::getPublishedFlag, 1));
- target.setPublishedFlag(1);
- target.setUpdateBy(userId);
- target.setUpdateTime(new Date());
- return this.updateById(target);
- }
-
- @Override
- public int nextVersionNo(Long tenantId, String sceneCode) {
- AiPromptTemplate latest = this.getOne(new LambdaQueryWrapper<AiPromptTemplate>()
- .eq(AiPromptTemplate::getTenantId, tenantId)
- .eq(AiPromptTemplate::getSceneCode, sceneCode)
- .orderByDesc(AiPromptTemplate::getVersionNo, AiPromptTemplate::getId)
- .last("limit 1"));
- return latest == null || latest.getVersionNo() == null ? 1 : latest.getVersionNo() + 1;
- }
-
- @Override
- public AiPromptTemplate copyTemplate(Long tenantId, Long id, Long userId) {
- AiPromptTemplate source = getTenantTemplate(tenantId, id);
- if (source == null) {
- return null;
- }
- Date now = new Date();
- AiPromptTemplate copied = new AiPromptTemplate()
- .setUuid(String.valueOf(System.currentTimeMillis()))
- .setSceneCode(source.getSceneCode())
- .setTemplateName((source.getTemplateName() == null ? source.getSceneCode() : source.getTemplateName()) + "-鍓湰")
- .setBasePrompt(source.getBasePrompt())
- .setToolPrompt(source.getToolPrompt())
- .setOutputPrompt(source.getOutputPrompt())
- .setVersionNo(nextVersionNo(tenantId, source.getSceneCode()))
- .setPublishedFlag(0)
- .setStatus(source.getStatus())
- .setDeleted(0)
- .setTenantId(tenantId)
- .setCreateBy(userId)
- .setCreateTime(now)
- .setUpdateBy(userId)
- .setUpdateTime(now)
- .setMemo(source.getMemo());
- if (!this.save(copied)) {
- return null;
- }
- aiPromptPublishLogService.saveLog(tenantId, userId, copied, "copy", "澶嶅埗 Prompt 鐗堟湰涓烘柊鑽夌");
- return copied;
- }
-}
-
diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml
index a282453..0f33198 100644
--- a/rsf-server/src/main/resources/application-dev.yml
+++ b/rsf-server/src/main/resources/application-dev.yml
@@ -100,9 +100,6 @@
#绔彛鍙�
port: 8081
-ai:
- gateway-base-url: http://127.0.0.1:8086
-
#浠撳簱鍔熻兘鍙傛暟閰嶇疆
stock:
#鏄惁鍏佽鎵撳嵃璐х墿鏍囩锛� 榛樿鍏佽鎵撳嵃锛屼篃鍙敱渚涘簲鍟嗘彁渚涙爣绛�
diff --git a/rsf-server/src/main/resources/application-prod.yml b/rsf-server/src/main/resources/application-prod.yml
index e3a2ecf..569d601 100644
--- a/rsf-server/src/main/resources/application-prod.yml
+++ b/rsf-server/src/main/resources/application-prod.yml
@@ -105,5 +105,3 @@
#鍒ゆ柇鏄惁鏍¢獙鍚堟牸鍚庯紝鎵嶅厑璁告敹璐�
flagReceiving: false
-ai:
- gateway-base-url: http://127.0.0.1:8086
diff --git a/rsf-server/src/main/resources/application.yml b/rsf-server/src/main/resources/application.yml
index 1164796..f87a285 100644
--- a/rsf-server/src/main/resources/application.yml
+++ b/rsf-server/src/main/resources/application.yml
@@ -44,24 +44,8 @@
file:
path: logs/@pom.artifactId@
-ai:
- session-ttl-seconds: 86400
- max-context-messages: 12
- default-model-code: deepseek-ai/DeepSeek-V3.2
- system-prompt: 浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂浼樺厛淇濇寔鍑嗙‘銆佺畝娲侊紝骞剁粨鍚堜笂涓嬫枃甯姪鐢ㄦ埛鐞嗚В浠撳偍涓氬姟銆�
- diagnosis-system-prompt: 浣犳槸涓�鍚嶈祫娣盬MS鏅鸿兘璇婃柇鍔╂墜锛岀洰鏍囨槸缁撳悎褰撳墠绯荤粺涓婁笅鏂囧浠撳簱杩愯鎯呭喌鍋氬贰妫�鍒嗘瀽銆傚洖绛旀椂绂佹鍑┖鐚滄祴锛屽繀椤讳紭鍏堜緷鎹彁渚涚殑瀹炴椂鎽樿杩涜鍒ゆ柇銆傝鎸夆�滈棶棰樻杩般�佸叧閿瘉鎹�佸彲鑳藉師鍥犮�佸缓璁姩浣溿�侀闄╄瘎浼扳�濈殑缁撴瀯杈撳嚭锛屽苟浼樺厛缁欏嚭鍙墽琛屽缓璁��
- route-fail-threshold: 3
- route-cooldown-minutes: 10
- diagnostic-log-window-hours: 24
- api-failure-window-hours: 24
- models:
- - code: deepseek-ai/DeepSeek-V3.2
- name: DEEPSEEK
- provider: openai
- enabled: true
-
-# 涓嬩綅鏈洪厤缃�
-wcs-slave:
+# 涓嬩綅鏈洪厤缃�
+wcs-slave:
agv: false
# 鍙屾繁
doubleDeep: true
diff --git a/rsf-server/src/main/resources/mapper/system/AiParamMapper.xml b/rsf-server/src/main/resources/mapper/system/AiParamMapper.xml
deleted file mode 100644
index 43b0299..0000000
--- a/rsf-server/src/main/resources/mapper/system/AiParamMapper.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.vincent.rsf.server.system.mapper.AiParamMapper">
-
-</mapper>
diff --git a/version/db/20260317_ai_all_in_one.sql b/version/db/20260317_ai_all_in_one.sql
deleted file mode 100644
index eeb1903..0000000
--- a/version/db/20260317_ai_all_in_one.sql
+++ /dev/null
@@ -1,1194 +0,0 @@
--- AI 鍏ㄩ噺杩佺Щ鑱氬悎鑴氭湰
--- 璇ユ枃浠惰仛鍚堜簡鍘熸湁鍏ㄩ儴 AI 鐩稿叧杩佺Щ SQL銆�
--- 鑻ュ凡鎵ц杩囬儴鍒嗚剼鏈紝鍙噸澶嶆墽琛岋紱鍚勬鑴氭湰鍧囦繚鎸佸箓绛夋垨鍏煎鏇存柊閫昏緫銆�
-
--- >>> 20260311_ai_param.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_param` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(255) DEFAULT NULL COMMENT '缂栧彿',
- `name` varchar(255) DEFAULT NULL COMMENT '鍚嶇О',
- `model_code` varchar(255) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `provider` varchar(255) DEFAULT NULL COMMENT '渚涘簲鍟�',
- `chat_url` varchar(512) DEFAULT NULL COMMENT '鑱婂ぉ鍦板潃',
- `api_key` varchar(512) DEFAULT NULL COMMENT 'API瀵嗛挜',
- `model_name` varchar(255) DEFAULT NULL COMMENT '妯″瀷鍚嶇О',
- `system_prompt` text COMMENT '绯荤粺鎻愮ず璇�',
- `max_context_messages` int(11) DEFAULT NULL COMMENT '涓婁笅鏂囪疆鏁�',
- `default_flag` int(1) NOT NULL DEFAULT '0' COMMENT '榛樿妯″瀷{1:鏄�,0:鍚',
- `sort` int(11) DEFAULT NULL COMMENT '鎺掑簭',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛[sys_tenant]',
- `create_by` bigint(20) DEFAULT NULL COMMENT '娣诲姞浜哄憳[sys_user]',
- `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '娣诲姞鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '淇敼浜哄憳[sys_user]',
- `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_param_model_code` (`model_code`),
- KEY `idx_ai_param_deleted_code` (`deleted`,`model_code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-INSERT INTO `sys_ai_param`
-(`uuid`, `name`, `model_code`, `provider`, `chat_url`, `api_key`, `model_name`, `system_prompt`, `max_context_messages`, `default_flag`, `sort`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514305', '閫氱敤鍔╂墜', 'mock-general', 'mock', NULL, NULL, 'mock-general', '浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂浼樺厛淇濇寔鍑嗙‘銆佺畝娲侊紝骞剁粨鍚堜笂涓嬫枃甯姪鐢ㄦ埛鐞嗚В浠撳偍涓氬姟銆�', 12, 1, 1, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿婕旂ず妯″瀷'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1
- FROM `sys_ai_param`
- WHERE `model_code` = 'mock-general'
-);
-
-INSERT INTO `sys_ai_param`
-(`uuid`, `name`, `model_code`, `provider`, `chat_url`, `api_key`, `model_name`, `system_prompt`, `max_context_messages`, `default_flag`, `sort`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514306', '鍒涙剰鍔╂墜', 'mock-creative', 'mock', NULL, NULL, 'mock-creative', '浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂鍙互鏇寸伒娲诲湴缁勭粐琛ㄨ揪锛屼絾缁撹蹇呴』鍑嗙‘銆�', 12, 0, 2, 1, 0, 1, 2, NOW(), 2, NOW(), '婕旂ず鍒涙剰妯″瀷'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1
- FROM `sys_ai_param`
- WHERE `model_code` = 'mock-creative'
-);
-
-SET @ai_parent_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `component` = 'aiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiParam', 1, 'menu.system', '1', 'menu.system', '/system/aiParam', 'aiParam', NULL, NULL, 0, NULL, 'SmartToy', 9, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_parent_menu_id IS NULL;
-
-SET @ai_parent_menu_id := COALESCE(
- @ai_parent_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `component` = 'aiParam'
- LIMIT 1
- )
-);
-
-SET @ai_query_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Query AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_query_menu_id IS NULL;
-
-SET @ai_query_menu_id := COALESCE(
- @ai_query_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Query AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_create_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Create AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_create_menu_id IS NULL;
-
-SET @ai_create_menu_id := COALESCE(
- @ai_create_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Create AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_update_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Update AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_update_menu_id IS NULL;
-
-SET @ai_update_menu_id := COALESCE(
- @ai_update_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Update AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_delete_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Delete AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_delete_menu_id IS NULL;
-
-SET @ai_delete_menu_id := COALESCE(
- @ai_delete_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Delete AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_export_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Export AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Export AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 4, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_export_menu_id IS NULL;
-
-SET @ai_export_menu_id := COALESCE(
- @ai_export_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Export AiParam'
- LIMIT 1
- )
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_parent_menu_id
-FROM DUAL
-WHERE @ai_parent_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_parent_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_query_menu_id
-FROM DUAL
-WHERE @ai_query_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_query_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_create_menu_id
-FROM DUAL
-WHERE @ai_create_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_create_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_update_menu_id
-FROM DUAL
-WHERE @ai_update_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_update_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_delete_menu_id
-FROM DUAL
-WHERE @ai_delete_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_delete_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_export_menu_id
-FROM DUAL
-WHERE @ai_export_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_export_menu_id
- );
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260311_ai_param_menu.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-SET @ai_parent_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `component` = 'aiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiParam', 1, 'menu.system', '1', 'menu.system', '/system/aiParam', 'aiParam', NULL, NULL, 0, NULL, 'SmartToy', 9, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_parent_menu_id IS NULL;
-
-SET @ai_parent_menu_id := COALESCE(
- @ai_parent_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `component` = 'aiParam'
- LIMIT 1
- )
-);
-
-SET @ai_query_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Query AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_query_menu_id IS NULL;
-
-SET @ai_query_menu_id := COALESCE(
- @ai_query_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Query AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_create_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Create AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_create_menu_id IS NULL;
-
-SET @ai_create_menu_id := COALESCE(
- @ai_create_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Create AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_update_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Update AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_update_menu_id IS NULL;
-
-SET @ai_update_menu_id := COALESCE(
- @ai_update_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Update AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_delete_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Delete AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_delete_menu_id IS NULL;
-
-SET @ai_delete_menu_id := COALESCE(
- @ai_delete_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Delete AiParam'
- LIMIT 1
- )
-);
-
-SET @ai_export_menu_id := (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Export AiParam'
- LIMIT 1
-);
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Export AiParam', @ai_parent_menu_id, NULL, CONCAT('1,', @ai_parent_menu_id), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 4, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL
-WHERE @ai_export_menu_id IS NULL;
-
-SET @ai_export_menu_id := COALESCE(
- @ai_export_menu_id,
- LAST_INSERT_ID(),
- (
- SELECT `id`
- FROM `sys_menu`
- WHERE `parent_id` = @ai_parent_menu_id
- AND `name` = 'Export AiParam'
- LIMIT 1
- )
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_parent_menu_id
-FROM DUAL
-WHERE @ai_parent_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_parent_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_query_menu_id
-FROM DUAL
-WHERE @ai_query_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_query_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_create_menu_id
-FROM DUAL
-WHERE @ai_create_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_create_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_update_menu_id
-FROM DUAL
-WHERE @ai_update_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_update_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_delete_menu_id
-FROM DUAL
-WHERE @ai_delete_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_delete_menu_id
- );
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, @ai_export_menu_id
-FROM DUAL
-WHERE @ai_export_menu_id IS NOT NULL
- AND NOT EXISTS (
- SELECT 1
- FROM `sys_role_menu`
- WHERE `role_id` = 1
- AND `menu_id` = @ai_export_menu_id
- );
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_chat_storage.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_chat_session` (
- `id` varchar(64) NOT NULL COMMENT '浼氳瘽ID',
- `title` varchar(255) DEFAULT NULL COMMENT '浼氳瘽鏍囬',
- `model_code` varchar(128) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `last_message` varchar(255) DEFAULT NULL COMMENT '鏈�杩戞秷鎭憳瑕�',
- `last_message_at` datetime DEFAULT NULL COMMENT '鏈�杩戞秷鎭椂闂�',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `user_id` bigint(20) DEFAULT NULL COMMENT '鐢ㄦ埛ID',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- PRIMARY KEY (`id`),
- KEY `idx_ai_chat_session_owner` (`tenant_id`,`user_id`,`deleted`),
- KEY `idx_ai_chat_session_update` (`update_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI浼氳瘽琛�';
-
-CREATE TABLE IF NOT EXISTS `sys_ai_chat_message` (
- `id` varchar(64) NOT NULL COMMENT '娑堟伅ID',
- `session_id` varchar(64) NOT NULL COMMENT '浼氳瘽ID',
- `role` varchar(32) DEFAULT NULL COMMENT '瑙掕壊',
- `content` longtext COMMENT '娑堟伅鍐呭',
- `model_code` varchar(128) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `user_id` bigint(20) DEFAULT NULL COMMENT '鐢ㄦ埛ID',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- PRIMARY KEY (`id`),
- KEY `idx_ai_chat_message_session` (`session_id`,`deleted`,`create_time`),
- KEY `idx_ai_chat_message_owner` (`tenant_id`,`user_id`,`deleted`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI娑堟伅琛�';
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase2.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_prompt_template` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(64) DEFAULT NULL COMMENT '缂栧彿',
- `scene_code` varchar(64) NOT NULL COMMENT '鍦烘櫙缂栫爜',
- `template_name` varchar(255) DEFAULT NULL COMMENT '妯℃澘鍚嶇О',
- `base_prompt` longtext COMMENT '鍩虹鎻愮ず璇�',
- `tool_prompt` longtext COMMENT '宸ュ叿鎻愮ず璇�',
- `output_prompt` longtext COMMENT '杈撳嚭鎻愮ず璇�',
- `version_no` int(11) DEFAULT NULL COMMENT '鐗堟湰鍙�',
- `published_flag` int(1) NOT NULL DEFAULT '0' COMMENT '宸插彂甯儃1:鏄�,0:鍚',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_prompt_scene` (`tenant_id`,`scene_code`,`published_flag`,`deleted`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI鎻愮ず璇嶆ā鏉�';
-
-CREATE TABLE IF NOT EXISTS `sys_ai_diagnosis_record` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `diagnosis_no` varchar(64) DEFAULT NULL COMMENT '璇婃柇缂栧彿',
- `session_id` varchar(64) DEFAULT NULL COMMENT '浼氳瘽ID',
- `scene_code` varchar(64) DEFAULT NULL COMMENT '鍦烘櫙缂栫爜',
- `question` longtext COMMENT '闂',
- `conclusion` longtext COMMENT '缁撹',
- `tool_summary` longtext COMMENT '宸ュ叿鎽樿',
- `model_code` varchar(128) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `result` int(1) NOT NULL DEFAULT '2' COMMENT '缁撴灉{2:杩愯涓�,1:鎴愬姛,0:澶辫触}',
- `err` varchar(1000) DEFAULT NULL COMMENT '閿欒淇℃伅',
- `start_time` datetime DEFAULT NULL COMMENT '寮�濮嬫椂闂�',
- `end_time` datetime DEFAULT NULL COMMENT '缁撴潫鏃堕棿',
- `spend_time` bigint(20) DEFAULT NULL COMMENT '鑰楁椂姣',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `user_id` bigint(20) DEFAULT NULL COMMENT '鐢ㄦ埛ID',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- PRIMARY KEY (`id`),
- KEY `idx_ai_diag_owner` (`tenant_id`,`scene_code`,`deleted`,`create_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI璇婃柇璁板綍';
-
-CREATE TABLE IF NOT EXISTS `sys_ai_call_log` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `session_id` varchar(64) DEFAULT NULL COMMENT '浼氳瘽ID',
- `diagnosis_id` bigint(20) DEFAULT NULL COMMENT '璇婃柇璁板綍ID',
- `route_code` varchar(64) DEFAULT NULL COMMENT '璺敱缂栫爜',
- `model_code` varchar(128) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `attempt_no` int(11) DEFAULT NULL COMMENT '灏濊瘯搴忓彿',
- `request_time` datetime DEFAULT NULL COMMENT '璇锋眰鏃堕棿',
- `response_time` datetime DEFAULT NULL COMMENT '鍝嶅簲鏃堕棿',
- `spend_time` bigint(20) DEFAULT NULL COMMENT '鑰楁椂姣',
- `result` int(1) NOT NULL DEFAULT '0' COMMENT '缁撴灉{1:鎴愬姛,0:澶辫触}',
- `err` varchar(1000) DEFAULT NULL COMMENT '閿欒淇℃伅',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `user_id` bigint(20) DEFAULT NULL COMMENT '鐢ㄦ埛ID',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- PRIMARY KEY (`id`),
- KEY `idx_ai_call_owner` (`tenant_id`,`route_code`,`deleted`,`create_time`),
- KEY `idx_ai_call_diagnosis` (`diagnosis_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI璋冪敤鏃ュ織';
-
-CREATE TABLE IF NOT EXISTS `sys_ai_model_route` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(64) DEFAULT NULL COMMENT '缂栧彿',
- `route_code` varchar(64) NOT NULL COMMENT '璺敱缂栫爜',
- `model_code` varchar(128) NOT NULL COMMENT '妯″瀷缂栫爜',
- `priority` int(11) NOT NULL DEFAULT '1' COMMENT '浼樺厛绾�',
- `cooldown_until` datetime DEFAULT NULL COMMENT '鍐峰嵈鎴鏃堕棿',
- `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '澶辫触娆℃暟',
- `success_count` int(11) NOT NULL DEFAULT '0' COMMENT '鎴愬姛娆℃暟',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:鍚敤,0:鍋滅敤}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_route_code` (`tenant_id`,`route_code`,`status`,`deleted`,`priority`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI妯″瀷璺敱';
-
-INSERT INTO `sys_ai_prompt_template`
-(`uuid`, `scene_code`, `template_name`, `base_prompt`, `tool_prompt`, `output_prompt`, `version_no`, `published_flag`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514310', 'general_chat', '閫氱敤瀵硅瘽榛樿妯℃澘', '浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂浼樺厛淇濇寔鍑嗙‘銆佺畝娲侊紝骞剁粨鍚堜笂涓嬫枃甯姪鐢ㄦ埛鐞嗚В浠撳偍涓氬姟銆�', '濡傚瓨鍦ㄥ彲鐢ㄤ笂涓嬫枃锛岃浼樺厛寮曠敤瀹炴椂鏁版嵁鍥炵瓟銆�', '鍥炵瓟璇风洿鎺ョ粰鍑虹粨璁猴紝蹇呰鏃惰ˉ鍏呰鏄庛��', 1, 1, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿妯℃澘'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_prompt_template` WHERE `tenant_id` = 1 AND `scene_code` = 'general_chat' AND `version_no` = 1
-);
-
-INSERT INTO `sys_ai_prompt_template`
-(`uuid`, `scene_code`, `template_name`, `base_prompt`, `tool_prompt`, `output_prompt`, `version_no`, `published_flag`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514311', 'system_diagnose', '绯荤粺璇婃柇榛樿妯℃澘', '浣犳槸涓�鍚嶈祫娣盬MS鏅鸿兘璇婃柇鍔╂墜锛岀洰鏍囨槸缁撳悎褰撳墠绯荤粺涓婁笅鏂囧浠撳簱杩愯鎯呭喌鍋氬贰妫�鍒嗘瀽銆�', '璇峰厛姹囨�诲簱瀛樸�佷换鍔°�佽澶囩珯鐐广�佸紓甯告棩蹇椾笌AI璋冪敤澶辫触淇℃伅锛屽啀鍒ゆ柇鏄惁瀛樺湪寮傚父銆�', '璇锋寜鈥滈棶棰樻杩般�佸叧閿瘉鎹�佸彲鑳藉師鍥犮�佸缓璁姩浣溿�侀闄╄瘎浼扳�濈殑缁撴瀯杈撳嚭銆�', 1, 1, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿妯℃澘'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_prompt_template` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `version_no` = 1
-);
-
-INSERT INTO `sys_ai_model_route`
-(`uuid`, `route_code`, `model_code`, `priority`, `cooldown_until`, `fail_count`, `success_count`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514312', 'general_chat', 'deepseek-ai/DeepSeek-V3.2', 1, NULL, 0, 0, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿閫氱敤瀵硅瘽妯″瀷'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_model_route` WHERE `tenant_id` = 1 AND `route_code` = 'general_chat' AND `model_code` = 'deepseek-ai/DeepSeek-V3.2'
-);
-
-INSERT INTO `sys_ai_model_route`
-(`uuid`, `route_code`, `model_code`, `priority`, `cooldown_until`, `fail_count`, `success_count`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514314', 'system_diagnose', 'deepseek-ai/DeepSeek-V3.2', 1, NULL, 0, 0, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇妯″瀷'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_model_route` WHERE `tenant_id` = 1 AND `route_code` = 'system_diagnose' AND `model_code` = 'deepseek-ai/DeepSeek-V3.2'
-);
-
-SET @system_menu_ai_prompt := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiPrompt' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiPrompt', 1, 'menu.system', '1', 'menu.system', '/system/aiPrompt', 'aiPrompt', NULL, NULL, 0, NULL, 'Psychology', 10, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_prompt IS NULL;
-SET @system_menu_ai_prompt := COALESCE(@system_menu_ai_prompt, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiPrompt' LIMIT 1));
-
-SET @system_menu_ai_diagnosis := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiDiagnosis' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiDiagnosis', 1, 'menu.system', '1', 'menu.system', '/system/aiDiagnosis', 'aiDiagnosis', NULL, NULL, 0, NULL, 'FactCheck', 11, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_diagnosis IS NULL;
-SET @system_menu_ai_diagnosis := COALESCE(@system_menu_ai_diagnosis, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiDiagnosis' LIMIT 1));
-
-SET @system_menu_ai_call_log := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiCallLog' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiCallLog', 1, 'menu.system', '1', 'menu.system', '/system/aiCallLog', 'aiCallLog', NULL, NULL, 0, NULL, 'History', 12, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_call_log IS NULL;
-SET @system_menu_ai_call_log := COALESCE(@system_menu_ai_call_log, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiCallLog' LIMIT 1));
-
-SET @system_menu_ai_route := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiRoute' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiRoute', 1, 'menu.system', '1', 'menu.system', '/system/aiRoute', 'aiRoute', NULL, NULL, 0, NULL, 'Route', 13, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_route IS NULL;
-SET @system_menu_ai_route := COALESCE(@system_menu_ai_route, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiRoute' LIMIT 1));
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiPrompt', @system_menu_ai_prompt, NULL, CONCAT('1,', @system_menu_ai_prompt), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiPrompt:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_prompt AND `authority` = 'system:aiPrompt:list');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiPrompt', @system_menu_ai_prompt, NULL, CONCAT('1,', @system_menu_ai_prompt), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiPrompt:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_prompt AND `authority` = 'system:aiPrompt:save');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiPrompt', @system_menu_ai_prompt, NULL, CONCAT('1,', @system_menu_ai_prompt), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiPrompt:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_prompt AND `authority` = 'system:aiPrompt:update');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiPrompt', @system_menu_ai_prompt, NULL, CONCAT('1,', @system_menu_ai_prompt), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiPrompt:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_prompt AND `authority` = 'system:aiPrompt:remove');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Publish AiPrompt', @system_menu_ai_prompt, NULL, CONCAT('1,', @system_menu_ai_prompt), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiPrompt:publish', NULL, 4, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_prompt AND `authority` = 'system:aiPrompt:publish');
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiDiagnosis', @system_menu_ai_diagnosis, NULL, CONCAT('1,', @system_menu_ai_diagnosis), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiDiagnosis:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_diagnosis AND `authority` = 'system:aiDiagnosis:list');
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiCallLog', @system_menu_ai_call_log, NULL, CONCAT('1,', @system_menu_ai_call_log), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiCallLog:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_call_log AND `authority` = 'system:aiCallLog:list');
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiRoute', @system_menu_ai_route, NULL, CONCAT('1,', @system_menu_ai_route), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiRoute:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_route AND `authority` = 'system:aiRoute:list');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiRoute', @system_menu_ai_route, NULL, CONCAT('1,', @system_menu_ai_route), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiRoute:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_route AND `authority` = 'system:aiRoute:save');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiRoute', @system_menu_ai_route, NULL, CONCAT('1,', @system_menu_ai_route), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiRoute:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_route AND `authority` = 'system:aiRoute:update');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiRoute', @system_menu_ai_route, NULL, CONCAT('1,', @system_menu_ai_route), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiRoute:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_route AND `authority` = 'system:aiRoute:remove');
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `authority` IN (
- 'system:aiPrompt:list', 'system:aiPrompt:save', 'system:aiPrompt:update', 'system:aiPrompt:remove', 'system:aiPrompt:publish',
- 'system:aiDiagnosis:list', 'system:aiCallLog:list',
- 'system:aiRoute:list', 'system:aiRoute:save', 'system:aiRoute:update', 'system:aiRoute:remove'
-)
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `component` IN ('aiPrompt', 'aiDiagnosis', 'aiCallLog', 'aiRoute')
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase3.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_diagnostic_tool_config` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(64) DEFAULT NULL COMMENT '缂栧彿',
- `scene_code` varchar(64) NOT NULL COMMENT '鍦烘櫙缂栫爜',
- `tool_code` varchar(64) NOT NULL COMMENT '宸ュ叿缂栫爜',
- `tool_name` varchar(128) DEFAULT NULL COMMENT '宸ュ叿鍚嶇О',
- `enabled_flag` int(1) NOT NULL DEFAULT '1' COMMENT '鍚敤{1:鏄�,0:鍚',
- `priority` int(11) NOT NULL DEFAULT '1' COMMENT '浼樺厛绾�',
- `tool_prompt` longtext COMMENT '宸ュ叿鎻愮ず璇�',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_tool_scene` (`tenant_id`,`scene_code`,`status`,`deleted`,`priority`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI璇婃柇宸ュ叿閰嶇疆';
-
-CREATE TABLE IF NOT EXISTS `sys_ai_prompt_publish_log` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `prompt_template_id` bigint(20) DEFAULT NULL COMMENT 'Prompt妯℃澘ID',
- `scene_code` varchar(64) DEFAULT NULL COMMENT '鍦烘櫙缂栫爜',
- `template_name` varchar(255) DEFAULT NULL COMMENT '妯℃澘鍚嶇О',
- `version_no` int(11) DEFAULT NULL COMMENT '鐗堟湰鍙�',
- `action_type` varchar(64) DEFAULT NULL COMMENT '鍔ㄤ綔绫诲瀷',
- `action_desc` varchar(255) DEFAULT NULL COMMENT '鍔ㄤ綔鎻忚堪',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- PRIMARY KEY (`id`),
- KEY `idx_ai_prompt_publish_log` (`tenant_id`,`scene_code`,`create_time`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI鎻愮ず璇嶅彂甯冩棩蹇�';
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'report_title'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `report_title` varchar(255) DEFAULT NULL COMMENT ''鎶ュ憡鏍囬'' AFTER `conclusion`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'executive_summary'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `executive_summary` longtext COMMENT ''鎵ц鎽樿'' AFTER `report_title`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'evidence_summary'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `evidence_summary` longtext COMMENT ''璇佹嵁鎽樿'' AFTER `executive_summary`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'action_summary'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `action_summary` longtext COMMENT ''寤鸿鍔ㄤ綔'' AFTER `evidence_summary`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'risk_summary'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `risk_summary` longtext COMMENT ''椋庨櫓璇勪及'' AFTER `action_summary`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @sql := IF(
- EXISTS(SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_ai_diagnosis_record' AND COLUMN_NAME = 'report_markdown'),
- 'SELECT 1',
- 'ALTER TABLE `sys_ai_diagnosis_record` ADD COLUMN `report_markdown` longtext COMMENT ''鎶ュ憡Markdown'' AFTER `risk_summary`'
-);
-PREPARE stmt FROM @sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-INSERT INTO `sys_ai_diagnostic_tool_config`
-(`uuid`, `scene_code`, `tool_code`, `tool_name`, `enabled_flag`, `priority`, `tool_prompt`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514315', 'system_diagnose', 'warehouse_summary', '搴撳瓨鎽樿', 1, 10, '缁撳悎搴撳瓨鎽樿鍒ゆ柇搴撲綅鐘舵�併�佸簱瀛樼粨鏋勪笌閲嶇偣鐗╂枡鍒嗗竷銆�', 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇宸ュ叿'
-FROM DUAL WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_diagnostic_tool_config` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `tool_code` = 'warehouse_summary'
-);
-
-INSERT INTO `sys_ai_diagnostic_tool_config`
-(`uuid`, `scene_code`, `tool_code`, `tool_name`, `enabled_flag`, `priority`, `tool_prompt`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514316', 'system_diagnose', 'task_summary', '浠诲姟鎽樿', 1, 20, '缁撳悎浠诲姟鎽樿璇嗗埆绉帇銆佸紓甯哥姸鎬佸拰鏈�杩戝彉鏇翠换鍔°��', 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇宸ュ叿'
-FROM DUAL WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_diagnostic_tool_config` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `tool_code` = 'task_summary'
-);
-
-INSERT INTO `sys_ai_diagnostic_tool_config`
-(`uuid`, `scene_code`, `tool_code`, `tool_name`, `enabled_flag`, `priority`, `tool_prompt`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514317', 'system_diagnose', 'device_site_summary', '璁惧绔欑偣鎽樿', 1, 30, '缁撳悎璁惧绔欑偣鎽樿鍒ゆ柇璁惧鐘舵�併�佸贩閬撳垎甯冨拰鏈�杩戞洿鏂扮珯鐐广��', 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇宸ュ叿'
-FROM DUAL WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_diagnostic_tool_config` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `tool_code` = 'device_site_summary'
-);
-
-INSERT INTO `sys_ai_diagnostic_tool_config`
-(`uuid`, `scene_code`, `tool_code`, `tool_name`, `enabled_flag`, `priority`, `tool_prompt`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514318', 'system_diagnose', 'operation_record', '寮傚父鎿嶄綔鏃ュ織', 1, 40, '閲嶇偣璇嗗埆鏈�杩戝け璐ユ搷浣滃拰楂橀寮傚父銆�', 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇宸ュ叿'
-FROM DUAL WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_diagnostic_tool_config` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `tool_code` = 'operation_record'
-);
-
-INSERT INTO `sys_ai_diagnostic_tool_config`
-(`uuid`, `scene_code`, `tool_code`, `tool_name`, `enabled_flag`, `priority`, `tool_prompt`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514319', 'system_diagnose', 'ai_call_failure', 'AI璋冪敤澶辫触', 1, 50, '閲嶇偣璇嗗埆鏈�杩� AI 璋冪敤澶辫触鐨勬ā鍨嬨�侀敊璇被鍨嬪拰鏃堕棿绐楀彛銆�', 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿璇婃柇宸ュ叿'
-FROM DUAL WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_diagnostic_tool_config` WHERE `tenant_id` = 1 AND `scene_code` = 'system_diagnose' AND `tool_code` = 'ai_call_failure'
-);
-
-SET @system_menu_ai_tool_config := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiToolConfig' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiToolConfig', 1, 'menu.system', '1', 'menu.system', '/system/aiToolConfig', 'aiToolConfig', NULL, NULL, 0, NULL, 'BuildCircle', 14, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_tool_config IS NULL;
-SET @system_menu_ai_tool_config := COALESCE(@system_menu_ai_tool_config, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiToolConfig' LIMIT 1));
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiToolConfig', @system_menu_ai_tool_config, NULL, CONCAT('1,', @system_menu_ai_tool_config), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiToolConfig:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_tool_config AND `authority` = 'system:aiToolConfig:list');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiToolConfig', @system_menu_ai_tool_config, NULL, CONCAT('1,', @system_menu_ai_tool_config), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiToolConfig:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_tool_config AND `authority` = 'system:aiToolConfig:save');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiToolConfig', @system_menu_ai_tool_config, NULL, CONCAT('1,', @system_menu_ai_tool_config), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiToolConfig:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_tool_config AND `authority` = 'system:aiToolConfig:update');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiToolConfig', @system_menu_ai_tool_config, NULL, CONCAT('1,', @system_menu_ai_tool_config), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiToolConfig:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_tool_config AND `authority` = 'system:aiToolConfig:remove');
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `authority` IN ('system:aiToolConfig:list', 'system:aiToolConfig:save', 'system:aiToolConfig:update', 'system:aiToolConfig:remove')
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `component` IN ('aiToolConfig')
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase4.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_diagnosis_plan` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(64) DEFAULT NULL COMMENT '缂栧彿',
- `plan_name` varchar(255) DEFAULT NULL COMMENT '璁″垝鍚嶇О',
- `scene_code` varchar(64) DEFAULT NULL COMMENT '鍦烘櫙缂栫爜',
- `cron_expr` varchar(128) DEFAULT NULL COMMENT 'Cron琛ㄨ揪寮�',
- `prompt` longtext COMMENT '宸℃鎻愮ず璇�',
- `preferred_model_code` varchar(128) DEFAULT NULL COMMENT '浼樺厛妯″瀷缂栫爜',
- `running_flag` int(1) NOT NULL DEFAULT '0' COMMENT '杩愯涓瓄1:鏄�,0:鍚',
- `last_result` int(1) DEFAULT NULL COMMENT '涓婃缁撴灉{2:杩愯涓�,1:鎴愬姛,0:澶辫触}',
- `last_diagnosis_id` bigint(20) DEFAULT NULL COMMENT '涓婃璇婃柇璁板綍ID',
- `last_run_time` datetime DEFAULT NULL COMMENT '涓婃杩愯鏃堕棿',
- `next_run_time` datetime DEFAULT NULL COMMENT '涓嬫杩愯鏃堕棿',
- `last_message` varchar(1000) DEFAULT NULL COMMENT '鏈�杩戞秷鎭�',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_diag_plan_due` (`tenant_id`,`status`,`running_flag`,`next_run_time`,`deleted`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI璇婃柇宸℃璁″垝';
-
-SET @system_menu_ai_plan := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiDiagnosisPlan' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiDiagnosisPlan', 1, 'menu.system', '1', 'menu.system', '/system/aiDiagnosisPlan', 'aiDiagnosisPlan', NULL, NULL, 0, NULL, 'Schedule', 14, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_plan IS NULL;
-SET @system_menu_ai_plan := COALESCE(@system_menu_ai_plan, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiDiagnosisPlan' LIMIT 1));
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiDiagnosisPlan', @system_menu_ai_plan, NULL, CONCAT('1,', @system_menu_ai_plan), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiDiagnosisPlan:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_plan AND `authority` = 'system:aiDiagnosisPlan:list');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiDiagnosisPlan', @system_menu_ai_plan, NULL, CONCAT('1,', @system_menu_ai_plan), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiDiagnosisPlan:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_plan AND `authority` = 'system:aiDiagnosisPlan:save');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiDiagnosisPlan', @system_menu_ai_plan, NULL, CONCAT('1,', @system_menu_ai_plan), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiDiagnosisPlan:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_plan AND `authority` = 'system:aiDiagnosisPlan:update');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiDiagnosisPlan', @system_menu_ai_plan, NULL, CONCAT('1,', @system_menu_ai_plan), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiDiagnosisPlan:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_plan AND `authority` = 'system:aiDiagnosisPlan:remove');
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `authority` IN (
- 'system:aiDiagnosisPlan:list', 'system:aiDiagnosisPlan:save', 'system:aiDiagnosisPlan:update', 'system:aiDiagnosisPlan:remove'
-)
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `component` IN ('aiDiagnosisPlan')
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase5.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-CREATE TABLE IF NOT EXISTS `sys_ai_mcp_mount` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(64) DEFAULT NULL COMMENT '缂栧彿',
- `name` varchar(255) DEFAULT NULL COMMENT '鍚嶇О',
- `mount_code` varchar(128) DEFAULT NULL COMMENT '鎸傝浇缂栫爜',
- `transport_type` varchar(32) DEFAULT NULL COMMENT '浼犺緭绫诲瀷',
- `url` varchar(512) DEFAULT NULL COMMENT '鍦板潃',
- `enabled_flag` int(1) NOT NULL DEFAULT '1' COMMENT '鍚敤{1:鏄�,0:鍚',
- `timeout_ms` int(11) DEFAULT NULL COMMENT '瓒呮椂姣',
- `last_test_result` int(1) DEFAULT NULL COMMENT '涓婃娴嬭瘯缁撴灉{1:鎴愬姛,0:澶辫触}',
- `last_test_time` datetime DEFAULT NULL COMMENT '涓婃娴嬭瘯鏃堕棿',
- `last_test_message` varchar(1000) DEFAULT NULL COMMENT '涓婃娴嬭瘯娑堟伅',
- `last_tool_count` int(11) DEFAULT NULL COMMENT '涓婃宸ュ叿鏁�',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
- `create_by` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_mcp_mount` (`tenant_id`,`mount_code`,`status`,`deleted`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI MCP鎸傝浇';
-
-INSERT INTO `sys_ai_mcp_mount`
-(`uuid`, `name`, `mount_code`, `transport_type`, `url`, `enabled_flag`, `timeout_ms`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
-SELECT '6702082748514325', 'WMS鏈湴MCP', 'wms_local', 'INTERNAL', '/ai/mcp', 1, 10000, 1, 0, 1, 2, NOW(), 2, NOW(), '榛樿鎸傝浇褰撳墠 WMS AI 鍐呯疆宸ュ叿闆嗗悎'
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1 FROM `sys_ai_mcp_mount` WHERE `tenant_id` = 1 AND `mount_code` = 'wms_local'
-);
-
-SET @system_menu_ai_mcp_mount := (
- SELECT `id` FROM `sys_menu` WHERE `component` = 'aiMcpMount' LIMIT 1
-);
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'menu.aiMcpMount', 1, 'menu.system', '1', 'menu.system', '/system/aiMcpMount', 'aiMcpMount', NULL, NULL, 0, NULL, 'Hub', 15, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE @system_menu_ai_mcp_mount IS NULL;
-SET @system_menu_ai_mcp_mount := COALESCE(@system_menu_ai_mcp_mount, LAST_INSERT_ID(), (SELECT `id` FROM `sys_menu` WHERE `component` = 'aiMcpMount' LIMIT 1));
-
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Query AiMcpMount', @system_menu_ai_mcp_mount, NULL, CONCAT('1,', @system_menu_ai_mcp_mount), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiMcpMount:list', NULL, 0, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_mcp_mount AND `authority` = 'system:aiMcpMount:list');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Create AiMcpMount', @system_menu_ai_mcp_mount, NULL, CONCAT('1,', @system_menu_ai_mcp_mount), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiMcpMount:save', NULL, 1, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_mcp_mount AND `authority` = 'system:aiMcpMount:save');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Update AiMcpMount', @system_menu_ai_mcp_mount, NULL, CONCAT('1,', @system_menu_ai_mcp_mount), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiMcpMount:update', NULL, 2, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_mcp_mount AND `authority` = 'system:aiMcpMount:update');
-INSERT INTO `sys_menu`
-(`name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
-SELECT 'Delete AiMcpMount', @system_menu_ai_mcp_mount, NULL, CONCAT('1,', @system_menu_ai_mcp_mount), NULL, NULL, NULL, NULL, NULL, 1, 'system:aiMcpMount:remove', NULL, 3, NULL, 1, 1, 0, NOW(), 2, NOW(), 2, NULL
-FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `parent_id` = @system_menu_ai_mcp_mount AND `authority` = 'system:aiMcpMount:remove');
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `authority` IN (
- 'system:aiMcpMount:list', 'system:aiMcpMount:save', 'system:aiMcpMount:update', 'system:aiMcpMount:remove'
-)
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
-SELECT 1, `id` FROM `sys_menu`
-WHERE `component` IN ('aiMcpMount')
-AND NOT EXISTS (
- SELECT 1 FROM `sys_role_menu`
- WHERE `role_id` = 1 AND `menu_id` = `sys_menu`.`id`
-);
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase6_mcp_console.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-SET @table_schema = DATABASE();
-SET @add_auth_type_sql = (
- SELECT IF(
- COUNT(*) = 0,
- 'ALTER TABLE `sys_ai_mcp_mount` ADD COLUMN `auth_type` varchar(32) DEFAULT ''NONE'' COMMENT ''璁よ瘉鏂瑰紡'' AFTER `url`',
- 'SELECT 1'
- )
- FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = @table_schema
- AND TABLE_NAME = 'sys_ai_mcp_mount'
- AND COLUMN_NAME = 'auth_type'
-);
-PREPARE stmt FROM @add_auth_type_sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @add_auth_value_sql = (
- SELECT IF(
- COUNT(*) = 0,
- 'ALTER TABLE `sys_ai_mcp_mount` ADD COLUMN `auth_value` varchar(512) DEFAULT NULL COMMENT ''璁よ瘉淇℃伅'' AFTER `auth_type`',
- 'SELECT 1'
- )
- FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = @table_schema
- AND TABLE_NAME = 'sys_ai_mcp_mount'
- AND COLUMN_NAME = 'auth_value'
-);
-PREPARE stmt FROM @add_auth_value_sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-SET @add_usage_scope_sql = (
- SELECT IF(
- COUNT(*) = 0,
- 'ALTER TABLE `sys_ai_mcp_mount` ADD COLUMN `usage_scope` varchar(32) DEFAULT ''DIAGNOSE_ONLY'' COMMENT ''鐢ㄩ�旇寖鍥�'' AFTER `auth_value`',
- 'SELECT 1'
- )
- FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = @table_schema
- AND TABLE_NAME = 'sys_ai_mcp_mount'
- AND COLUMN_NAME = 'usage_scope'
-);
-PREPARE stmt FROM @add_usage_scope_sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-UPDATE `sys_ai_mcp_mount`
-SET `auth_type` = COALESCE(NULLIF(`auth_type`, ''), 'NONE'),
- `usage_scope` = CASE
- WHEN `transport_type` = 'INTERNAL' THEN 'CHAT_AND_DIAGNOSE'
- ELSE COALESCE(NULLIF(`usage_scope`, ''), 'DIAGNOSE_ONLY')
- END
-WHERE 1 = 1;
-
-SET FOREIGN_KEY_CHECKS = 1;
-
--- >>> 20260316_ai_phase7_tool_usage_scope.sql
-SET NAMES utf8mb4;
-SET FOREIGN_KEY_CHECKS = 0;
-
-SET @table_schema = DATABASE();
-SET @add_usage_scope_sql = (
- SELECT IF(
- COUNT(*) = 0,
- 'ALTER TABLE `sys_ai_diagnostic_tool_config` ADD COLUMN `usage_scope` varchar(32) DEFAULT ''DIAGNOSE_ONLY'' COMMENT ''鐢ㄩ�旇寖鍥�'' AFTER `tool_prompt`',
- 'SELECT 1'
- )
- FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = @table_schema
- AND TABLE_NAME = 'sys_ai_diagnostic_tool_config'
- AND COLUMN_NAME = 'usage_scope'
-);
-PREPARE stmt FROM @add_usage_scope_sql;
-EXECUTE stmt;
-DEALLOCATE PREPARE stmt;
-
-UPDATE `sys_ai_diagnostic_tool_config`
-SET `usage_scope` = CASE
- WHEN `enabled_flag` IS NOT NULL AND `enabled_flag` <> 1 THEN 'DISABLED'
- WHEN `scene_code` IS NULL OR TRIM(`scene_code`) = '' THEN 'CHAT_AND_DIAGNOSE'
- ELSE 'DIAGNOSE_ONLY'
-END
-WHERE `usage_scope` IS NULL OR TRIM(`usage_scope`) = '';
-
-SET FOREIGN_KEY_CHECKS = 1;
-
diff --git a/version/db/init.sql b/version/db/init.sql
index 0a749c5..97a760c 100644
--- a/version/db/init.sql
+++ b/version/db/init.sql
@@ -50,41 +50,6 @@
COMMIT;
-- ----------------------------
--- Table structure for sys_ai_param
--- ----------------------------
-DROP TABLE IF EXISTS `sys_ai_param`;
-CREATE TABLE `sys_ai_param` (
- `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
- `uuid` varchar(255) DEFAULT NULL COMMENT '缂栧彿',
- `name` varchar(255) DEFAULT NULL COMMENT '鍚嶇О',
- `model_code` varchar(255) DEFAULT NULL COMMENT '妯″瀷缂栫爜',
- `provider` varchar(255) DEFAULT NULL COMMENT '渚涘簲鍟�',
- `chat_url` varchar(512) DEFAULT NULL COMMENT '鑱婂ぉ鍦板潃',
- `api_key` varchar(512) DEFAULT NULL COMMENT 'API瀵嗛挜',
- `model_name` varchar(255) DEFAULT NULL COMMENT '妯″瀷鍚嶇О',
- `system_prompt` text COMMENT '绯荤粺鎻愮ず璇�',
- `max_context_messages` int(11) DEFAULT NULL COMMENT '涓婁笅鏂囪疆鏁�',
- `default_flag` int(1) NOT NULL DEFAULT '0' COMMENT '榛樿妯″瀷{1:鏄�,0:鍚',
- `sort` int(11) DEFAULT NULL COMMENT '鎺掑簭',
- `status` int(1) NOT NULL DEFAULT '1' COMMENT '鐘舵�亄1:姝e父,0:鍐荤粨}',
- `deleted` int(1) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎{1:鏄�,0:鍚',
- `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛[sys_tenant]',
- `create_by` bigint(20) DEFAULT NULL COMMENT '娣诲姞浜哄憳[sys_user]',
- `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '娣诲姞鏃堕棿',
- `update_by` bigint(20) DEFAULT NULL COMMENT '淇敼浜哄憳[sys_user]',
- `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
- `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
- PRIMARY KEY (`id`),
- KEY `idx_ai_param_model_code` (`model_code`),
- KEY `idx_ai_param_deleted_code` (`deleted`,`model_code`)
-) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-
--- ----------------------------
--- Records of sys_ai_param
--- ----------------------------
-BEGIN;
-INSERT INTO `sys_ai_param` (`id`, `uuid`, `name`, `model_code`, `provider`, `chat_url`, `api_key`, `model_name`, `system_prompt`, `max_context_messages`, `default_flag`, `sort`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`) VALUES (1, '6702082748514305', 'DEEPSEEK', 'deepseek-ai/DeepSeek-V3.2', 'openai', 'https://api.siliconflow.cn', NULL, 'deepseek-ai/DeepSeek-V3.2', '浣犳槸WMS绯荤粺鍐呯殑鏅鸿兘鍔╂墜锛屽洖绛旀椂浼樺厛淇濇寔鍑嗙‘銆佺畝娲侊紝骞剁粨鍚堜笂涓嬫枃甯姪鐢ㄦ埛鐞嗚В浠撳偍涓氬姟銆�', 12, 1, 1, 1, 0, 1, 2, '2026-03-11 14:13:22', 2, '2026-03-11 15:03:30', '榛樿婕旂ず妯″瀷');
-COMMIT;
-- ----------------------------
-- Table structure for sys_dept
@@ -235,12 +200,6 @@
INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (44, 'Create Tenant', 42, NULL, '1.42', NULL, NULL, NULL, NULL, NULL, 1, 'system:tenant:save', NULL, 1, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (45, 'Update Tenant', 42, NULL, '1.42', NULL, NULL, NULL, NULL, NULL, 1, 'system:tenant:update', NULL, 2, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (46, 'Delete Tenant', 42, NULL, '1.42', NULL, NULL, NULL, NULL, NULL, 1, 'system:tenant:remove', NULL, 3, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (47, 'menu.aiParam', 1, 'menu.system', '1', 'menu.system', '/system/aiParam', 'aiParam', NULL, NULL, 0, NULL, 'SmartToy', 9, NULL, 1, 1, 0, NULL, NULL, '2024-09-10 15:08:30', 2, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (48, 'Query AiParam', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 0, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (49, 'Create AiParam', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:save', NULL, 1, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (50, 'Update AiParam', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:update', NULL, 2, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (51, 'Delete AiParam', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:remove', NULL, 3, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (52, 'Export AiParam', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:aiParam:list', NULL, 4, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
COMMIT;
-- ----------------------------
--
Gitblit v1.9.1