From a27e9aaec1cf6a16ac6d73bb7b10793ee15cbc95 Mon Sep 17 00:00:00 2001 From: sladro Date: Wed, 17 Sep 2025 18:54:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90WebSocket=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E7=B3=BB=E7=BB=9F=E6=8E=A5=E5=85=A5=E5=92=8C=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E9=9D=A2=E6=9D=BF=E7=8A=B6=E6=80=81=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在API客户端添加WebSocket日志记录钩子,所有CAD操作自动记录到后台 - 修复信息面板软件状态字段名(is_running vs status)和分页逻辑错误 - CAD连接成功时自动同步更新信息面板软件状态显示 - WebSocket连接时主动获取历史日志,实现完整的日志显示功能 - 清理调试输出,优化用户体验 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 20 +++++++++- src/components/layout/InfoManagementPanel.vue | 39 ++++++++++++------- src/services/apiClient.js | 32 +++++++++++++++ src/services/websocketService.js | 29 ++++---------- 4 files changed, 83 insertions(+), 37 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index de33c01..0b6e7b0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -142,6 +142,8 @@ const handleOperation = async () => { - ✅ Creo专属页面框架(模型分析、导出工具) - ✅ 通用格式转换页面框架 - ✅ 智能薄壳化分析接口(startShellAnalysis) +- ✅ WebSocket日志系统完整接入 +- ✅ 信息面板实时日志显示和软件状态同步 ## 页面添加标准流程 @@ -153,13 +155,29 @@ const handleOperation = async () => { - 添加页面切换处理方法 4. **组件间通信** - 使用emit事件,不用router.push +## 日志系统实现总结 + +### ✅ 完成内容 +1. **API钩子集成** - 所有CAD操作自动通过WebSocket记录日志到后台 +2. **实时状态同步** - CAD连接成功后自动更新信息面板软件状态 +3. **字段名修复** - 修复了后台返回`is_running`字段与前端`status`字段不匹配的问题 +4. **分页逻辑修复** - 修复了翻页时重复调用后台接口导致的分页错乱 +5. **日志实时显示** - 信息面板实时接收并按时间排序显示操作日志 + +### 🔧 关键修改 +- **apiClient.js**: 添加WebSocket日志记录钩子和CAD连接成功时的状态同步 +- **InfoManagementPanel.vue**: 修复软件状态字段名和分页逻辑 +- **websocketService.js**: 连接时主动获取历史日志,清理调试输出 + +### 💡 数据流 +`CAD操作` → `API调用` → `后处理钩子` → `WebSocket记录` → `后台存储` → `实时推送` → `信息面板显示` + ## 待开发功能 - Creo模型分析、导出工具具体功能实现 - 通用格式转换具体功能实现 - Revit和PDMS集成 - 3D模型查看器 -- 实时日志系统 --- diff --git a/src/components/layout/InfoManagementPanel.vue b/src/components/layout/InfoManagementPanel.vue index ba1a09b..3ca9abe 100644 --- a/src/components/layout/InfoManagementPanel.vue +++ b/src/components/layout/InfoManagementPanel.vue @@ -63,21 +63,21 @@
{{ software.name || software.id }}
-
- {{ getStatusText(software.status) }} +
+ {{ software.is_running ? '运行中' : '已停止' }}
@@ -134,13 +134,20 @@ class="log-item" >
-
{{ log.operation }}
+
+ + + + {{ log.operation }} +
{{ formatTime(log.timestamp) }}
{{ log.details }}
+ {{ log.operation_category }} {{ log.user_id }} {{ getLogStatusText(log.status) }} + {{ log.duration }}ms
@@ -236,8 +243,8 @@ const operationFilter = ref('') const currentPage = ref(1) const logsPerPage = 10 -// 过滤后的日志 -const filteredLogs = computed(() => { +// 过滤后的完整日志 +const allFilteredLogs = computed(() => { let filtered = logs.value if (logTypeFilter.value) { @@ -252,14 +259,19 @@ const filteredLogs = computed(() => { ) } + return filtered +}) + +// 当前页显示的日志 +const filteredLogs = computed(() => { const start = (currentPage.value - 1) * logsPerPage const end = start + logsPerPage - return filtered.slice(start, end) + return allFilteredLogs.value.slice(start, end) }) // 总页数 const totalPages = computed(() => { - return Math.ceil(logs.value.length / logsPerPage) + return Math.ceil(allFilteredLogs.value.length / logsPerPage) }) // WebSocket事件监听器管理 @@ -283,9 +295,10 @@ const initListeners = () => { // 监听日志更新 const onLogUpdate = (logData) => { if (Array.isArray(logData)) { - logs.value = logData + // 新日志添加到顶部,保持最新日志在前 + logs.value = [...logData].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)) } else if (logData.logs) { - logs.value = logData.logs + logs.value = [...logData.logs].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)) } } websocketService.on('onLogUpdate', onLogUpdate) @@ -433,14 +446,12 @@ const formatTime = (timestamp) => { const prevPage = () => { if (currentPage.value > 1) { currentPage.value-- - refreshLogs() } } const nextPage = () => { if (currentPage.value < totalPages.value) { currentPage.value++ - refreshLogs() } } diff --git a/src/services/apiClient.js b/src/services/apiClient.js index 66673ed..3bce57b 100644 --- a/src/services/apiClient.js +++ b/src/services/apiClient.js @@ -2,7 +2,9 @@ // 包含拦截器、日志记录和后处理能力 import { getDefaultRequestConfig, getNotificationConfig } from '@/config/cad' +import cadConfig from '@/config/cad' import { ElNotification, ElLoading } from 'element-plus' +import websocketService from './websocketService' // 日志记录系统 class ApiLogger { @@ -59,6 +61,7 @@ class ApiClient { // 设置通知处理器 setupNotificationHandler() { + // 通知处理器 PostProcessManager.addHandler(async (response, context) => { // 默认显示通知,可通过配置关闭 if (context.showNotification !== false && context.operationContext) { @@ -84,6 +87,35 @@ class ApiClient { } } }) + + // WebSocket日志记录处理器 + PostProcessManager.addHandler(async (response, context) => { + if (context.operationContext) { + const { software, operation } = context.operationContext + const status = response.ok ? 'success' : 'failed' + const details = response.ok ? + `${software} ${operation}执行成功` : + `${software} ${operation}执行失败: ${response.statusText || '未知错误'}` + + // 发送到WebSocket后台记录 + websocketService.logOperation( + `${software} - ${operation}`, + details, + { + action_type: 'execute', + target_object: context.url, + status: status, + duration: context.duration || 0, + operation_category: 'CAD操作' + } + ) + + // CAD连接成功时同步更新信息面板软件状态 + if (response.ok && operation === '连接测试') { + websocketService.getSoftwareList() + } + } + }) } // 核心请求方法 diff --git a/src/services/websocketService.js b/src/services/websocketService.js index 28dfca0..eb4ef9d 100644 --- a/src/services/websocketService.js +++ b/src/services/websocketService.js @@ -31,7 +31,6 @@ class WebSocketService { onOperationTypesUpdate: [] } - console.log('WebSocketService 初始化完成') } // 获取WebSocket连接URL @@ -47,12 +46,10 @@ class WebSocketService { } const url = this.getConnectionUrl() - console.log('开始连接统一管理后台:', url) this.setState('CONNECTING') this.ws = new WebSocket(url) this.ws.onopen = (event) => { - console.log('统一管理后台连接成功') this.setState('CONNECTED') this.reconnectAttempts = 0 this.startHeartbeat() @@ -63,13 +60,13 @@ class WebSocketService { this.getSoftwareList() this.getLogStats() this.getOperationTypes() + this.queryLogs() }, 500) } this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data) - console.log('收到管理后台消息:', message) this.handleMessage(message) this.emit('onMessage', message) } catch (error) { @@ -78,7 +75,6 @@ class WebSocketService { } this.ws.onclose = (event) => { - console.log('统一管理后台连接关闭:', event) this.setState('DISCONNECTED') this.stopHeartbeat() this.emit('onClose', event) @@ -86,7 +82,6 @@ class WebSocketService { // 自动重连 if (!event.wasClean && this.reconnectAttempts < this.config.RECONNECT_ATTEMPTS) { const delay = this.config.RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts) - console.log(`统一管理后台将在 ${delay}ms 后重连,第 ${this.reconnectAttempts + 1} 次尝试`) setTimeout(() => { this.reconnectAttempts++ this.connect() @@ -95,7 +90,6 @@ class WebSocketService { } this.ws.onerror = (event) => { - console.error('统一管理后台连接错误:', event) this.setState('ERROR') this.emit('onError', event) } @@ -103,7 +97,6 @@ class WebSocketService { // 断开连接 disconnect() { - console.log('主动断开统一管理后台连接') if (this.ws) { this.ws.close(1000, 'Client disconnect') this.ws = null @@ -116,11 +109,9 @@ class WebSocketService { send(message) { if (this.connectionState === 'CONNECTED' && this.ws) { const jsonMessage = JSON.stringify(message) - console.log('发送管理后台消息:', message) this.ws.send(jsonMessage) return true } else { - console.warn('统一管理后台未连接,消息发送失败:', message) return false } } @@ -132,13 +123,17 @@ class WebSocketService { this.handleInfoMessage(message) break case 'heartbeat': - console.log('收到心跳响应') break case 'software_list_update': this.handleSoftwareListUpdate(message) break case 'log_recorded': - console.log('操作日志已记录:', message.data) + break + case 'software_started': + case 'software_stopped': + case 'software_restarted': + // 软件状态变更后自动刷新软件列表 + this.getSoftwareList() break case 'error': console.error('管理后台错误:', message.message) @@ -181,7 +176,6 @@ class WebSocketService { handleSoftwareListUpdate(message) { if (message.data && message.data.software_list) { this.softwareList = message.data.software_list - console.log('软件列表已更新:', this.softwareList) this.emit('onSoftwareUpdate', this.softwareList) } } @@ -208,7 +202,6 @@ class WebSocketService { return false } - console.log('启动软件:', softwareId) return this.send({ type: 'start_software', software_id: softwareId @@ -222,7 +215,6 @@ class WebSocketService { return false } - console.log('停止软件:', softwareId) return this.send({ type: 'stop_software', software_id: softwareId @@ -236,7 +228,6 @@ class WebSocketService { return false } - console.log('重启软件:', softwareId) return this.send({ type: 'restart_software', software_id: softwareId @@ -261,7 +252,6 @@ class WebSocketService { operation_category: options.operation_category || '软件控制' } - console.log('记录操作日志:', logData) return this.send(logData) } @@ -281,7 +271,6 @@ class WebSocketService { if (filters.start_time) queryData.start_time = filters.start_time if (filters.end_time) queryData.end_time = filters.end_time - console.log('查询操作日志:', queryData) return this.send(queryData) } @@ -300,19 +289,16 @@ class WebSocketService { // API方法 - 获取日志统计信息 getLogStats() { - console.log('获取日志统计信息') return this.send({ type: 'get_log_stats' }) } // API方法 - 清理过期日志 cleanupLogs() { - console.log('清理过期日志') return this.send({ type: 'cleanup_logs' }) } // API方法 - 获取操作类型列表 getOperationTypes() { - console.log('获取操作类型列表') return this.send({ type: 'get_operation_types' }) } @@ -337,7 +323,6 @@ class WebSocketService { if (this.connectionState !== newState) { const oldState = this.connectionState this.connectionState = newState - console.log(`统一管理后台状态变化: ${oldState} -> ${newState}`) this.emit('onStateChange', { oldState, newState }) } }