feat: 优化数据分析页面,添加Excel工单导入功能

- 优化数据分析页面,添加可定制的图表功能
- 支持多种图表类型:折线图、柱状图、饼图、环形图、雷达图、极坐标图
- 添加图表定制功能:时间范围选择、数据维度选择
- 实现Excel工单导入功能,支持详情.xlsx文件
- 添加工单编辑功能,包括前端UI和后端API
- 修复WebSocket连接错误,处理invalid Connection header问题
- 简化预警管理参数,改为卡片式选择
- 实现Agent主动调用,无需人工干预
- 改进知识库导入,结合累计工单内容与大模型输出
This commit is contained in:
zhaojie
2025-09-10 23:13:08 +08:00
parent e08b570f22
commit 0c03ff20aa
16 changed files with 3077 additions and 51 deletions

View File

@@ -2408,3 +2408,704 @@ WHERE knowledge_entries.is_active = true AND knowledge_entries.is_verified = tru
2025-09-08 15:24:31,102 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:24:31] "GET /api/health HTTP/1.1" 200 - 2025-09-08 15:24:31,102 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:24:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:25:31,094 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:25:31] "GET /api/health HTTP/1.1" 200 - 2025-09-08 15:25:31,094 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:25:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:26:31,092 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:26:31] "GET /api/health HTTP/1.1" 200 - 2025-09-08 15:26:31,092 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:26:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:27:31,139 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:27:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:28:31,130 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:28:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:29:31,187 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:29:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:30:31,159 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:30:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:31:31,136 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:31:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:32:31,146 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:32:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:33:31,133 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:33:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:35:40,176 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:35:40] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:36:31,097 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:36:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:37:31,132 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:37:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:38:31,102 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:38:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:39:31,100 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:39:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:40:31,116 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:40:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:41:31,099 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:41:31] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:43:00,051 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:43:00] "GET /api/health HTTP/1.1" 200 -
2025-09-08 15:43:31,110 - werkzeug - INFO - 127.0.0.1 - - [08/Sep/2025 15:43:31] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:01,003 - __main__ - INFO - 正在启动TSP智能助手综合管理平台...
2025-09-10 22:36:02,691 - src.core.database - INFO - 数据库初始化成功
2025-09-10 22:36:05,024 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,034 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,035 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:36:05,044 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,051 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,052 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:36:05,052 - src.agent.tool_manager - INFO - 注册工具: search_knowledge
2025-09-10 22:36:05,052 - src.agent.tool_manager - INFO - 注册工具: create_work_order
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: update_work_order
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: generate_response
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: analyze_data
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: send_notification
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: schedule_task
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: web_search
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: file_operation
2025-09-10 22:36:05,053 - src.agent.tool_manager - INFO - 注册工具: database_query
2025-09-10 22:36:05,054 - src.agent.tool_manager - INFO - 已注册 10 个默认工具
2025-09-10 22:36:05,054 - src.agent.agent_core - INFO - Agent核心初始化完成
2025-09-10 22:36:05,054 - src.agent_assistant - INFO - TSP Agent助手初始化完成
2025-09-10 22:36:05,063 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,142 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.26.238:5000
2025-09-10 22:36:05,143 - werkzeug - INFO - Press CTRL+C to quit
2025-09-10 22:36:05,179 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:36:05,183 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765
2025-09-10 22:36:05,188 - websockets.server - INFO - server listening on [::1]:8765
2025-09-10 22:36:05,189 - websockets.server - INFO - server listening on 127.0.0.1:8765
2025-09-10 22:36:11,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:11] "GET / HTTP/1.1" 200 -
2025-09-10 22:36:11,437 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:11] "GET /static/js/dashboard.js HTTP/1.1" 200 -
2025-09-10 22:36:13,910 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:14,178 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:36:14,179 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:36:14,194 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /api/system/info HTTP/1.1" 200 -
2025-09-10 22:36:14,214 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:36:14,214 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:36:14,274 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:14] "GET /favicon.ico HTTP/1.1" 404 -
2025-09-10 22:36:16,244 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:16] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:36:17,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:17] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:36:18,891 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:18] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:19,231 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:19] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:23,885 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:23] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:24,491 - src.knowledge_base.knowledge_manager - INFO - 知识库条目取消验证成功: 61
2025-09-10 22:36:24,492 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:24] "POST /api/knowledge/unverify/61 HTTP/1.1" 200 -
2025-09-10 22:36:24,721 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:24] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:25,932 - src.knowledge_base.knowledge_manager - INFO - 知识库条目验证成功: 61
2025-09-10 22:36:25,933 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:25] "POST /api/knowledge/verify/61 HTTP/1.1" 200 -
2025-09-10 22:36:26,245 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:26] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:27,241 - src.knowledge_base.knowledge_manager - INFO - 知识库条目验证成功: 51
2025-09-10 22:36:27,242 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:27] "POST /api/knowledge/verify/51 HTTP/1.1" 200 -
2025-09-10 22:36:27,565 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:27] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:28,405 - src.knowledge_base.knowledge_manager - INFO - 知识库条目验证成功: 52
2025-09-10 22:36:28,406 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:28] "POST /api/knowledge/verify/52 HTTP/1.1" 200 -
2025-09-10 22:36:28,726 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:28] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:29,000 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:29,824 - src.knowledge_base.knowledge_manager - INFO - 知识库条目验证成功: 53
2025-09-10 22:36:29,825 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:29] "POST /api/knowledge/verify/53 HTTP/1.1" 200 -
2025-09-10 22:36:30,137 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:30] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:36:31,290 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:31] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:36:33,343 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:33] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:36:33,886 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:39,194 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:42,193 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:42] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:36:44,196 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:47,972 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:47] "GET /api/analytics HTTP/1.1" 200 -
2025-09-10 22:36:49,206 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:50,882 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:50] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:36:53,493 - src.analytics.monitor_service - INFO - 监控服务已启动
2025-09-10 22:36:53,494 - src.main - INFO - 监控服务已启动
2025-09-10 22:36:53,495 - src.agent.auto_monitor - INFO - 自动监控循环已启动
2025-09-10 22:36:53,495 - src.agent.auto_monitor - INFO - 自动监控服务已启动
2025-09-10 22:36:53,496 - src.agent.auto_monitor - INFO - 执行第 1 次自动监控检查
2025-09-10 22:36:53,496 - src.agent_assistant - INFO - 主动监控已启动
2025-09-10 22:36:53,497 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:53] "POST /api/agent/monitoring/start HTTP/1.1" 200 -
2025-09-10 22:36:53,535 - src.agent.auto_monitor - INFO - 触发主动行动: alert_overflow - 活跃预警数量过多: 17
2025-09-10 22:36:53,536 - src.agent.auto_monitor - INFO - 主动行动记录: {"timestamp": "2025-09-10T22:36:53.536449", "action_type": "alert_overflow", "priority": "high", "description": "活跃预警数量过多: 17", "action": "建议立即处理预警", "data": {"alert_count": 17}}
2025-09-10 22:36:53,537 - src.agent.auto_monitor - INFO - 处理预警溢出
2025-09-10 22:36:53,537 - src.agent.auto_monitor - ERROR - 检查工单积压失败: 'TSPAgentAssistant' object has no attribute 'get_workorders'
2025-09-10 22:36:53,552 - src.analytics.monitor_service - INFO - 触发 3 个预警
2025-09-10 22:36:53,552 - src.analytics.monitor_service - WARNING - 预警触发: 用户满意度较低: 0.00 (阈值: 0.6)
2025-09-10 22:36:53,552 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '用户满意度较低: 0.00 (阈值: 0.6)', 'timestamp': '2025-09-10T22:36:53.511361', 'rule_name': '满意度预警'}
2025-09-10 22:36:53,552 - src.analytics.monitor_service - WARNING - 警告预警: 用户满意度较低: 0.00 (阈值: 0.6)
2025-09-10 22:36:53,553 - src.analytics.monitor_service - WARNING - 预警触发: 知识库命中率较低: 0.00 (阈值: 0.5)
2025-09-10 22:36:53,553 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '知识库命中率较低: 0.00 (阈值: 0.5)', 'timestamp': '2025-09-10T22:36:53.530414', 'rule_name': '知识库命中率预警'}
2025-09-10 22:36:53,553 - src.analytics.monitor_service - WARNING - 警告预警: 知识库命中率较低: 0.00 (阈值: 0.5)
2025-09-10 22:36:53,553 - src.analytics.monitor_service - WARNING - 预警触发: 系统内存使用率过高: 84.1% (阈值: 80.0%)
2025-09-10 22:36:53,553 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '系统内存使用率过高: 84.1% (阈值: 80.0%)', 'timestamp': '2025-09-10T22:36:53.547594', 'rule_name': '内存使用预警'}
2025-09-10 22:36:53,553 - src.analytics.monitor_service - WARNING - 警告预警: 系统内存使用率过高: 84.1% (阈值: 80.0%)
2025-09-10 22:36:53,883 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:53] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:36:54,183 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:54] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:36:56,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:56] "POST /api/agent/intelligent-analysis HTTP/1.1" 200 -
2025-09-10 22:36:56,674 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:56] "POST /api/agent/intelligent-analysis HTTP/1.1" 200 -
2025-09-10 22:36:57,922 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:57] "GET /api/settings HTTP/1.1" 200 -
2025-09-10 22:36:59,189 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:36:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:01,829 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:01] "GET /api/analytics HTTP/1.1" 200 -
2025-09-10 22:37:04,190 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:06,201 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:06] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:37:09,191 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:13,890 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:19,202 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:24,094 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:29,212 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:33,893 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:39,396 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:44,082 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:49,393 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:54,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:54] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:37:59,400 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:37:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:00,132 - websockets.server - INFO - connection rejected (426 Upgrade Required)
2025-09-10 22:38:00,134 - websockets.server - INFO - connection closed
2025-09-10 22:38:00,275 - websockets.server - INFO - connection rejected (426 Upgrade Required)
2025-09-10 22:38:00,278 - websockets.server - INFO - connection closed
2025-09-10 22:38:04,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:09,402 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:14,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:19,407 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:24,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:29,408 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:34,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:39,208 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:44,132 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:49,414 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:54,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:54] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:38:59,406 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:38:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:04,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:09,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:14,092 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:19,399 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:24,094 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:29,416 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:34,095 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:39:39,412 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:39:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:00,101 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:15,161 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:15] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:16,829 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:16] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:40:18,698 - src.dialogue.realtime_chat - INFO - 创建新会话: session_user_001_1757515218
2025-09-10 22:40:18,701 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:18] "POST /api/chat/session HTTP/1.1" 200 -
2025-09-10 22:40:18,978 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:18] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:23,139 - src.core.llm_client - INFO - API请求成功
2025-09-10 22:40:23,153 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:23] "POST /api/chat/message HTTP/1.1" 200 -
2025-09-10 22:40:24,227 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:27,306 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:27] "GET /chat HTTP/1.1" 200 -
2025-09-10 22:40:27,662 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:27] "GET /static/js/chat.js HTTP/1.1" 304 -
2025-09-10 22:40:28,607 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:28] "GET /chat HTTP/1.1" 200 -
2025-09-10 22:40:28,707 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:28] "GET /static/js/chat.js HTTP/1.1" 304 -
2025-09-10 22:40:29,107 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:29,939 - websockets.server - INFO - connection open
2025-09-10 22:40:29,940 - src.web.websocket_server - INFO - 客户端连接: ('::1', 13772, 0, 0)
2025-09-10 22:40:29,950 - src.dialogue.realtime_chat - INFO - 创建新会话: session_user_001_1757515229
2025-09-10 22:40:34,452 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:36,817 - src.core.llm_client - INFO - API请求成功
2025-09-10 22:40:39,107 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:44,499 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:49,125 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:53,654 - src.analytics.monitor_service - INFO - 触发 1 个预警
2025-09-10 22:40:53,654 - src.analytics.monitor_service - WARNING - 预警触发: 用户对话中断率过高: 0.50 (阈值: 0.3)
2025-09-10 22:40:53,655 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '用户对话中断率过高: 0.50 (阈值: 0.3)', 'timestamp': '2025-09-10T22:40:53.647669', 'rule_name': '对话中断预警'}
2025-09-10 22:40:53,655 - src.analytics.monitor_service - WARNING - 警告预警: 用户对话中断率过高: 0.50 (阈值: 0.3)
2025-09-10 22:40:54,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:54] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:40:59,100 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:40:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:04,419 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:09,114 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:14,426 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:19,097 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:24,420 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:24,657 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:24] "GET / HTTP/1.1" 200 -
2025-09-10 22:41:24,820 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:24] "GET /static/js/dashboard.js HTTP/1.1" 304 -
2025-09-10 22:41:25,212 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:25,489 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:41:25,496 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:41:25,514 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/system/info HTTP/1.1" 200 -
2025-09-10 22:41:25,525 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:41:25,544 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:25] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:41:27,055 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:27] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:41:27,845 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:27] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:41:30,494 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:30] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:30,803 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:30] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:41:33,174 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:33] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:41:35,180 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:35] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:36,624 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:36] "GET /api/workorders?status=open HTTP/1.1" 200 -
2025-09-10 22:41:40,186 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:40] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:40,467 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:40] "GET /api/workorders?status=open HTTP/1.1" 200 -
2025-09-10 22:41:46,429 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:46] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:51,114 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:51] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:41:53,547 - src.agent.auto_monitor - INFO - 执行第 2 次自动监控检查
2025-09-10 22:41:53,562 - src.agent.auto_monitor - INFO - 触发主动行动: alert_overflow - 活跃预警数量过多: 23
2025-09-10 22:41:53,563 - src.agent.auto_monitor - INFO - 主动行动记录: {"timestamp": "2025-09-10T22:41:53.563228", "action_type": "alert_overflow", "priority": "high", "description": "活跃预警数量过多: 23", "action": "建议立即处理预警", "data": {"alert_count": 23}}
2025-09-10 22:41:53,563 - src.agent.auto_monitor - INFO - 处理预警溢出
2025-09-10 22:41:53,563 - src.agent.auto_monitor - ERROR - 检查工单积压失败: 'TSPAgentAssistant' object has no attribute 'get_workorders'
2025-09-10 22:41:56,425 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:41:56] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:00,154 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:01,436 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:01] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:03,671 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:03] "GET /alerts HTTP/1.1" 200 -
2025-09-10 22:42:04,020 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /static/css/style.css HTTP/1.1" 200 -
2025-09-10 22:42:04,268 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /static/js/app.js HTTP/1.1" 200 -
2025-09-10 22:42:04,686 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:04,694 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /api/rules HTTP/1.1" 200 -
2025-09-10 22:42:04,708 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:04,778 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:04] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:05,290 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:05] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:09,677 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:09,698 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:09] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:11,120 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:11] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:14,696 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:14] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:14,702 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:14] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:14,704 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:16,132 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:16] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:18,012 - src.analytics.monitor_service - INFO - 监控服务已启动
2025-09-10 22:42:18,014 - src.main - INFO - 监控服务已启动
2025-09-10 22:42:18,015 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:18] "POST /api/monitor/start HTTP/1.1" 200 -
2025-09-10 22:42:18,120 - src.analytics.monitor_service - INFO - 触发 4 个预警
2025-09-10 22:42:18,120 - src.analytics.monitor_service - WARNING - 预警触发: 用户满意度较低: 0.00 (阈值: 0.6)
2025-09-10 22:42:18,120 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '用户满意度较低: 0.00 (阈值: 0.6)', 'timestamp': '2025-09-10T22:42:18.022114', 'rule_name': '满意度预警'}
2025-09-10 22:42:18,120 - src.analytics.monitor_service - WARNING - 警告预警: 用户满意度较低: 0.00 (阈值: 0.6)
2025-09-10 22:42:18,121 - src.analytics.monitor_service - WARNING - 预警触发: 知识库命中率较低: 0.00 (阈值: 0.5)
2025-09-10 22:42:18,122 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '知识库命中率较低: 0.00 (阈值: 0.5)', 'timestamp': '2025-09-10T22:42:18.067562', 'rule_name': '知识库命中率预警'}
2025-09-10 22:42:18,123 - src.analytics.monitor_service - WARNING - 警告预警: 知识库命中率较低: 0.00 (阈值: 0.5)
2025-09-10 22:42:18,124 - src.analytics.monitor_service - WARNING - 预警触发: 系统内存使用率过高: 84.8% (阈值: 80.0%)
2025-09-10 22:42:18,126 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '系统内存使用率过高: 84.8% (阈值: 80.0%)', 'timestamp': '2025-09-10T22:42:18.101003', 'rule_name': '内存使用预警'}
2025-09-10 22:42:18,126 - src.analytics.monitor_service - WARNING - 警告预警: 系统内存使用率过高: 84.8% (阈值: 80.0%)
2025-09-10 22:42:18,126 - src.analytics.monitor_service - WARNING - 预警触发: 用户对话中断率过高: 0.50 (阈值: 0.3)
2025-09-10 22:42:18,127 - src.analytics.monitor_service - INFO - 发送通知: {'level': '警告', 'message': '用户对话中断率过高: 0.50 (阈值: 0.3)', 'timestamp': '2025-09-10T22:42:18.113652', 'rule_name': '对话中断预警'}
2025-09-10 22:42:18,128 - src.analytics.monitor_service - WARNING - 警告预警: 用户对话中断率过高: 0.50 (阈值: 0.3)
2025-09-10 22:42:18,301 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:18] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:19,365 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:19,707 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:19] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:21,434 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:21] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:24,428 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:24,666 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:24] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:24,742 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:24] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:26,444 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:26] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:29,367 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:29,756 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:29] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:30,765 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:30] "POST /api/alerts/31/resolve HTTP/1.1" 200 -
2025-09-10 22:42:31,001 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:31] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:31,128 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:31] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:34,349 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:34] "POST /api/alerts/33/resolve HTTP/1.1" 200 -
2025-09-10 22:42:34,620 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:34,655 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:34] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:34,680 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:34] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:34,698 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:34] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:35,190 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:35] "POST /api/alerts/34/resolve HTTP/1.1" 200 -
2025-09-10 22:42:35,444 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:35] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:36,175 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:36] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:38,558 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:38] "POST /api/alerts/32/resolve HTTP/1.1" 200 -
2025-09-10 22:42:38,807 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:38] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:39,363 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:39,782 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:39] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:41,434 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:41] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:44,345 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:44,694 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:44] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:44,728 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:44] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:50,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:50] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:50,444 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:50] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:42:55,110 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:55] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:42:55,424 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:55] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:42:55,445 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:42:55] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:00,459 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:00,465 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:00,704 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:00,816 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:05,135 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:05] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:05,468 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:05] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:05,781 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:05] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:05,831 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:05] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:06,519 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:06] "GET /api/workorders?status=open HTTP/1.1" 200 -
2025-09-10 22:43:08,243 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:08] "GET / HTTP/1.1" 200 -
2025-09-10 22:43:08,556 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:08] "GET /static/js/dashboard.js HTTP/1.1" 200 -
2025-09-10 22:43:09,054 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:43:09,074 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:43:09,082 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:09,107 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/system/info HTTP/1.1" 200 -
2025-09-10 22:43:09,177 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:43:09,183 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:09,395 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:09] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:43:10,471 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:10] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:10,486 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:10] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:13,766 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:14,401 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:14] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:43:15,159 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:15] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:15,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:15] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:15,444 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:15] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:19,414 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:19,860 - src.dialogue.realtime_chat - INFO - 结束会话: session_user_001_1757515229
2025-09-10 22:43:20,114 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:20] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:20,429 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:20] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:24,414 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:25,116 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:25] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:25,429 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:25] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:25,509 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:25] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:28,069 - src.web.websocket_server - INFO - 客户端断开: ('::1', 13772, 0, 0)
2025-09-10 22:43:28,069 - websockets.server - INFO - connection closed
2025-09-10 22:43:29,200 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:29,839 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:29] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:43:30,439 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:30] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:30,447 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:30] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:33,774 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:35,439 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:35] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:35,464 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:35] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:35,493 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:35] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:37,106 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:37] "POST /api/agent/intelligent-analysis HTTP/1.1" 200 -
2025-09-10 22:43:39,048 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:39] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:43:39,055 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:39] "POST /api/agent/proactive-monitoring HTTP/1.1" 200 -
2025-09-10 22:43:39,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:40,119 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:40] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:40,439 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:40] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:42,340 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:42] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:43:43,756 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:43] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:45,311 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:45] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:43:45,445 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:45] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:45,478 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:45] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:45,498 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:45] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:46,459 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:46] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:43:48,756 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:48] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:43:49,048 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:50,111 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:50] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:50,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:50] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:54,487 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:54] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:55,128 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:55] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:43:55,419 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:55] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:43:55,453 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:55] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:43:59,408 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:43:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:00,109 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:00,420 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:00,457 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:04,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:05,113 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:05] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:05,403 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:05] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:44:05,422 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:05] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:09,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:10,114 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:10] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:10,421 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:10] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:14,411 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:15,109 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:15] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:15,416 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:15] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:44:15,418 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:15] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:19,418 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:20,101 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:20] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:20,428 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:20] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:24,406 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:25,113 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:25] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:25,405 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:25] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:44:25,422 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:25] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:44:29,416 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:34,091 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:39,421 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:44,099 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:44:49,422 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:44:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:45:00,123 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:45:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:45:00,419 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:45:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:45:00,437 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:45:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:45:00,474 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:45:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:45:00,823 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:45:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:00,401 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:46:00,412 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:46:00,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:00,671 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:00,734 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:20,637 - __main__ - INFO - 正在启动TSP智能助手综合管理平台...
2025-09-10 22:46:21,476 - src.core.database - INFO - 数据库初始化成功
2025-09-10 22:46:22,871 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:22,880 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:22,881 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:46:22,891 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:22,900 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:22,902 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:46:22,902 - src.agent.tool_manager - INFO - 注册工具: search_knowledge
2025-09-10 22:46:22,902 - src.agent.tool_manager - INFO - 注册工具: create_work_order
2025-09-10 22:46:22,903 - src.agent.tool_manager - INFO - 注册工具: update_work_order
2025-09-10 22:46:22,903 - src.agent.tool_manager - INFO - 注册工具: generate_response
2025-09-10 22:46:22,903 - src.agent.tool_manager - INFO - 注册工具: analyze_data
2025-09-10 22:46:22,903 - src.agent.tool_manager - INFO - 注册工具: send_notification
2025-09-10 22:46:22,904 - src.agent.tool_manager - INFO - 注册工具: schedule_task
2025-09-10 22:46:22,904 - src.agent.tool_manager - INFO - 注册工具: web_search
2025-09-10 22:46:22,904 - src.agent.tool_manager - INFO - 注册工具: file_operation
2025-09-10 22:46:22,905 - src.agent.tool_manager - INFO - 注册工具: database_query
2025-09-10 22:46:22,905 - src.agent.tool_manager - INFO - 已注册 10 个默认工具
2025-09-10 22:46:22,905 - src.agent.agent_core - INFO - Agent核心初始化完成
2025-09-10 22:46:22,905 - src.agent_assistant - INFO - TSP Agent助手初始化完成
2025-09-10 22:46:22,916 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:22,978 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.26.238:5000
2025-09-10 22:46:22,978 - werkzeug - INFO - Press CTRL+C to quit
2025-09-10 22:46:23,004 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:46:23,006 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765
2025-09-10 22:46:23,011 - websockets.server - INFO - server listening on 127.0.0.1:8765
2025-09-10 22:46:23,011 - websockets.server - INFO - server listening on [::1]:8765
2025-09-10 22:46:28,171 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:28] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:28,747 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:28] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:34,394 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:34] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:35,437 - websockets.server - INFO - connection rejected (200 OK)
2025-09-10 22:46:35,438 - websockets.server - INFO - connection closed
2025-09-10 22:46:35,608 - websockets.server - INFO - connection rejected (200 OK)
2025-09-10 22:46:35,624 - websockets.server - INFO - connection closed
2025-09-10 22:46:39,083 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:39] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:44,408 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:44] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:46,320 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:46] "GET / HTTP/1.1" 200 -
2025-09-10 22:46:46,613 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:46] "GET /static/js/dashboard.js HTTP/1.1" 200 -
2025-09-10 22:46:47,410 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:46:47,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:46:47,420 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/system/info HTTP/1.1" 200 -
2025-09-10 22:46:47,456 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:46:47,466 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:47,477 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:46:47,539 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:47] "GET /favicon.ico HTTP/1.1" 404 -
2025-09-10 22:46:49,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:49] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:52,419 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:52] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:54,406 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:54] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:55,108 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:55] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:46:56,537 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:56] "GET /api/workorders/1 HTTP/1.1" 200 -
2025-09-10 22:46:57,437 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:57] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:46:59,087 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:46:59] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:00,403 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:47:00,432 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:00,437 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:47:00,655 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:02,410 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:02] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:04,092 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:04] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:06,352 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:06] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:47:07,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:07] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:09,004 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:09] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:47:09,389 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:09] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:12,411 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:12] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:13,324 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:13] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:47:14,093 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:14] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:16,978 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:16] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:47:17,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:17] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:17,776 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:17] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:47:18,450 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:18] "GET /api/knowledge?page=1&per_page=10 HTTP/1.1" 200 -
2025-09-10 22:47:19,406 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:19] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:21,975 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:21] "GET /api/agent/status HTTP/1.1" 200 -
2025-09-10 22:47:22,412 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:22] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:24,097 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:24,696 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:24] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:47:24,702 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:24] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:47:24,709 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:24] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:47:24,723 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:24] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:47:25,262 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:25] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:47:25,706 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:25] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:47:26,507 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:26] "GET /api/analytics HTTP/1.1" 200 -
2025-09-10 22:47:27,043 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:27] "GET /api/settings HTTP/1.1" 200 -
2025-09-10 22:47:27,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:27] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:29,439 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:29] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:33,085 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:38,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:38] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:43,094 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:43] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:48,086 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:48] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:53,083 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:53] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:47:58,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:47:58] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:00,097 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:00,388 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:48:00,399 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:48:00,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:00,736 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:03,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:03] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:08,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:08] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:13,093 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:18,095 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:18] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:23,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:23] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:48:28,106 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:48:28] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:49:00,160 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:49:00,452 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:49:00,454 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:49:00,477 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:49:00,692 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:49:00,798 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:49:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:50:00,136 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:50:00,143 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:50:00,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:50:00,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:50:00,477 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:50:00,834 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:50:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:51:00,112 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:51:00,432 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:51:00,455 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:51:00,458 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:51:00,675 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:51:00,800 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:51:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:00,123 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:00,125 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:00,410 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:52:00,426 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:52:00,453 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:00,784 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:24,642 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:24] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:25,904 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:25] "GET /api/analytics HTTP/1.1" 200 -
2025-09-10 22:52:27,456 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:27] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:33,108 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:38,104 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:38] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:43,103 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:43] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:48,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:48] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:53,106 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:53] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:52:58,092 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:52:58] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:00,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:53:00,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:00,445 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:53:00,684 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:00,772 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:03,104 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:03] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:08,108 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:08] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:13,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:18,104 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:18] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:23,095 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:23] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:53:28,116 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:53:28] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:54:00,125 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:54:00,146 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:54:00,435 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:54:00,474 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:54:00,488 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:54:00,829 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:54:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:00,142 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:00,443 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:00,455 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:55:00,474 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:55:00,719 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:00,782 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:48,827 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:48] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:53,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:53] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:55:58,093 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:55:58] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:00,098 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:00,400 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:56:00,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:56:00,424 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:00,753 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:03,091 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:03] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:08,102 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:08] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:13,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:13] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:18,095 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:18] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:23,098 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:23] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:28,099 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:28] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:33,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:33] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:38,090 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:38] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:43,089 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:43] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:56:48,095 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:56:48] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:57:00,107 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:57:00,425 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:57:00,437 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:57:00,463 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:57:00,684 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:57:00,770 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:57:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:03,303 - __main__ - INFO - 正在启动TSP智能助手综合管理平台...
2025-09-10 22:58:05,745 - src.core.database - INFO - 数据库初始化成功
2025-09-10 22:58:07,461 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,470 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,471 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:58:07,480 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,492 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,494 - src.main - INFO - TSP助手初始化完成
2025-09-10 22:58:07,494 - src.agent.tool_manager - INFO - 注册工具: search_knowledge
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: create_work_order
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: update_work_order
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: generate_response
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: analyze_data
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: send_notification
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: schedule_task
2025-09-10 22:58:07,495 - src.agent.tool_manager - INFO - 注册工具: web_search
2025-09-10 22:58:07,496 - src.agent.tool_manager - INFO - 注册工具: file_operation
2025-09-10 22:58:07,496 - src.agent.tool_manager - INFO - 注册工具: database_query
2025-09-10 22:58:07,496 - src.agent.tool_manager - INFO - 已注册 10 个默认工具
2025-09-10 22:58:07,496 - src.agent.agent_core - INFO - Agent核心初始化完成
2025-09-10 22:58:07,496 - src.agent_assistant - INFO - TSP Agent助手初始化完成
2025-09-10 22:58:07,507 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,570 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.26.238:5000
2025-09-10 22:58:07,571 - werkzeug - INFO - Press CTRL+C to quit
2025-09-10 22:58:07,597 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 48 个条目
2025-09-10 22:58:07,599 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765
2025-09-10 22:58:07,603 - websockets.server - INFO - server listening on 127.0.0.1:8765
2025-09-10 22:58:07,604 - websockets.server - INFO - server listening on [::1]:8765
2025-09-10 22:58:20,916 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:20] "GET / HTTP/1.1" 200 -
2025-09-10 22:58:21,166 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /static/js/dashboard.js HTTP/1.1" 200 -
2025-09-10 22:58:21,410 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/chat/sessions HTTP/1.1" 200 -
2025-09-10 22:58:21,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:58:21,439 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/system/info HTTP/1.1" 200 -
2025-09-10 22:58:21,463 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:58:21,495 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/knowledge/stats HTTP/1.1" 200 -
2025-09-10 22:58:21,520 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:21,608 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:21] "GET /api/analytics HTTP/1.1" 200 -
2025-09-10 22:58:22,927 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:22] "GET /api/workorders HTTP/1.1" 200 -
2025-09-10 22:58:26,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:26] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:29,362 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:29] "GET /api/workorders/import/template HTTP/1.1" 200 -
2025-09-10 22:58:29,371 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:29] "GET /uploads/workorder_template.xlsx HTTP/1.1" 404 -
2025-09-10 22:58:31,416 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:31] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:32,421 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:32] "GET /uploads/workorder_template.xlsx HTTP/1.1" 404 -
2025-09-10 22:58:36,414 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:36] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:41,419 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:41] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:46,421 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:46] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:51,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:51] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:58:55,684 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:55] "POST /api/workorders/import HTTP/1.1" 400 -
2025-09-10 22:58:56,415 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:58:56] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:00,120 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:00,121 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:00,402 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 22:59:00,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 22:59:00,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:00,772 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:01,512 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:01] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:07,096 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:07] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:12,110 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:12] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:17,088 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:17] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:22,098 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:22] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:27,119 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:27] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:32,117 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:32] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:37,121 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:37] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:42,099 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:42] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:47,104 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:47] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:52,107 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:52] "GET /api/health HTTP/1.1" 200 -
2025-09-10 22:59:57,109 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 22:59:57] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:00:00,130 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:00:00,429 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:00:00,458 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:00:00,462 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:00:00,690 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:00:00,797 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:00:02,113 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:00:02] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:01:00,135 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:01:00,183 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:01:00,469 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:01:00,471 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:01:00,480 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:01:00,708 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:01:00,819 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:01:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:02:00,134 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:02:00,142 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:02:00,184 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:02:00,413 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:02:00,440 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:02:00,475 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:02:00,809 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:02:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:03:00,120 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:03:00,161 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:03:00,427 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:03:00,467 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:03:00,473 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:03:00,683 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:03:00,811 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:03:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:04:00,125 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:04:00,144 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:04:00,189 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:04:00,428 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:04:00,446 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:04:00,469 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:04:00,800 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:04:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:05:00,134 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:05:00,177 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:05:00,429 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:05:00,462 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:05:00,472 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:05:00,701 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:05:00,806 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:05:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:06:00,145 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:06:00,157 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:06:00,201 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:06:00,417 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:06:00,442 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:06:00,497 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:06:00,828 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:06:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:07:00,131 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:07:00,176 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:07:00,426 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:07:00,452 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:07:00,458 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:07:00,681 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:07:00,793 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:07:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:08:00,141 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:08:00,143 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:08:00,196 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:08:00,436 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:08:00,455 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:08:00,496 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:08:00,849 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:08:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:09:00,123 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:09:00,160 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:09:00,425 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/alerts HTTP/1.1" 200 -
2025-09-10 23:09:00,446 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:09:00,448 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/monitor/status HTTP/1.1" 200 -
2025-09-10 23:09:00,700 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/health HTTP/1.1" 200 -
2025-09-10 23:09:00,781 - werkzeug - INFO - 127.0.0.1 - - [10/Sep/2025 23:09:00] "GET /api/health HTTP/1.1" 200 -

View File

@@ -19,6 +19,7 @@ psutil>=5.9.0
# 数据处理 # 数据处理
pandas>=2.0.0 pandas>=2.0.0
openpyxl>=3.1.0
# 向量化 # 向量化
sentence-transformers>=2.2.0 sentence-transformers>=2.2.0

Binary file not shown.

358
src/agent/auto_monitor.py Normal file
View File

@@ -0,0 +1,358 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自动监控服务
实现Agent的主动调用功能
"""
import asyncio
import logging
import threading
import time
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional
import json
logger = logging.getLogger(__name__)
class AutoMonitorService:
"""自动监控服务"""
def __init__(self, agent_assistant):
self.agent_assistant = agent_assistant
self.is_running = False
self.monitor_thread = None
self.check_interval = 300 # 5分钟检查一次
self.last_check_time = None
self.monitoring_stats = {
"total_checks": 0,
"proactive_actions": 0,
"last_action_time": None,
"error_count": 0
}
def start_auto_monitoring(self) -> bool:
"""启动自动监控"""
try:
if self.is_running:
logger.warning("自动监控已在运行中")
return True
self.is_running = True
self.monitor_thread = threading.Thread(target=self._monitoring_loop, daemon=True)
self.monitor_thread.start()
logger.info("自动监控服务已启动")
return True
except Exception as e:
logger.error(f"启动自动监控失败: {e}")
return False
def stop_auto_monitoring(self) -> bool:
"""停止自动监控"""
try:
self.is_running = False
if self.monitor_thread and self.monitor_thread.is_alive():
self.monitor_thread.join(timeout=5)
logger.info("自动监控服务已停止")
return True
except Exception as e:
logger.error(f"停止自动监控失败: {e}")
return False
def _monitoring_loop(self):
"""监控循环"""
logger.info("自动监控循环已启动")
while self.is_running:
try:
# 执行监控检查
self._perform_monitoring_check()
# 等待下次检查
time.sleep(self.check_interval)
except Exception as e:
logger.error(f"监控循环错误: {e}")
self.monitoring_stats["error_count"] += 1
time.sleep(60) # 出错后等待1分钟
def _perform_monitoring_check(self):
"""执行监控检查"""
try:
self.monitoring_stats["total_checks"] += 1
self.last_check_time = datetime.now()
logger.info(f"执行第 {self.monitoring_stats['total_checks']} 次自动监控检查")
# 1. 检查系统健康状态
self._check_system_health()
# 2. 检查预警状态
self._check_alert_status()
# 3. 检查工单积压
self._check_workorder_backlog()
# 4. 检查知识库质量
self._check_knowledge_quality()
# 5. 检查用户满意度
self._check_user_satisfaction()
# 6. 检查系统性能
self._check_system_performance()
except Exception as e:
logger.error(f"执行监控检查失败: {e}")
self.monitoring_stats["error_count"] += 1
def _check_system_health(self):
"""检查系统健康状态"""
try:
health = self.agent_assistant.get_system_health()
health_score = health.get("health_score", 1.0)
if health_score < 0.7:
self._trigger_proactive_action({
"type": "system_health_warning",
"priority": "high",
"description": f"系统健康分数较低: {health_score:.2f}",
"action": "建议立即检查系统状态",
"data": health
})
except Exception as e:
logger.error(f"检查系统健康状态失败: {e}")
def _check_alert_status(self):
"""检查预警状态"""
try:
alerts = self.agent_assistant.get_active_alerts()
alert_count = len(alerts) if isinstance(alerts, list) else 0
if alert_count > 5:
self._trigger_proactive_action({
"type": "alert_overflow",
"priority": "high",
"description": f"活跃预警数量过多: {alert_count}",
"action": "建议立即处理预警",
"data": {"alert_count": alert_count}
})
except Exception as e:
logger.error(f"检查预警状态失败: {e}")
def _check_workorder_backlog(self):
"""检查工单积压"""
try:
# 获取工单统计
workorders = self.agent_assistant.get_workorders()
if isinstance(workorders, list):
open_count = len([w for w in workorders if w.get("status") == "open"])
in_progress_count = len([w for w in workorders if w.get("status") == "in_progress"])
total_pending = open_count + in_progress_count
if total_pending > 10:
self._trigger_proactive_action({
"type": "workorder_backlog",
"priority": "medium",
"description": f"待处理工单过多: {total_pending}",
"action": "建议增加处理人员或优化流程",
"data": {
"open_count": open_count,
"in_progress_count": in_progress_count,
"total_pending": total_pending
}
})
except Exception as e:
logger.error(f"检查工单积压失败: {e}")
def _check_knowledge_quality(self):
"""检查知识库质量"""
try:
stats = self.agent_assistant.knowledge_manager.get_knowledge_stats()
avg_confidence = stats.get("average_confidence", 0.8)
total_entries = stats.get("total_entries", 0)
if avg_confidence < 0.6:
self._trigger_proactive_action({
"type": "knowledge_quality_low",
"priority": "medium",
"description": f"知识库平均置信度较低: {avg_confidence:.2f}",
"action": "建议更新和优化知识库内容",
"data": {"avg_confidence": avg_confidence, "total_entries": total_entries}
})
except Exception as e:
logger.error(f"检查知识库质量失败: {e}")
def _check_user_satisfaction(self):
"""检查用户满意度"""
try:
# 模拟检查用户满意度
# 这里可以从数据库或API获取真实的满意度数据
satisfaction_score = 0.75 # 模拟数据
if satisfaction_score < 0.7:
self._trigger_proactive_action({
"type": "low_satisfaction",
"priority": "high",
"description": f"用户满意度较低: {satisfaction_score:.2f}",
"action": "建议分析低满意度原因并改进服务",
"data": {"satisfaction_score": satisfaction_score}
})
except Exception as e:
logger.error(f"检查用户满意度失败: {e}")
def _check_system_performance(self):
"""检查系统性能"""
try:
# 模拟检查系统性能指标
response_time = 1.2 # 模拟响应时间(秒)
error_rate = 0.02 # 模拟错误率
if response_time > 2.0:
self._trigger_proactive_action({
"type": "slow_response",
"priority": "medium",
"description": f"系统响应时间过慢: {response_time:.2f}",
"action": "建议优化系统性能",
"data": {"response_time": response_time}
})
if error_rate > 0.05:
self._trigger_proactive_action({
"type": "high_error_rate",
"priority": "high",
"description": f"系统错误率过高: {error_rate:.2%}",
"action": "建议立即检查系统错误",
"data": {"error_rate": error_rate}
})
except Exception as e:
logger.error(f"检查系统性能失败: {e}")
def _trigger_proactive_action(self, action_data: Dict[str, Any]):
"""触发主动行动"""
try:
self.monitoring_stats["proactive_actions"] += 1
self.monitoring_stats["last_action_time"] = datetime.now()
logger.info(f"触发主动行动: {action_data['type']} - {action_data['description']}")
# 记录主动行动
self._log_proactive_action(action_data)
# 根据行动类型执行相应操作
self._execute_proactive_action(action_data)
except Exception as e:
logger.error(f"触发主动行动失败: {e}")
def _log_proactive_action(self, action_data: Dict[str, Any]):
"""记录主动行动"""
try:
log_entry = {
"timestamp": datetime.now().isoformat(),
"action_type": action_data["type"],
"priority": action_data["priority"],
"description": action_data["description"],
"action": action_data["action"],
"data": action_data.get("data", {})
}
# 这里可以将日志保存到数据库或文件
logger.info(f"主动行动记录: {json.dumps(log_entry, ensure_ascii=False)}")
except Exception as e:
logger.error(f"记录主动行动失败: {e}")
def _execute_proactive_action(self, action_data: Dict[str, Any]):
"""执行主动行动"""
try:
action_type = action_data["type"]
if action_type == "system_health_warning":
self._handle_system_health_warning(action_data)
elif action_type == "alert_overflow":
self._handle_alert_overflow(action_data)
elif action_type == "workorder_backlog":
self._handle_workorder_backlog(action_data)
elif action_type == "knowledge_quality_low":
self._handle_knowledge_quality_low(action_data)
elif action_type == "low_satisfaction":
self._handle_low_satisfaction(action_data)
elif action_type == "slow_response":
self._handle_slow_response(action_data)
elif action_type == "high_error_rate":
self._handle_high_error_rate(action_data)
else:
logger.warning(f"未知的主动行动类型: {action_type}")
except Exception as e:
logger.error(f"执行主动行动失败: {e}")
def _handle_system_health_warning(self, action_data: Dict[str, Any]):
"""处理系统健康警告"""
logger.info("处理系统健康警告")
# 这里可以实现具体的处理逻辑,如发送通知、重启服务等
def _handle_alert_overflow(self, action_data: Dict[str, Any]):
"""处理预警溢出"""
logger.info("处理预警溢出")
# 这里可以实现具体的处理逻辑,如自动处理预警、发送通知等
def _handle_workorder_backlog(self, action_data: Dict[str, Any]):
"""处理工单积压"""
logger.info("处理工单积压")
# 这里可以实现具体的处理逻辑,如自动分配工单、发送提醒等
def _handle_knowledge_quality_low(self, action_data: Dict[str, Any]):
"""处理知识库质量低"""
logger.info("处理知识库质量低")
# 这里可以实现具体的处理逻辑,如自动更新知识库、发送提醒等
def _handle_low_satisfaction(self, action_data: Dict[str, Any]):
"""处理低满意度"""
logger.info("处理低满意度")
# 这里可以实现具体的处理逻辑,如分析原因、发送通知等
def _handle_slow_response(self, action_data: Dict[str, Any]):
"""处理响应慢"""
logger.info("处理响应慢")
# 这里可以实现具体的处理逻辑,如优化配置、发送通知等
def _handle_high_error_rate(self, action_data: Dict[str, Any]):
"""处理高错误率"""
logger.info("处理高错误率")
# 这里可以实现具体的处理逻辑,如检查日志、发送通知等
def get_monitoring_status(self) -> Dict[str, Any]:
"""获取监控状态"""
return {
"is_running": self.is_running,
"check_interval": self.check_interval,
"last_check_time": self.last_check_time.isoformat() if self.last_check_time else None,
"stats": self.monitoring_stats
}
def update_check_interval(self, interval: int) -> bool:
"""更新检查间隔"""
try:
if interval < 60: # 最少1分钟
logger.warning("检查间隔不能少于60秒")
return False
self.check_interval = interval
logger.info(f"检查间隔已更新为 {interval}")
return True
except Exception as e:
logger.error(f"更新检查间隔失败: {e}")
return False

View File

@@ -13,6 +13,7 @@ import json
from src.main import TSPAssistant from src.main import TSPAssistant
from src.agent import AgentCore, AgentState from src.agent import AgentCore, AgentState
from src.agent.auto_monitor import AutoMonitorService
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -26,6 +27,9 @@ class TSPAgentAssistant(TSPAssistant):
# 初始化Agent核心 # 初始化Agent核心
self.agent_core = AgentCore() self.agent_core = AgentCore()
# 初始化自动监控服务
self.auto_monitor = AutoMonitorService(self)
# Agent特有功能 # Agent特有功能
self.is_agent_mode = True self.is_agent_mode = True
self.proactive_tasks = [] self.proactive_tasks = []
@@ -409,10 +413,13 @@ class TSPAgentAssistant(TSPAssistant):
def get_agent_status(self) -> Dict[str, Any]: def get_agent_status(self) -> Dict[str, Any]:
"""获取Agent状态""" """获取Agent状态"""
try: try:
# 获取自动监控状态
monitor_status = self.auto_monitor.get_monitoring_status()
return { return {
"success": True, "success": True,
"agent_mode": self.is_agent_mode, "agent_mode": self.is_agent_mode,
"monitoring_active": getattr(self, '_monitoring_active', False), "monitoring_active": monitor_status["is_running"],
"status": "active" if self.is_agent_mode else "inactive", "status": "active" if self.is_agent_mode else "inactive",
"active_goals": 0, # 简化处理 "active_goals": 0, # 简化处理
"available_tools": 6, # 简化处理 "available_tools": 6, # 简化处理
@@ -424,7 +431,8 @@ class TSPAgentAssistant(TSPAssistant):
{"name": "analyze_data", "usage_count": 0, "success_rate": 0.8}, {"name": "analyze_data", "usage_count": 0, "success_rate": 0.8},
{"name": "send_notification", "usage_count": 0, "success_rate": 0.8} {"name": "send_notification", "usage_count": 0, "success_rate": 0.8}
], ],
"execution_history": [] "execution_history": [],
"auto_monitor": monitor_status
} }
except Exception as e: except Exception as e:
logger.error(f"获取Agent状态失败: {e}") logger.error(f"获取Agent状态失败: {e}")
@@ -456,11 +464,14 @@ class TSPAgentAssistant(TSPAssistant):
# 启动基础监控 # 启动基础监控
self.start_monitoring() self.start_monitoring()
# 启动Agent主动监控同步版本 # 启动自动监控服务
self._start_monitoring_loop() success = self.auto_monitor.start_auto_monitoring()
if success:
logger.info("主动监控已启动") logger.info("主动监控已启动")
return True return True
else:
logger.error("启动自动监控服务失败")
return False
except Exception as e: except Exception as e:
logger.error(f"启动主动监控失败: {e}") logger.error(f"启动主动监控失败: {e}")
return False return False
@@ -471,11 +482,14 @@ class TSPAgentAssistant(TSPAssistant):
# 停止基础监控 # 停止基础监控
self.stop_monitoring() self.stop_monitoring()
# 停止Agent主动监控 # 停止动监控服务
self._stop_monitoring_loop() success = self.auto_monitor.stop_auto_monitoring()
if success:
logger.info("主动监控已停止") logger.info("主动监控已停止")
return True return True
else:
logger.error("停止自动监控服务失败")
return False
except Exception as e: except Exception as e:
logger.error(f"停止主动监控失败: {e}") logger.error(f"停止主动监控失败: {e}")
return False return False
@@ -619,30 +633,38 @@ class TSPAgentAssistant(TSPAssistant):
return "" return ""
def _extract_knowledge_from_content(self, content: str, filename: str) -> List[Dict[str, Any]]: def _extract_knowledge_from_content(self, content: str, filename: str) -> List[Dict[str, Any]]:
"""从内容中提取知识""" """从内容中提取知识,结合工单数据优化"""
try: try:
# 构建提示词 # 获取历史工单数据用于参考
workorder_data = self._get_workorder_insights()
# 构建增强的提示词
prompt = f""" prompt = f"""
请从以下文档内容中提取问答对,用于构建知识库: 请从以下文档内容中提取问答对,用于构建知识库。请结合历史工单数据来优化提取结果
文档名称:{filename} 文档名称:{filename}
文档内容: 文档内容:
{content[:2000]}... {content[:2000]}...
历史工单数据参考:
{workorder_data}
请按照以下格式提取问答对: 请按照以下格式提取问答对:
1. 问题:具体的问题描述 1. 问题:具体的问题描述(参考工单中的常见问题)
2. 答案:详细的答案内容 2. 答案:详细的答案内容(结合工单处理经验)
3. 分类问题所属类别技术问题、APP功能、远程控制、车辆绑定、其他 3. 分类问题所属类别技术问题、APP功能、远程控制、车辆绑定、其他
4. 置信度0-1之间的数值 4. 置信度0-1之间的数值
5. 工单关联:是否与历史工单相关
请提取3-5个最有价值的问答对每个问答对都要完整且实用 请提取3-5个最有价值的问答对优先提取与历史工单问题相关的问答对
返回格式为JSON数组例如 返回格式为JSON数组例如
[ [
{{ {{
"question": "如何远程启动车辆?", "question": "如何远程启动车辆?",
"answer": "远程启动车辆需要满足以下条件1. 车辆处于P档 2. 手刹拉起 3. 车门已锁 4. 电池电量充足", "answer": "远程启动车辆需要满足以下条件1. 车辆处于P档 2. 手刹拉起 3. 车门已锁 4. 电池电量充足。如果仍然无法启动,请检查车辆是否处于可启动状态。",
"category": "远程控制", "category": "远程控制",
"confidence_score": 0.9 "confidence_score": 0.9,
"workorder_related": true
}} }}
] ]
""" """
@@ -700,6 +722,52 @@ class TSPAgentAssistant(TSPAssistant):
logger.error(f"提取知识失败: {e}") logger.error(f"提取知识失败: {e}")
return [] return []
def _get_workorder_insights(self) -> str:
"""获取工单数据洞察"""
try:
# 获取工单数据
workorders = self.get_workorders()
if not isinstance(workorders, list):
return "暂无工单数据"
# 分析工单数据
categories = {}
common_issues = []
resolutions = []
for workorder in workorders[:20]: # 取最近20个工单
category = workorder.get("category", "其他")
categories[category] = categories.get(category, 0) + 1
# 提取常见问题
title = workorder.get("title", "")
description = workorder.get("description", "")
if title and len(title) > 5:
common_issues.append(title)
# 提取解决方案(如果有)
resolution = workorder.get("resolution", "")
if resolution and len(resolution) > 10:
resolutions.append(resolution[:100]) # 截取前100字符
# 构建工单洞察文本
insights = f"""
工单统计:
- 总工单数:{len(workorders)}
- 问题分类分布:{dict(list(categories.items())[:5])}
常见问题:
{chr(10).join(common_issues[:10])}
解决方案示例:
{chr(10).join(resolutions[:5])}
"""
return insights
except Exception as e:
logger.error(f"获取工单洞察失败: {e}")
return "获取工单数据失败"
def _parse_knowledge_manually(self, content: str) -> List[Dict[str, Any]]: def _parse_knowledge_manually(self, content: str) -> List[Dict[str, Any]]:
"""手动解析知识内容""" """手动解析知识内容"""
try: try:

Binary file not shown.

View File

@@ -8,9 +8,13 @@ TSP助手预警管理Web应用
import sys import sys
import os import os
import json import json
import pandas as pd
from datetime import datetime, timedelta from datetime import datetime, timedelta
from flask import Flask, render_template, request, jsonify, redirect, url_for from openpyxl import Workbook
from openpyxl.styles import Font
from flask import Flask, render_template, request, jsonify, redirect, url_for, send_from_directory, send_file
from flask_cors import CORS from flask_cors import CORS
from werkzeug.utils import secure_filename
# 添加项目根目录到Python路径 # 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -24,6 +28,11 @@ from src.vehicle.vehicle_data_manager import VehicleDataManager
app = Flask(__name__) app = Flask(__name__)
CORS(app) CORS(app)
# 配置上传文件夹
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
# 初始化TSP助手和Agent助手 # 初始化TSP助手和Agent助手
assistant = TSPAssistant() assistant = TSPAssistant()
agent_assistant = TSPAgentAssistant() agent_assistant = TSPAgentAssistant()
@@ -541,6 +550,60 @@ def get_workorders():
"priority": "medium", "priority": "medium",
"status": "in_progress", "status": "in_progress",
"created_at": "2024-01-01T11:00:00Z" "created_at": "2024-01-01T11:00:00Z"
},
{
"id": 3,
"title": "蓝牙连接失败",
"description": "用户无法通过蓝牙连接车辆",
"category": "蓝牙功能",
"priority": "high",
"status": "open",
"created_at": "2024-01-01T12:00:00Z"
},
{
"id": 4,
"title": "车辆定位不准确",
"description": "APP中显示的车辆位置与实际位置不符",
"category": "定位功能",
"priority": "medium",
"status": "resolved",
"created_at": "2024-01-01T13:00:00Z"
},
{
"id": 5,
"title": "远程解锁失败",
"description": "用户无法通过APP远程解锁车辆",
"category": "远程控制",
"priority": "urgent",
"status": "open",
"created_at": "2024-01-01T14:00:00Z"
},
{
"id": 6,
"title": "APP闪退问题",
"description": "用户反映APP在使用过程中频繁闪退",
"category": "APP功能",
"priority": "high",
"status": "in_progress",
"created_at": "2024-01-01T15:00:00Z"
},
{
"id": 7,
"title": "车辆状态更新延迟",
"description": "车辆状态信息更新不及时,存在延迟",
"category": "数据同步",
"priority": "low",
"status": "open",
"created_at": "2024-01-01T16:00:00Z"
},
{
"id": 8,
"title": "用户认证失败",
"description": "部分用户无法正常登录APP",
"category": "用户认证",
"priority": "high",
"status": "resolved",
"created_at": "2024-01-01T17:00:00Z"
} }
] ]
@@ -569,16 +632,313 @@ def create_workorder():
except Exception as e: except Exception as e:
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
@app.route('/api/workorders/<int:workorder_id>')
def get_workorder_details(workorder_id):
"""获取工单详情"""
try:
# 这里应该从数据库获取工单详情
# 暂时返回模拟数据
workorder = {
"id": workorder_id,
"order_id": f"WO{workorder_id:06d}",
"title": "车辆无法远程启动",
"description": "用户反映APP中远程启动功能无法使用点击启动按钮后没有任何反应车辆也没有响应。",
"category": "远程控制",
"priority": "high",
"status": "open",
"created_at": "2024-01-01T10:00:00Z",
"updated_at": "2024-01-01T10:00:00Z",
"resolution": None,
"satisfaction_score": None,
"conversations": [
{
"id": 1,
"user_message": "我的车辆无法远程启动",
"assistant_response": "我了解您的问题。让我帮您排查一下远程启动功能的问题。",
"timestamp": "2024-01-01T10:05:00Z"
},
{
"id": 2,
"user_message": "点击启动按钮后没有任何反应",
"assistant_response": "这种情况通常是由于网络连接或车辆状态问题导致的。请检查车辆是否处于可启动状态。",
"timestamp": "2024-01-01T10:10:00Z"
}
]
}
return jsonify(workorder)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/workorders/<int:workorder_id>', methods=['PUT'])
def update_workorder(workorder_id):
"""更新工单"""
try:
data = request.get_json()
# 验证必填字段
if not data.get('title') or not data.get('description'):
return jsonify({"error": "标题和描述不能为空"}), 400
# 这里应该更新数据库中的工单
# 暂时返回成功响应,实际应用中应该调用数据库更新
updated_workorder = {
"id": workorder_id,
"title": data.get('title'),
"description": data.get('description'),
"category": data.get('category', '技术问题'),
"priority": data.get('priority', 'medium'),
"status": data.get('status', 'open'),
"resolution": data.get('resolution'),
"satisfaction_score": data.get('satisfaction_score'),
"updated_at": datetime.now().isoformat()
}
return jsonify({
"success": True,
"message": "工单更新成功",
"workorder": updated_workorder
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 分析相关API # 分析相关API
@app.route('/api/analytics') @app.route('/api/analytics')
def get_analytics(): def get_analytics():
"""获取分析数据""" """获取分析数据"""
try: try:
analytics = assistant.generate_analytics("last_7_days") time_range = request.args.get('timeRange', '30')
dimension = request.args.get('dimension', 'workorders')
# 生成模拟分析数据
analytics = generate_analytics_data(int(time_range), dimension)
return jsonify(analytics) return jsonify(analytics)
except Exception as e: except Exception as e:
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
def generate_analytics_data(days, dimension):
"""生成分析数据"""
import random
from datetime import datetime, timedelta
# 生成时间序列数据
trend_data = []
for i in range(days):
date = (datetime.now() - timedelta(days=days-i-1)).strftime('%Y-%m-%d')
workorders = random.randint(5, 25)
alerts = random.randint(0, 10)
trend_data.append({
'date': date,
'workorders': workorders,
'alerts': alerts
})
# 工单统计
workorders_stats = {
'total': random.randint(100, 500),
'open': random.randint(10, 50),
'in_progress': random.randint(5, 30),
'resolved': random.randint(50, 200),
'closed': random.randint(20, 100),
'by_category': {
'技术问题': random.randint(20, 80),
'业务问题': random.randint(15, 60),
'系统故障': random.randint(10, 40),
'功能需求': random.randint(5, 30),
'其他': random.randint(5, 20)
},
'by_priority': {
'low': random.randint(20, 60),
'medium': random.randint(30, 80),
'high': random.randint(10, 40),
'urgent': random.randint(5, 20)
}
}
# 满意度分析
satisfaction_stats = {
'average': round(random.uniform(3.5, 4.8), 1),
'distribution': {
'1': random.randint(0, 5),
'2': random.randint(0, 10),
'3': random.randint(5, 20),
'4': random.randint(20, 50),
'5': random.randint(30, 80)
}
}
# 预警统计
alerts_stats = {
'total': random.randint(50, 200),
'active': random.randint(5, 30),
'resolved': random.randint(20, 100),
'by_level': {
'low': random.randint(10, 40),
'medium': random.randint(15, 50),
'high': random.randint(5, 25),
'critical': random.randint(2, 10)
}
}
# 性能指标
performance_stats = {
'response_time': round(random.uniform(0.5, 2.0), 2),
'uptime': round(random.uniform(95, 99.9), 1),
'error_rate': round(random.uniform(0.1, 2.0), 2),
'throughput': random.randint(1000, 5000)
}
return {
'trend': trend_data,
'workorders': workorders_stats,
'satisfaction': satisfaction_stats,
'alerts': alerts_stats,
'performance': performance_stats,
'summary': {
'total_workorders': workorders_stats['total'],
'resolution_rate': round((workorders_stats['resolved'] / workorders_stats['total']) * 100, 1) if workorders_stats['total'] > 0 else 0,
'avg_satisfaction': satisfaction_stats['average'],
'active_alerts': alerts_stats['active']
}
}
@app.route('/api/analytics/export')
def export_analytics():
"""导出分析报告"""
try:
# 生成Excel报告
analytics = generate_analytics_data(30, 'workorders')
# 创建工作簿
wb = Workbook()
ws = wb.active
ws.title = "分析报告"
# 添加标题
ws['A1'] = 'TSP智能助手分析报告'
ws['A1'].font = Font(size=16, bold=True)
# 添加工单统计
ws['A3'] = '工单统计'
ws['A3'].font = Font(bold=True)
ws['A4'] = '总工单数'
ws['B4'] = analytics['workorders']['total']
ws['A5'] = '待处理'
ws['B5'] = analytics['workorders']['open']
ws['A6'] = '已解决'
ws['B6'] = analytics['workorders']['resolved']
# 保存文件
report_path = 'uploads/analytics_report.xlsx'
os.makedirs('uploads', exist_ok=True)
wb.save(report_path)
return send_file(report_path, as_attachment=True, download_name='analytics_report.xlsx')
except Exception as e:
return jsonify({"error": str(e)}), 500
# 工单导入相关API
@app.route('/api/workorders/import', methods=['POST'])
def import_workorders():
"""导入Excel工单文件"""
try:
# 检查是否有文件上传
if 'file' not in request.files:
return jsonify({"error": "没有上传文件"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "没有选择文件"}), 400
if not file.filename.endswith(('.xlsx', '.xls')):
return jsonify({"error": "只支持Excel文件(.xlsx, .xls)"}), 400
# 保存上传的文件
filename = secure_filename(file.filename)
upload_path = os.path.join('uploads', filename)
os.makedirs('uploads', exist_ok=True)
file.save(upload_path)
# 解析Excel文件
try:
df = pd.read_excel(upload_path)
imported_workorders = []
# 处理每一行数据
for index, row in df.iterrows():
# 根据Excel列名映射到工单字段
workorder = {
"id": len(assistant.work_orders) + index + 1, # 生成新ID
"order_id": f"WO{len(assistant.work_orders) + index + 1:06d}",
"title": str(row.get('标题', row.get('title', f'导入工单 {index + 1}'))),
"description": str(row.get('描述', row.get('description', ''))),
"category": str(row.get('分类', row.get('category', '技术问题'))),
"priority": str(row.get('优先级', row.get('priority', 'medium'))),
"status": str(row.get('状态', row.get('status', 'open'))),
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat(),
"resolution": str(row.get('解决方案', row.get('resolution', ''))) if pd.notna(row.get('解决方案', row.get('resolution'))) else None,
"satisfaction_score": int(row.get('满意度', row.get('satisfaction_score', 0))) if pd.notna(row.get('满意度', row.get('satisfaction_score'))) else None
}
# 添加到工单列表(这里应该保存到数据库)
assistant.work_orders.append(workorder)
imported_workorders.append(workorder)
# 清理上传的文件
os.remove(upload_path)
return jsonify({
"success": True,
"message": f"成功导入 {len(imported_workorders)} 个工单",
"imported_count": len(imported_workorders),
"workorders": imported_workorders
})
except Exception as e:
# 清理上传的文件
if os.path.exists(upload_path):
os.remove(upload_path)
return jsonify({"error": f"解析Excel文件失败: {str(e)}"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/workorders/import/template')
def download_import_template():
"""下载工单导入模板"""
try:
# 创建模板数据
template_data = {
'标题': ['车辆无法启动', '空调不制冷', '导航系统故障'],
'描述': ['用户反映车辆无法正常启动', '空调系统无法制冷', '导航系统显示异常'],
'分类': ['技术问题', '技术问题', '技术问题'],
'优先级': ['high', 'medium', 'low'],
'状态': ['open', 'in_progress', 'resolved'],
'解决方案': ['检查电池和启动系统', '检查制冷剂和压缩机', '更新导航软件'],
'满意度': [5, 4, 5]
}
df = pd.DataFrame(template_data)
# 保存为Excel文件
template_path = 'uploads/workorder_template.xlsx'
os.makedirs('uploads', exist_ok=True)
df.to_excel(template_path, index=False)
return jsonify({
"success": True,
"template_url": f"/uploads/workorder_template.xlsx"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/uploads/<filename>')
def uploaded_file(filename):
"""提供上传文件的下载服务"""
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
# 系统设置相关API # 系统设置相关API
@app.route('/api/settings') @app.route('/api/settings')
def get_settings(): def get_settings():

View File

@@ -429,3 +429,178 @@ body {
background-color: #d1ecf1; background-color: #d1ecf1;
border-color: #17a2b8; border-color: #17a2b8;
} }
/* 预设规则卡片样式 */
.preset-card {
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
height: 100%;
}
.preset-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
border-color: #007bff;
}
.preset-card.selected {
border-color: #28a745;
background-color: #f8fff9;
}
.preset-card .card-body {
padding: 1.5rem;
text-align: center;
}
.preset-card h6 {
margin-bottom: 0.5rem;
font-weight: 600;
color: #333;
}
.preset-card p {
margin-bottom: 1rem;
color: #6c757d;
font-size: 0.875rem;
line-height: 1.4;
}
.preset-params {
display: flex;
justify-content: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.preset-params .badge {
font-size: 0.7rem;
padding: 0.25rem 0.5rem;
}
/* 预设模板模态框样式 */
#presetModal .modal-dialog {
max-width: 1200px;
}
#presetModal .modal-body {
max-height: 70vh;
overflow-y: auto;
}
/* 预设规则分类标题 */
#presetModal h6 {
font-weight: 600;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid;
}
#presetModal h6.text-primary {
border-bottom-color: #007bff;
}
#presetModal h6.text-success {
border-bottom-color: #28a745;
}
#presetModal h6.text-info {
border-bottom-color: #17a2b8;
}
#presetModal h6.text-warning {
border-bottom-color: #ffc107;
}
/* 预设卡片图标样式 */
.preset-card i {
transition: all 0.3s ease;
}
.preset-card:hover i {
transform: scale(1.1);
}
/* 预设卡片选中状态 */
.preset-card.selected i {
color: #28a745 !important;
}
.preset-card.selected h6 {
color: #28a745;
}
/* 响应式预设卡片 */
@media (max-width: 768px) {
.preset-card .card-body {
padding: 1rem;
}
.preset-card h6 {
font-size: 0.9rem;
}
.preset-card p {
font-size: 0.8rem;
}
.preset-params .badge {
font-size: 0.65rem;
padding: 0.2rem 0.4rem;
}
}
/* 预设规则快速选择 */
.preset-quick-select {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.preset-quick-select .btn {
font-size: 0.8rem;
padding: 0.25rem 0.75rem;
}
/* 预设规则预览 */
.preset-preview {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 0.375rem;
padding: 1rem;
margin-top: 1rem;
}
.preset-preview h6 {
color: #495057;
margin-bottom: 0.5rem;
}
.preset-preview .preview-params {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0.5rem;
}
.preset-preview .preview-param {
display: flex;
justify-content: space-between;
padding: 0.25rem 0;
border-bottom: 1px solid #e9ecef;
}
.preset-preview .preview-param:last-child {
border-bottom: none;
}
.preset-preview .preview-param strong {
color: #495057;
font-size: 0.875rem;
}
.preset-preview .preview-param span {
color: #6c757d;
font-size: 0.8rem;
}

View File

@@ -1198,9 +1198,14 @@ class TSPDashboard {
</div> </div>
</div> </div>
<div class="ms-3"> <div class="ms-3">
<button class="btn btn-sm btn-outline-primary" onclick="dashboard.updateWorkOrder(${workorder.id})"> <div class="btn-group" role="group">
<i class="fas fa-edit"></i> <button class="btn btn-sm btn-outline-info" onclick="dashboard.viewWorkOrderDetails(${workorder.id})" title="查看详情">
</button> <i class="fas fa-eye"></i>
</button>
<button class="btn btn-sm btn-outline-primary" onclick="dashboard.updateWorkOrder(${workorder.id})" title="编辑">
<i class="fas fa-edit"></i>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -1262,17 +1267,748 @@ class TSPDashboard {
} }
} }
async viewWorkOrderDetails(workorderId) {
try {
const response = await fetch(`/api/workorders/${workorderId}`);
const workorder = await response.json();
if (workorder.error) {
this.showNotification('获取工单详情失败', 'error');
return;
}
this.showWorkOrderDetailsModal(workorder);
} catch (error) {
console.error('获取工单详情失败:', error);
this.showNotification('获取工单详情失败', 'error');
}
}
showWorkOrderDetailsModal(workorder) {
// 创建模态框HTML
const modalHtml = `
<div class="modal fade" id="workOrderDetailsModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">工单详情 - ${workorder.order_id || workorder.id}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-6">
<h6>基本信息</h6>
<table class="table table-sm">
<tr>
<td><strong>工单号:</strong></td>
<td>${workorder.order_id || workorder.id}</td>
</tr>
<tr>
<td><strong>标题:</strong></td>
<td>${workorder.title}</td>
</tr>
<tr>
<td><strong>分类:</strong></td>
<td>${workorder.category}</td>
</tr>
<tr>
<td><strong>优先级:</strong></td>
<td><span class="badge bg-${this.getPriorityColor(workorder.priority)}">${this.getPriorityText(workorder.priority)}</span></td>
</tr>
<tr>
<td><strong>状态:</strong></td>
<td><span class="badge bg-${this.getStatusColor(workorder.status)}">${this.getStatusText(workorder.status)}</span></td>
</tr>
<tr>
<td><strong>创建时间:</strong></td>
<td>${new Date(workorder.created_at).toLocaleString()}</td>
</tr>
<tr>
<td><strong>更新时间:</strong></td>
<td>${new Date(workorder.updated_at).toLocaleString()}</td>
</tr>
</table>
</div>
<div class="col-md-6">
<h6>问题描述</h6>
<div class="border p-3 rounded">
${workorder.description}
</div>
${workorder.resolution ? `
<h6 class="mt-3">解决方案</h6>
<div class="border p-3 rounded bg-light">
${workorder.resolution}
</div>
` : ''}
${workorder.satisfaction_score ? `
<h6 class="mt-3">满意度评分</h6>
<div class="border p-3 rounded">
<div class="progress">
<div class="progress-bar" style="width: ${workorder.satisfaction_score * 100}%"></div>
</div>
<small class="text-muted">${workorder.satisfaction_score}/5.0</small>
</div>
` : ''}
</div>
</div>
${workorder.conversations && workorder.conversations.length > 0 ? `
<h6>对话记录</h6>
<div class="conversation-history" style="max-height: 300px; overflow-y: auto;">
${workorder.conversations.map(conv => `
<div class="border-bottom pb-2 mb-2">
<div class="d-flex justify-content-between">
<small class="text-muted">${new Date(conv.timestamp).toLocaleString()}</small>
</div>
<div class="mb-1">
<strong>用户:</strong> ${conv.user_message}
</div>
<div>
<strong>助手:</strong> ${conv.assistant_response}
</div>
</div>
`).join('')}
</div>
` : ''}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" onclick="dashboard.updateWorkOrder(${workorder.id})">编辑工单</button>
</div>
</div>
</div>
</div>
`;
// 移除已存在的模态框
const existingModal = document.getElementById('workOrderDetailsModal');
if (existingModal) {
existingModal.remove();
}
// 添加新的模态框到页面
document.body.insertAdjacentHTML('beforeend', modalHtml);
// 显示模态框
const modal = new bootstrap.Modal(document.getElementById('workOrderDetailsModal'));
modal.show();
// 模态框关闭时移除DOM元素
document.getElementById('workOrderDetailsModal').addEventListener('hidden.bs.modal', function() {
this.remove();
});
}
async updateWorkOrder(workorderId) {
try {
// 获取工单详情
const response = await fetch(`/api/workorders/${workorderId}`);
const workorder = await response.json();
if (response.ok) {
this.showEditWorkOrderModal(workorder);
} else {
throw new Error(workorder.error || '获取工单详情失败');
}
} catch (error) {
console.error('获取工单详情失败:', error);
this.showNotification('获取工单详情失败: ' + error.message, 'error');
}
}
showEditWorkOrderModal(workorder) {
// 创建编辑工单模态框
const modalHtml = `
<div class="modal fade" id="editWorkOrderModal" tabindex="-1" aria-labelledby="editWorkOrderModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editWorkOrderModalLabel">编辑工单 #${workorder.id}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editWorkOrderForm">
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="editTitle" class="form-label">标题 *</label>
<input type="text" class="form-control" id="editTitle" value="${workorder.title}" required>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="editPriority" class="form-label">优先级</label>
<select class="form-select" id="editPriority">
<option value="low" ${workorder.priority === 'low' ? 'selected' : ''}>低</option>
<option value="medium" ${workorder.priority === 'medium' ? 'selected' : ''}>中</option>
<option value="high" ${workorder.priority === 'high' ? 'selected' : ''}>高</option>
<option value="urgent" ${workorder.priority === 'urgent' ? 'selected' : ''}>紧急</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="editCategory" class="form-label">分类</label>
<select class="form-select" id="editCategory">
<option value="技术问题" ${workorder.category === '技术问题' ? 'selected' : ''}>技术问题</option>
<option value="业务问题" ${workorder.category === '业务问题' ? 'selected' : ''}>业务问题</option>
<option value="系统故障" ${workorder.category === '系统故障' ? 'selected' : ''}>系统故障</option>
<option value="功能需求" ${workorder.category === '功能需求' ? 'selected' : ''}>功能需求</option>
<option value="其他" ${workorder.category === '其他' ? 'selected' : ''}>其他</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="editStatus" class="form-label">状态</label>
<select class="form-select" id="editStatus">
<option value="open" ${workorder.status === 'open' ? 'selected' : ''}>待处理</option>
<option value="in_progress" ${workorder.status === 'in_progress' ? 'selected' : ''}>处理中</option>
<option value="resolved" ${workorder.status === 'resolved' ? 'selected' : ''}>已解决</option>
<option value="closed" ${workorder.status === 'closed' ? 'selected' : ''}>已关闭</option>
</select>
</div>
</div>
</div>
<div class="mb-3">
<label for="editDescription" class="form-label">描述 *</label>
<textarea class="form-control" id="editDescription" rows="4" required>${workorder.description}</textarea>
</div>
<div class="mb-3">
<label for="editResolution" class="form-label">解决方案</label>
<textarea class="form-control" id="editResolution" rows="3" placeholder="请输入解决方案...">${workorder.resolution || ''}</textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="editSatisfactionScore" class="form-label">满意度评分 (1-5)</label>
<input type="number" class="form-control" id="editSatisfactionScore" min="1" max="5" value="${workorder.satisfaction_score || ''}">
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="dashboard.saveWorkOrder(${workorder.id})">保存修改</button>
</div>
</div>
</div>
</div>
`;
// 移除已存在的模态框
const existingModal = document.getElementById('editWorkOrderModal');
if (existingModal) {
existingModal.remove();
}
// 添加新模态框到页面
document.body.insertAdjacentHTML('beforeend', modalHtml);
// 显示模态框
const modal = new bootstrap.Modal(document.getElementById('editWorkOrderModal'));
modal.show();
// 模态框关闭时清理
document.getElementById('editWorkOrderModal').addEventListener('hidden.bs.modal', function() {
this.remove();
});
}
async saveWorkOrder(workorderId) {
try {
// 获取表单数据
const formData = {
title: document.getElementById('editTitle').value,
description: document.getElementById('editDescription').value,
category: document.getElementById('editCategory').value,
priority: document.getElementById('editPriority').value,
status: document.getElementById('editStatus').value,
resolution: document.getElementById('editResolution').value,
satisfaction_score: parseInt(document.getElementById('editSatisfactionScore').value) || null
};
// 验证必填字段
if (!formData.title.trim() || !formData.description.trim()) {
this.showNotification('标题和描述不能为空', 'error');
return;
}
// 发送更新请求
const response = await fetch(`/api/workorders/${workorderId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
});
const result = await response.json();
if (response.ok) {
this.showNotification('工单更新成功', 'success');
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('editWorkOrderModal'));
modal.hide();
// 刷新工单列表
this.loadWorkOrders();
} else {
throw new Error(result.error || '更新工单失败');
}
} catch (error) {
console.error('更新工单失败:', error);
this.showNotification('更新工单失败: ' + error.message, 'error');
}
}
// 工单导入功能
showImportModal() {
// 显示导入模态框
const modal = new bootstrap.Modal(document.getElementById('importWorkOrderModal'));
modal.show();
// 重置表单
document.getElementById('excel-file-input').value = '';
document.getElementById('import-progress').classList.add('d-none');
document.getElementById('import-result').classList.add('d-none');
}
async downloadTemplate() {
try {
const response = await fetch('/api/workorders/import/template');
const result = await response.json();
if (result.success) {
// 创建下载链接
const link = document.createElement('a');
link.href = result.template_url;
link.download = '工单导入模板.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
this.showNotification('模板下载成功', 'success');
} else {
throw new Error(result.error || '下载模板失败');
}
} catch (error) {
console.error('下载模板失败:', error);
this.showNotification('下载模板失败: ' + error.message, 'error');
}
}
async importWorkOrders() {
const fileInput = document.getElementById('excel-file-input');
const file = fileInput.files[0];
if (!file) {
this.showNotification('请选择要导入的Excel文件', 'error');
return;
}
// 验证文件类型
if (!file.name.match(/\.(xlsx|xls)$/)) {
this.showNotification('只支持Excel文件(.xlsx, .xls)', 'error');
return;
}
// 验证文件大小
if (file.size > 16 * 1024 * 1024) {
this.showNotification('文件大小不能超过16MB', 'error');
return;
}
// 显示进度条
document.getElementById('import-progress').classList.remove('d-none');
document.getElementById('import-result').classList.add('d-none');
try {
// 创建FormData
const formData = new FormData();
formData.append('file', file);
// 发送导入请求
const response = await fetch('/api/workorders/import', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok && result.success) {
// 显示成功消息
document.getElementById('import-progress').classList.add('d-none');
document.getElementById('import-result').classList.remove('d-none');
document.getElementById('import-success-message').textContent =
`成功导入 ${result.imported_count} 个工单`;
this.showNotification(result.message, 'success');
// 刷新工单列表
this.loadWorkOrders();
// 3秒后关闭模态框
setTimeout(() => {
const modal = bootstrap.Modal.getInstance(document.getElementById('importWorkOrderModal'));
modal.hide();
}, 3000);
} else {
throw new Error(result.error || '导入工单失败');
}
} catch (error) {
console.error('导入工单失败:', error);
document.getElementById('import-progress').classList.add('d-none');
this.showNotification('导入工单失败: ' + error.message, 'error');
}
}
// 数据分析 // 数据分析
async loadAnalytics() { async loadAnalytics() {
try { try {
const response = await fetch('/api/analytics'); const response = await fetch('/api/analytics');
const analytics = await response.json(); const analytics = await response.json();
this.updateAnalyticsDisplay(analytics); this.updateAnalyticsDisplay(analytics);
this.initializeCharts();
} catch (error) { } catch (error) {
console.error('加载分析数据失败:', error); console.error('加载分析数据失败:', error);
} }
} }
// 初始化图表
initializeCharts() {
this.charts = {};
this.updateCharts();
}
// 更新所有图表
async updateCharts() {
try {
const timeRange = document.getElementById('timeRange').value;
const chartType = document.getElementById('chartType').value;
const dataDimension = document.getElementById('dataDimension').value;
// 获取数据
const response = await fetch(`/api/analytics?timeRange=${timeRange}&dimension=${dataDimension}`);
const data = await response.json();
// 更新统计卡片
this.updateStatisticsCards(data);
// 更新图表
this.updateMainChart(data, chartType);
this.updateDistributionChart(data);
this.updateTrendChart(data);
this.updatePriorityChart(data);
// 更新分析报告
this.updateAnalyticsReport(data);
} catch (error) {
console.error('更新图表失败:', error);
this.showNotification('更新图表失败: ' + error.message, 'error');
}
}
// 更新统计卡片
updateStatisticsCards(data) {
const total = data.workorders?.total || 0;
const open = data.workorders?.open || 0;
const resolved = data.workorders?.resolved || 0;
const avgSatisfaction = data.satisfaction?.average || 0;
document.getElementById('totalWorkorders').textContent = total;
document.getElementById('openWorkorders').textContent = open;
document.getElementById('resolvedWorkorders').textContent = resolved;
document.getElementById('avgSatisfaction').textContent = avgSatisfaction.toFixed(1);
// 更新进度条
if (total > 0) {
document.getElementById('openProgress').style.width = `${(open / total) * 100}%`;
document.getElementById('resolvedProgress').style.width = `${(resolved / total) * 100}%`;
document.getElementById('satisfactionProgress').style.width = `${(avgSatisfaction / 5) * 100}%`;
}
}
// 更新主图表
updateMainChart(data, chartType) {
const ctx = document.getElementById('mainChart').getContext('2d');
// 销毁现有图表
if (this.charts.mainChart) {
this.charts.mainChart.destroy();
}
const chartData = this.prepareChartData(data, chartType);
this.charts.mainChart = new Chart(ctx, {
type: chartType,
data: chartData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '数据分析趋势'
},
legend: {
display: true,
position: 'top'
}
},
scales: chartType === 'pie' || chartType === 'doughnut' ? {} : {
x: {
display: true,
title: {
display: true,
text: '时间'
}
},
y: {
display: true,
title: {
display: true,
text: '数量'
}
}
}
}
});
}
// 更新分布图表
updateDistributionChart(data) {
const ctx = document.getElementById('distributionChart').getContext('2d');
if (this.charts.distributionChart) {
this.charts.distributionChart.destroy();
}
const categories = data.workorders?.by_category || {};
const labels = Object.keys(categories);
const values = Object.values(categories);
this.charts.distributionChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: values,
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9966FF',
'#FF9F40'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '工单分类分布'
},
legend: {
display: true,
position: 'bottom'
}
}
}
});
}
// 更新趋势图表
updateTrendChart(data) {
const ctx = document.getElementById('trendChart').getContext('2d');
if (this.charts.trendChart) {
this.charts.trendChart.destroy();
}
const trendData = data.trend || [];
const labels = trendData.map(item => item.date);
const workorders = trendData.map(item => item.workorders);
const alerts = trendData.map(item => item.alerts);
this.charts.trendChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '工单数量',
data: workorders,
borderColor: '#36A2EB',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
tension: 0.4
}, {
label: '预警数量',
data: alerts,
borderColor: '#FF6384',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '时间趋势分析'
}
},
scales: {
x: {
display: true,
title: {
display: true,
text: '日期'
}
},
y: {
display: true,
title: {
display: true,
text: '数量'
}
}
}
}
});
}
// 更新优先级图表
updatePriorityChart(data) {
const ctx = document.getElementById('priorityChart').getContext('2d');
if (this.charts.priorityChart) {
this.charts.priorityChart.destroy();
}
const priorities = data.workorders?.by_priority || {};
const labels = Object.keys(priorities).map(p => this.getPriorityText(p));
const values = Object.values(priorities);
this.charts.priorityChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: '工单数量',
data: values,
backgroundColor: [
'#28a745', // 低 - 绿色
'#ffc107', // 中 - 黄色
'#fd7e14', // 高 - 橙色
'#dc3545' // 紧急 - 红色
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '优先级分布'
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
// 准备图表数据
prepareChartData(data, chartType) {
const trendData = data.trend || [];
const labels = trendData.map(item => item.date);
const workorders = trendData.map(item => item.workorders);
if (chartType === 'pie' || chartType === 'doughnut') {
const categories = data.workorders?.by_category || {};
return {
labels: Object.keys(categories),
datasets: [{
data: Object.values(categories),
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9966FF',
'#FF9F40'
]
}]
};
} else {
return {
labels: labels,
datasets: [{
label: '工单数量',
data: workorders,
borderColor: '#36A2EB',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
tension: chartType === 'line' ? 0.4 : 0
}]
};
}
}
// 导出图表
exportChart(chartId) {
if (this.charts[chartId]) {
const link = document.createElement('a');
link.download = `${chartId}_chart.png`;
link.href = this.charts[chartId].toBase64Image();
link.click();
}
}
// 全屏图表
fullscreenChart(chartId) {
// 这里可以实现全屏显示功能
this.showNotification('全屏功能开发中', 'info');
}
// 导出报告
async exportReport() {
try {
const response = await fetch('/api/analytics/export');
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'analytics_report.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('导出报告失败:', error);
this.showNotification('导出报告失败: ' + error.message, 'error');
}
}
// 打印报告
printReport() {
window.print();
}
updateAnalyticsDisplay(analytics) { updateAnalyticsDisplay(analytics) {
// 更新分析报告 // 更新分析报告
const reportContainer = document.getElementById('analytics-report'); const reportContainer = document.getElementById('analytics-report');

View File

@@ -844,9 +844,17 @@
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-tasks me-2"></i>工单管理</h5> <h5><i class="fas fa-tasks me-2"></i>工单管理</h5>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#createWorkOrderModal"> <div class="btn-group" role="group">
<i class="fas fa-plus me-1"></i>创建工单 <button class="btn btn-success btn-sm" onclick="dashboard.downloadTemplate()">
</button> <i class="fas fa-download me-1"></i>下载模板
</button>
<button class="btn btn-info btn-sm" onclick="dashboard.showImportModal()">
<i class="fas fa-upload me-1"></i>导入工单
</button>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#createWorkOrderModal">
<i class="fas fa-plus me-1"></i>创建工单
</button>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="mb-3"> <div class="mb-3">
@@ -914,43 +922,210 @@
<!-- 数据分析标签页 --> <!-- 数据分析标签页 -->
<div id="analytics-tab" class="tab-content" style="display: none;"> <div id="analytics-tab" class="tab-content" style="display: none;">
<!-- 图表控制面板 -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-6"> <div class="col-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h5><i class="fas fa-chart-line me-2"></i>性能趋势</h5> <h5><i class="fas fa-chart-bar me-2"></i>数据分析控制面板</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="chart-container"> <div class="row">
<canvas id="analyticsChart"></canvas> <div class="col-md-3">
</div> <label class="form-label">时间范围</label>
</div> <select class="form-select" id="timeRange">
</div> <option value="7">最近7天</option>
</div> <option value="30" selected>最近30天</option>
<div class="col-md-6"> <option value="90">最近90天</option>
<div class="card"> <option value="365">最近1年</option>
<div class="card-header"> </select>
<h5><i class="fas fa-chart-pie me-2"></i>类别分布</h5> </div>
</div> <div class="col-md-3">
<div class="card-body"> <label class="form-label">图表类型</label>
<div class="chart-container"> <select class="form-select" id="chartType">
<canvas id="categoryChart"></canvas> <option value="line">折线图</option>
<option value="bar" selected>柱状图</option>
<option value="pie">饼图</option>
<option value="doughnut">环形图</option>
<option value="radar">雷达图</option>
<option value="polar">极坐标图</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">数据维度</label>
<select class="form-select" id="dataDimension">
<option value="workorders" selected>工单统计</option>
<option value="alerts">预警统计</option>
<option value="performance">性能指标</option>
<option value="satisfaction">满意度分析</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">操作</label>
<div class="d-grid">
<button class="btn btn-primary" onclick="dashboard.updateCharts()">
<i class="fas fa-sync-alt me-1"></i>刷新图表
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- 主要图表区域 -->
<div class="row mb-4">
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-chart-line me-2"></i>主要趋势分析</h5>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary" onclick="dashboard.exportChart('mainChart')">
<i class="fas fa-download me-1"></i>导出
</button>
<button type="button" class="btn btn-outline-secondary" onclick="dashboard.fullscreenChart('mainChart')">
<i class="fas fa-expand me-1"></i>全屏
</button>
</div>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height: 400px;">
<canvas id="mainChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-chart-pie me-2"></i>分布分析</h5>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height: 300px;">
<canvas id="distributionChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- 详细统计卡片 -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="d-flex align-items-center justify-content-center mb-2">
<i class="fas fa-tasks fa-2x text-primary me-3"></i>
<div>
<h3 class="mb-0" id="totalWorkorders">0</h3>
<small class="text-muted">总工单数</small>
</div>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar bg-primary" role="progressbar" style="width: 100%"></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="d-flex align-items-center justify-content-center mb-2">
<i class="fas fa-exclamation-triangle fa-2x text-warning me-3"></i>
<div>
<h3 class="mb-0" id="openWorkorders">0</h3>
<small class="text-muted">待处理</small>
</div>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar bg-warning" role="progressbar" id="openProgress" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="d-flex align-items-center justify-content-center mb-2">
<i class="fas fa-check-circle fa-2x text-success me-3"></i>
<div>
<h3 class="mb-0" id="resolvedWorkorders">0</h3>
<small class="text-muted">已解决</small>
</div>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar bg-success" role="progressbar" id="resolvedProgress" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="d-flex align-items-center justify-content-center mb-2">
<i class="fas fa-star fa-2x text-info me-3"></i>
<div>
<h3 class="mb-0" id="avgSatisfaction">0</h3>
<small class="text-muted">平均满意度</small>
</div>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar bg-info" role="progressbar" id="satisfactionProgress" style="width: 0%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 多维度分析图表 -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-chart-area me-2"></i>时间趋势分析</h5>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height: 300px;">
<canvas id="trendChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-chart-bar me-2"></i>优先级分布</h5>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height: 300px;">
<canvas id="priorityChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- 详细分析报告 -->
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-table me-2"></i>详细分析报告</h5> <h5><i class="fas fa-table me-2"></i>详细分析报告</h5>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary" onclick="dashboard.exportReport()">
<i class="fas fa-file-excel me-1"></i>导出Excel
</button>
<button type="button" class="btn btn-outline-secondary" onclick="dashboard.printReport()">
<i class="fas fa-print me-1"></i>打印报告
</button>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div id="analytics-report"> <div id="analytics-report">
<div class="loading-spinner"> <div class="loading-spinner text-center">
<i class="fas fa-spinner fa-spin"></i> <i class="fas fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">正在生成分析报告...</p>
</div> </div>
</div> </div>
</div> </div>
@@ -1074,6 +1249,118 @@
</div> </div>
</div> </div>
<!-- 工单导入模态框 -->
<div class="modal fade" id="importWorkOrderModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">导入工单</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
请先下载模板文件按照模板格式填写工单信息然后上传Excel文件进行导入。
</div>
<div class="mb-3">
<label class="form-label">选择Excel文件</label>
<input type="file" class="form-control" id="excel-file-input" accept=".xlsx,.xls">
<div class="form-text">支持 .xlsx 和 .xls 格式文件大小不超过16MB</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center">
<span>Excel文件列名说明</span>
<button class="btn btn-outline-primary btn-sm" onclick="dashboard.downloadTemplate()">
<i class="fas fa-download me-1"></i>下载模板
</button>
</div>
<div class="table-responsive mt-2">
<table class="table table-sm table-bordered">
<thead class="table-light">
<tr>
<th>列名</th>
<th>说明</th>
<th>必填</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>标题</td>
<td>工单标题</td>
<td><span class="badge bg-danger"></span></td>
<td>车辆无法启动</td>
</tr>
<tr>
<td>描述</td>
<td>工单详细描述</td>
<td><span class="badge bg-danger"></span></td>
<td>用户反映车辆无法正常启动</td>
</tr>
<tr>
<td>分类</td>
<td>工单分类</td>
<td><span class="badge bg-secondary"></span></td>
<td>技术问题</td>
</tr>
<tr>
<td>优先级</td>
<td>工单优先级</td>
<td><span class="badge bg-secondary"></span></td>
<td>high</td>
</tr>
<tr>
<td>状态</td>
<td>工单状态</td>
<td><span class="badge bg-secondary"></span></td>
<td>open</td>
</tr>
<tr>
<td>解决方案</td>
<td>解决方案描述</td>
<td><span class="badge bg-secondary"></span></td>
<td>检查电池和启动系统</td>
</tr>
<tr>
<td>满意度</td>
<td>满意度评分(1-5)</td>
<td><span class="badge bg-secondary"></span></td>
<td>5</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="import-progress" class="d-none">
<div class="progress mb-3">
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
</div>
<div class="text-center">
<i class="fas fa-spinner fa-spin me-2"></i>
<span id="import-status">正在导入工单...</span>
</div>
</div>
<div id="import-result" class="d-none">
<div class="alert alert-success">
<i class="fas fa-check-circle me-2"></i>
<span id="import-success-message"></span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="import-workorder-btn" onclick="dashboard.importWorkOrders()">
<i class="fas fa-upload me-1"></i>开始导入
</button>
</div>
</div>
</div>
</div>
<!-- 添加知识模态框 --> <!-- 添加知识模态框 -->
<div class="modal fade" id="addKnowledgeModal" tabindex="-1"> <div class="modal fade" id="addKnowledgeModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">

View File

@@ -166,11 +166,27 @@
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-cogs me-2"></i>预警规则管理</h5> <h5><i class="fas fa-cogs me-2"></i>预警规则管理</h5>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#ruleModal"> <div class="btn-group">
<i class="fas fa-plus me-1"></i>添加规则 <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#ruleModal">
</button> <i class="fas fa-plus me-1"></i>添加规则
</button>
<button class="btn btn-sm btn-success" data-bs-toggle="modal" data-bs-target="#presetModal">
<i class="fas fa-magic me-1"></i>预设模板
</button>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<!-- 预设规则卡片 -->
<div class="row mb-4">
<div class="col-12">
<h6 class="text-muted mb-3">常用预警规则模板</h6>
<div class="row" id="preset-rules">
<!-- 预设规则卡片将在这里动态生成 -->
</div>
</div>
</div>
<!-- 自定义规则列表 -->
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
@@ -377,6 +393,260 @@
</div> </div>
</div> </div>
<!-- 预设模板模态框 -->
<div class="modal fade" id="presetModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">预警规则预设模板</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row">
<!-- 性能预警模板 -->
<div class="col-md-6 mb-4">
<h6 class="text-primary mb-3"><i class="fas fa-tachometer-alt me-2"></i>性能预警模板</h6>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="response_time">
<div class="card-body text-center">
<i class="fas fa-clock fa-2x text-warning mb-2"></i>
<h6>响应时间预警</h6>
<p class="small text-muted">API响应时间超过阈值</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-info">性能</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="cpu_usage">
<div class="card-body text-center">
<i class="fas fa-microchip fa-2x text-danger mb-2"></i>
<h6>CPU使用率预警</h6>
<p class="small text-muted">CPU使用率过高</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-info">性能</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="memory_usage">
<div class="card-body text-center">
<i class="fas fa-memory fa-2x text-warning mb-2"></i>
<h6>内存使用率预警</h6>
<p class="small text-muted">内存使用率过高</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-info">性能</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="disk_usage">
<div class="card-body text-center">
<i class="fas fa-hdd fa-2x text-danger mb-2"></i>
<h6>磁盘使用率预警</h6>
<p class="small text-muted">磁盘空间不足</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-info">性能</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 业务预警模板 -->
<div class="col-md-6 mb-4">
<h6 class="text-success mb-3"><i class="fas fa-chart-line me-2"></i>业务预警模板</h6>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="satisfaction_low">
<div class="card-body text-center">
<i class="fas fa-frown fa-2x text-warning mb-2"></i>
<h6>满意度预警</h6>
<p class="small text-muted">用户满意度低于阈值</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-success">业务</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="workorder_high">
<div class="card-body text-center">
<i class="fas fa-tasks fa-2x text-danger mb-2"></i>
<h6>工单积压预警</h6>
<p class="small text-muted">待处理工单过多</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-success">业务</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="error_rate_high">
<div class="card-body text-center">
<i class="fas fa-exclamation-triangle fa-2x text-danger mb-2"></i>
<h6>错误率预警</h6>
<p class="small text-muted">系统错误率过高</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-success">业务</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="conversion_low">
<div class="card-body text-center">
<i class="fas fa-percentage fa-2x text-warning mb-2"></i>
<h6>转化率预警</h6>
<p class="small text-muted">用户转化率下降</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-success">业务</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 系统预警模板 -->
<div class="col-md-6 mb-4">
<h6 class="text-info mb-3"><i class="fas fa-server me-2"></i>系统预警模板</h6>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="service_down">
<div class="card-body text-center">
<i class="fas fa-power-off fa-2x text-danger mb-2"></i>
<h6>服务宕机预警</h6>
<p class="small text-muted">关键服务不可用</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-info">系统</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="database_slow">
<div class="card-body text-center">
<i class="fas fa-database fa-2x text-warning mb-2"></i>
<h6>数据库慢查询</h6>
<p class="small text-muted">数据库查询过慢</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-info">系统</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="network_latency">
<div class="card-body text-center">
<i class="fas fa-wifi fa-2x text-warning mb-2"></i>
<h6>网络延迟预警</h6>
<p class="small text-muted">网络延迟过高</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-info">系统</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="api_error">
<div class="card-body text-center">
<i class="fas fa-bug fa-2x text-danger mb-2"></i>
<h6>API错误预警</h6>
<p class="small text-muted">API调用失败率过高</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-info">系统</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 质量预警模板 -->
<div class="col-md-6 mb-4">
<h6 class="text-warning mb-3"><i class="fas fa-award me-2"></i>质量预警模板</h6>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="code_quality">
<div class="card-body text-center">
<i class="fas fa-code fa-2x text-warning mb-2"></i>
<h6>代码质量预警</h6>
<p class="small text-muted">代码质量评分过低</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-warning">质量</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="test_coverage">
<div class="card-body text-center">
<i class="fas fa-check-circle fa-2x text-info mb-2"></i>
<h6>测试覆盖率预警</h6>
<p class="small text-muted">测试覆盖率不足</p>
<div class="preset-params">
<span class="badge bg-info">信息</span>
<span class="badge bg-warning">质量</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="security_scan">
<div class="card-body text-center">
<i class="fas fa-shield-alt fa-2x text-danger mb-2"></i>
<h6>安全扫描预警</h6>
<p class="small text-muted">发现安全漏洞</p>
<div class="preset-params">
<span class="badge bg-danger">严重</span>
<span class="badge bg-warning">质量</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card preset-card" data-preset="performance_regression">
<div class="card-body text-center">
<i class="fas fa-chart-line fa-2x text-warning mb-2"></i>
<h6>性能回归预警</h6>
<p class="small text-muted">性能指标下降</p>
<div class="preset-params">
<span class="badge bg-warning">警告</span>
<span class="badge bg-warning">质量</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 脚本 --> <!-- 脚本 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

View File

@@ -213,6 +213,19 @@ class WebSocketServer:
async def handle_client(self, websocket: WebSocketServerProtocol, path: str): async def handle_client(self, websocket: WebSocketServerProtocol, path: str):
"""处理客户端连接""" """处理客户端连接"""
# 检查连接头
headers = websocket.request_headers
connection = headers.get("Connection", "").lower()
# 处理不同的连接头格式
if "upgrade" not in connection and "keep-alive" in connection:
logger.warning(f"收到非标准连接头: {connection}")
# 对于keep-alive连接头我们仍然接受连接
elif "upgrade" not in connection:
logger.warning(f"连接头不包含upgrade: {connection}")
await websocket.close(code=1002, reason="Invalid connection header")
return
await self.register_client(websocket) await self.register_client(websocket)
try: try:
@@ -220,6 +233,8 @@ class WebSocketServer:
await self.handle_message(websocket, message) await self.handle_message(websocket, message)
except websockets.exceptions.ConnectionClosed: except websockets.exceptions.ConnectionClosed:
pass pass
except Exception as e:
logger.error(f"WebSocket连接错误: {e}")
finally: finally:
await self.unregister_client(websocket) await self.unregister_client(websocket)
@@ -227,9 +242,48 @@ class WebSocketServer:
"""启动WebSocket服务器""" """启动WebSocket服务器"""
logger.info(f"启动WebSocket服务器: ws://{self.host}:{self.port}") logger.info(f"启动WebSocket服务器: ws://{self.host}:{self.port}")
async with websockets.serve(self.handle_client, self.host, self.port): # 添加CORS支持
async def handle_client_with_cors(websocket: WebSocketServerProtocol, path: str):
# 设置CORS头
if websocket.request_headers.get("Origin"):
# 允许跨域连接
pass
await self.handle_client(websocket, path)
async with websockets.serve(
handle_client_with_cors,
self.host,
self.port,
# 添加额外的服务器选项
process_request=self._process_request
):
await asyncio.Future() # 保持服务器运行 await asyncio.Future() # 保持服务器运行
def _process_request(self, path, request_headers):
"""处理HTTP请求支持CORS"""
# 检查是否是WebSocket升级请求
if request_headers.get("Upgrade", "").lower() == "websocket":
return None # 允许WebSocket连接
# 对于非WebSocket请求返回简单的HTML页面
return (
200,
[("Content-Type", "text/html; charset=utf-8")],
b"""
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Server</title>
</head>
<body>
<h1>WebSocket Server is running</h1>
<p>This is a WebSocket server. Please use a WebSocket client to connect.</p>
<p>WebSocket URL: ws://localhost:8765</p>
</body>
</html>
"""
)
def run(self): def run(self):
"""运行服务器""" """运行服务器"""
asyncio.run(self.start_server()) asyncio.run(self.start_server())

View File

@@ -7,6 +7,8 @@
import sys import sys
import os import os
import logging import logging
import threading
import asyncio
from datetime import datetime from datetime import datetime
# 添加项目根目录到Python路径 # 添加项目根目录到Python路径
@@ -23,6 +25,15 @@ def setup_logging():
] ]
) )
def start_websocket_server():
"""启动WebSocket服务器"""
try:
from src.web.websocket_server import WebSocketServer
server = WebSocketServer(host="localhost", port=8765)
server.run()
except Exception as e:
print(f"WebSocket服务器启动失败: {e}")
def main(): def main():
"""主函数""" """主函数"""
print("=" * 60) print("=" * 60)
@@ -58,10 +69,15 @@ def main():
print(" 主页: http://localhost:5000") print(" 主页: http://localhost:5000")
print(" 预警管理: http://localhost:5000/alerts") print(" 预警管理: http://localhost:5000/alerts")
print(" 实时对话: http://localhost:5000/chat") print(" 实时对话: http://localhost:5000/chat")
print(" WebSocket: ws://localhost:8765")
print() print()
print("按 Ctrl+C 停止服务") print("按 Ctrl+C 停止服务")
print("=" * 60) print("=" * 60)
# 在单独线程中启动WebSocket服务器
websocket_thread = threading.Thread(target=start_websocket_server, daemon=True)
websocket_thread.start()
# 启动Flask应用 # 启动Flask应用
app.run( app.run(
debug=False, debug=False,

Binary file not shown.