From dc8aa7a9d19b3094e201eaafa1b7b382bf4f5f57 Mon Sep 17 00:00:00 2001 From: wanghanlin <1533525126@qq.com> Date: Wed, 2 Sep 2026 15:20:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(backend):=20=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E5=AD=97=E6=AE=B5=E7=99=BD=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=B5=B0=20SortUtils=20=E9=98=B2=20SQL=20?= =?UTF-8?q?=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SortUtils.resolveColumn 统一排序字段白名单映射,未知字段回退默认列 - 各 Controller/Service 列表接口新增 sortField/sortOrder 参数并接入白名单 - 前端列表页列加 sorter 并透传服务端排序参数 --- frontend/src/api/api-key.ts | 7 +++- frontend/src/api/conversation.ts | 4 +- frontend/src/api/faq.ts | 4 +- frontend/src/api/llm-trace.ts | 4 ++ frontend/src/api/mcp-server.ts | 7 +++- frontend/src/api/model-config.ts | 4 +- frontend/src/api/sensitive-word.ts | 8 +++- frontend/src/api/user.ts | 4 +- frontend/src/api/webhook.ts | 7 +++- frontend/src/views/AccountManager.vue | 2 +- frontend/src/views/ApiKeyManager.vue | 10 +++-- frontend/src/views/AuditLogManager.vue | 10 +++-- frontend/src/views/CategoryManager.vue | 23 ++++------- frontend/src/views/ConversationManager.vue | 10 +++-- frontend/src/views/DashboardPanel.vue | 4 +- frontend/src/views/FaqManager.vue | 16 ++++---- frontend/src/views/FeedbackOps.vue | 16 +++++--- frontend/src/views/McpServerManager.vue | 10 +++-- frontend/src/views/ModelConfigManager.vue | 16 ++++---- frontend/src/views/PromptTracePanel.vue | 40 ++++++++++--------- frontend/src/views/SensitiveWordManager.vue | 18 +++++---- frontend/src/views/UserManager.vue | 14 ++++--- frontend/src/views/WebhookManager.vue | 10 +++-- .../com/wok/supportbot/common/SortUtils.java | 40 +++++++++++++++++++ .../controller/AiModelConfigController.java | 6 ++- .../controller/ApiKeyController.java | 6 ++- .../controller/ConversationController.java | 6 ++- .../supportbot/controller/FaqController.java | 6 ++- .../controller/LlmCallTraceController.java | 6 ++- .../controller/McpServerConfigController.java | 6 ++- .../controller/MessageFeedbackController.java | 6 ++- .../controller/SensitiveWordController.java | 22 ++++++++-- .../controller/SysUserController.java | 6 ++- .../controller/WebhookController.java | 6 ++- .../service/AiModelConfigService.java | 29 +++++++++++--- .../wok/supportbot/service/ApiKeyService.java | 17 +++++++- .../service/ConversationService.java | 24 ++++++++--- .../wok/supportbot/service/FaqService.java | 23 ++++++++++- .../service/LlmCallTraceService.java | 26 +++++++++++- .../service/McpServerConfigService.java | 21 ++++++++-- .../service/MessageFeedbackService.java | 22 ++++++++-- .../service/SensitiveWordService.java | 19 ++++++++- .../supportbot/service/SysUserService.java | 19 +++++++-- .../supportbot/service/WebhookService.java | 17 +++++++- 44 files changed, 429 insertions(+), 152 deletions(-) create mode 100644 src/main/java/com/wok/supportbot/common/SortUtils.java diff --git a/frontend/src/api/api-key.ts b/frontend/src/api/api-key.ts index 2af1346..e4f8ab9 100644 --- a/frontend/src/api/api-key.ts +++ b/frontend/src/api/api-key.ts @@ -1,8 +1,11 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listApiKeys(page = 1, size = 20): Promise { - return request.get(`/api-key/list?page=${page}&size=${size}`).then(r => r.data) +export function listApiKeys(page = 1, size = 20, sortField?: string, sortOrder?: string): Promise { + let path = `/api-key/list?page=${page}&size=${size}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` + return request.get(path).then(r => r.data) } /** 拉取全部 API Key(供筛选下拉,最多 1000 条) */ export function listAllApiKeys(): Promise { diff --git a/frontend/src/api/conversation.ts b/frontend/src/api/conversation.ts index c73069c..68423ca 100644 --- a/frontend/src/api/conversation.ts +++ b/frontend/src/api/conversation.ts @@ -1,11 +1,13 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listConversations(page = 1, size = 10, keyword?: string, accountId?: string, roleId?: string): Promise { +export function listConversations(page = 1, size = 10, keyword?: string, accountId?: string, roleId?: string, sortField?: string, sortOrder?: string): Promise { let path = `/conversation/list?page=${page}&size=${size}` if (keyword) path += `&keyword=${encodeURIComponent(keyword)}` if (accountId) path += `&accountId=${encodeURIComponent(accountId)}` if (roleId) path += `&roleId=${encodeURIComponent(roleId)}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } export function getConversationDetail(conversationId: string): Promise { return request.get(`/conversation/${conversationId}`).then(r => r.data) } diff --git a/frontend/src/api/faq.ts b/frontend/src/api/faq.ts index 59eaf9f..64e47de 100644 --- a/frontend/src/api/faq.ts +++ b/frontend/src/api/faq.ts @@ -1,11 +1,13 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listFaqs(page = 1, size = 20, keyword?: string, categoryId?: string, status?: string): Promise { +export function listFaqs(page = 1, size = 20, keyword?: string, categoryId?: string, status?: string, sortField?: string, sortOrder?: string): Promise { let path = `/faq/list?page=${page}&size=${size}` if (keyword) path += `&keyword=${encodeURIComponent(keyword)}` if (categoryId) path += `&categoryId=${categoryId}` if (status) path += `&status=${encodeURIComponent(status)}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } export function createFaq(data: any): Promise { return request.post('/faq', data).then(r => r.data) } diff --git a/frontend/src/api/llm-trace.ts b/frontend/src/api/llm-trace.ts index 6b73175..72cdf6d 100644 --- a/frontend/src/api/llm-trace.ts +++ b/frontend/src/api/llm-trace.ts @@ -13,6 +13,8 @@ export interface LlmTraceQuery { keyword?: string apiKeyId?: string errorType?: string + sortField?: string + sortOrder?: string } /** 分页查询调用记录 */ @@ -28,6 +30,8 @@ export function listLlmTraces(query: LlmTraceQuery = {}): Promise { if (query.keyword) params.set('keyword', query.keyword) if (query.apiKeyId) params.set('apiKeyId', query.apiKeyId) if (query.errorType) params.set('errorType', query.errorType) + if (query.sortField) params.set('sortField', query.sortField) + if (query.sortOrder) params.set('sortOrder', query.sortOrder) return request.get(`/llm-trace/list?${params.toString()}`).then(r => r.data) } diff --git a/frontend/src/api/mcp-server.ts b/frontend/src/api/mcp-server.ts index b098b67..6ca2d37 100644 --- a/frontend/src/api/mcp-server.ts +++ b/frontend/src/api/mcp-server.ts @@ -1,7 +1,12 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listMcpServers(page = 1, size = 10): Promise { return request.get(`/mcp-server/list?page=${page}&size=${size}`).then(r => r.data) } +export function listMcpServers(page = 1, size = 10, sortField?: string, sortOrder?: string): Promise { + let path = `/mcp-server/list?page=${page}&size=${size}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` + return request.get(path).then(r => r.data) +} export function getMcpServerDetail(id: string): Promise { return request.get(`/mcp-server/${id}`).then(r => r.data) } export function createMcpServer(data: any): Promise { return request.post('/mcp-server', data).then(r => r.data) } export function updateMcpServer(id: string, data: any): Promise { return request.put(`/mcp-server/${id}`, data).then(r => r.data) } diff --git a/frontend/src/api/model-config.ts b/frontend/src/api/model-config.ts index 17c78d9..743e6d6 100644 --- a/frontend/src/api/model-config.ts +++ b/frontend/src/api/model-config.ts @@ -1,9 +1,11 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listModelConfigs(page = 1, size = 10, appType?: string): Promise { +export function listModelConfigs(page = 1, size = 10, appType?: string, sortField?: string, sortOrder?: string): Promise { let path = `/model-config/list?page=${page}&size=${size}` if (appType) path += `&appType=${encodeURIComponent(appType)}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } export function getModelConfigDetail(id: string): Promise { return request.get(`/model-config/${id}`).then(r => r.data) } diff --git a/frontend/src/api/sensitive-word.ts b/frontend/src/api/sensitive-word.ts index 6a97f9d..12f419c 100644 --- a/frontend/src/api/sensitive-word.ts +++ b/frontend/src/api/sensitive-word.ts @@ -1,11 +1,13 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listSensitiveWords(page = 1, size = 20, keyword?: string, category?: string, level?: number): Promise { +export function listSensitiveWords(page = 1, size = 20, keyword?: string, category?: string, level?: number, sortField?: string, sortOrder?: string): Promise { let path = `/sensitive-word/list?page=${page}&size=${size}` if (keyword) path += `&keyword=${encodeURIComponent(keyword)}` if (category) path += `&category=${encodeURIComponent(category)}` if (level !== undefined && level !== null) path += `&level=${level}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } export function createSensitiveWord(data: any): Promise { return request.post('/sensitive-word', data).then(r => r.data) } @@ -14,8 +16,10 @@ export function updateSensitiveWord(id: string, data: any): Promise export function toggleSensitiveWord(id: string, isActive: boolean): Promise { return updateSensitiveWord(id, { isActive }) } export function deleteSensitiveWord(id: string): Promise { return request.delete(`/sensitive-word/${id}`).then(r => r.data) } export function batchImportSensitiveWords(data: { words: string[]; category: string; level: number }): Promise { return request.post('/sensitive-word/batch-import', data).then(r => r.data) } -export function listAuditLogs(page = 1, size = 20, sessionId?: string): Promise { +export function listAuditLogs(page = 1, size = 20, sessionId?: string, sortField?: string, sortOrder?: string): Promise { let path = `/sensitive-word/audit-log?page=${page}&size=${size}` if (sessionId) path += `&sessionId=${encodeURIComponent(sessionId)}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts index d2b7ede..1974b7c 100644 --- a/frontend/src/api/user.ts +++ b/frontend/src/api/user.ts @@ -1,10 +1,12 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listSysUsers(page = 1, size = 20, keyword?: string, enabled?: boolean): Promise { +export function listSysUsers(page = 1, size = 20, keyword?: string, enabled?: boolean, sortField?: string, sortOrder?: string): Promise { let path = `/sys-user/list?page=${page}&size=${size}` if (keyword) path += `&keyword=${encodeURIComponent(keyword)}` if (enabled !== undefined && enabled !== null) path += `&enabled=${enabled}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) } export function getSysUserDetail(id: string): Promise { return request.get(`/sys-user/${id}`).then(r => r.data) } diff --git a/frontend/src/api/webhook.ts b/frontend/src/api/webhook.ts index c06a670..a60e60e 100644 --- a/frontend/src/api/webhook.ts +++ b/frontend/src/api/webhook.ts @@ -1,7 +1,12 @@ import request from './request' import type { ApiResponse } from '@/types/api' -export function listWebhooks(page = 1, size = 20): Promise { return request.get(`/webhook/list?page=${page}&size=${size}`).then(r => r.data) } +export function listWebhooks(page = 1, size = 20, sortField?: string, sortOrder?: string): Promise { + let path = `/webhook/list?page=${page}&size=${size}` + if (sortField) path += `&sortField=${sortField}` + if (sortOrder) path += `&sortOrder=${sortOrder}` + return request.get(path).then(r => r.data) +} export function createWebhook(data: any): Promise { return request.post('/webhook', data).then(r => r.data) } export function updateWebhook(id: string, data: any): Promise { return request.put(`/webhook/${id}`, data).then(r => r.data) } export function deleteWebhook(id: string): Promise { return request.delete(`/webhook/${id}`).then(r => r.data) } diff --git a/frontend/src/views/AccountManager.vue b/frontend/src/views/AccountManager.vue index 776482f..9146807 100644 --- a/frontend/src/views/AccountManager.vue +++ b/frontend/src/views/AccountManager.vue @@ -37,7 +37,7 @@ const { confirm } = useConfirm() const accounts=ref([]) -const columns=[{colKey:'name',title:'账号名称',width:160},{colKey:'roles',title:'绑定角色',width:240},{colKey:'op',title:'操作',width:120}] +const columns=[{colKey:'name',title:'账号名称',width:160,sorter:true},{colKey:'roles',title:'绑定角色',width:240,sorter:true},{colKey:'op',title:'操作',width:120}] const modal=reactive({visible:false,isEdit:false,id:null as any,name:''}) diff --git a/frontend/src/views/ApiKeyManager.vue b/frontend/src/views/ApiKeyManager.vue index f02b174..bc464d4 100644 --- a/frontend/src/views/ApiKeyManager.vue +++ b/frontend/src/views/ApiKeyManager.vue @@ -5,8 +5,8 @@
共 {{ total }} 个 Key
- +