# -*- coding: utf-8 -*- """ TSP助手预警管理Web应用 提供预警系统的Web界面和API接口 重构版本 - 使用蓝图架构 """ import sys import os import logging from datetime import datetime, timedelta from flask import Flask, render_template, request, jsonify, send_from_directory from flask_cors import CORS # 添加项目根目录到Python路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.main import TSPAssistant from src.agent_assistant import TSPAgentAssistant from src.dialogue.realtime_chat import RealtimeChatManager from src.vehicle.vehicle_data_manager import VehicleDataManager from src.core.database import db_manager from src.core.models import Conversation, Alert, WorkOrder from src.core.query_optimizer import query_optimizer # 导入蓝图 from src.web.blueprints.alerts import alerts_bp from src.web.blueprints.workorders import workorders_bp from src.web.blueprints.conversations import conversations_bp from src.web.blueprints.knowledge import knowledge_bp from src.web.blueprints.monitoring import monitoring_bp from src.web.blueprints.system import system_bp # 配置日志 logger = logging.getLogger(__name__) # 抑制 /api/health 的访问日志 werkzeug_logger = logging.getLogger('werkzeug') class HealthLogFilter(logging.Filter): def filter(self, record): try: msg = record.getMessage() return '/api/health' not in msg except Exception: return True werkzeug_logger.addFilter(HealthLogFilter()) # 创建Flask应用 app = Flask(__name__) 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助手(避免启动时重复初始化) assistant = None agent_assistant = None chat_manager = None vehicle_manager = None def get_assistant(): """获取TSP助手实例(懒加载)""" global assistant if assistant is None: assistant = TSPAssistant() return assistant def get_agent_assistant(): """获取Agent助手实例(懒加载)""" global agent_assistant if agent_assistant is None: agent_assistant = TSPAgentAssistant() return agent_assistant def get_chat_manager(): """获取聊天管理器实例(懒加载)""" global chat_manager if chat_manager is None: chat_manager = RealtimeChatManager() return chat_manager def get_vehicle_manager(): """获取车辆数据管理器实例(懒加载)""" global vehicle_manager if vehicle_manager is None: vehicle_manager = VehicleDataManager() return vehicle_manager # 注册蓝图 app.register_blueprint(alerts_bp) app.register_blueprint(workorders_bp) app.register_blueprint(conversations_bp) app.register_blueprint(knowledge_bp) app.register_blueprint(monitoring_bp) app.register_blueprint(system_bp) # 页面路由 @app.route('/') def index(): """主页 - 综合管理平台""" return render_template('dashboard.html') @app.route('/alerts') def alerts(): """预警管理页面""" return render_template('index.html') @app.route('/chat') def chat(): """实时对话页面 (WebSocket版本)""" return render_template('chat.html') @app.route('/chat-http') def chat_http(): """实时对话页面 (HTTP版本)""" return render_template('chat_http.html') @app.route('/uploads/') def uploaded_file(filename): """提供上传文件的下载服务""" return send_from_directory(app.config['UPLOAD_FOLDER'], filename) # 核心API路由 @app.route('/api/health') def get_health(): """获取系统健康状态(附加1小时业务指标)""" try: base = get_assistant().get_system_health() or {} # 追加数据库近1小时指标 with db_manager.get_session() as session: since = datetime.now() - timedelta(hours=1) conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() levels = session.query(Alert.level).filter(Alert.is_active == True).all() level_map = {} for (lvl,) in levels: level_map[lvl] = level_map.get(lvl, 0) + 1 base.update({ "throughput_1h": conv_count, "avg_response_time_1h": avg_resp, "open_workorders": open_wos, "active_alerts_by_level": level_map }) return jsonify(base) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/rules') def get_rules(): """获取预警规则列表""" try: rules = get_assistant().alert_system.rules rules_data = [] for name, rule in rules.items(): rules_data.append({ "name": rule.name, "description": rule.description, "alert_type": rule.alert_type.value, "level": rule.level.value, "threshold": rule.threshold, "condition": rule.condition, "enabled": rule.enabled, "check_interval": rule.check_interval, "cooldown": rule.cooldown }) return jsonify(rules_data) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/rules', methods=['POST']) def create_rule(): """创建预警规则""" try: from src.analytics.alert_system import AlertRule, AlertLevel, AlertType data = request.get_json() rule = AlertRule( name=data['name'], description=data['description'], alert_type=AlertType(data['alert_type']), level=AlertLevel(data['level']), threshold=float(data['threshold']), condition=data['condition'], enabled=data.get('enabled', True), check_interval=int(data.get('check_interval', 300)), cooldown=int(data.get('cooldown', 3600)) ) success = get_assistant().alert_system.add_custom_rule(rule) if success: return jsonify({"success": True, "message": "规则创建成功"}) else: return jsonify({"success": False, "message": "规则创建失败"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/rules/', methods=['PUT']) def update_rule(rule_name): """更新预警规则""" try: data = request.get_json() success = get_assistant().alert_system.update_rule(rule_name, **data) if success: return jsonify({"success": True, "message": "规则更新成功"}) else: return jsonify({"success": False, "message": "规则更新失败"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/rules/', methods=['DELETE']) def delete_rule(rule_name): """删除预警规则""" try: success = get_assistant().alert_system.delete_rule(rule_name) if success: return jsonify({"success": True, "message": "规则删除成功"}) else: return jsonify({"success": False, "message": "规则删除失败"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/monitor/start', methods=['POST']) def start_monitoring(): """启动监控服务""" try: success = get_assistant().start_monitoring() if success: return jsonify({"success": True, "message": "监控服务已启动"}) else: return jsonify({"success": False, "message": "启动监控服务失败"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/monitor/stop', methods=['POST']) def stop_monitoring(): """停止监控服务""" try: success = get_assistant().stop_monitoring() if success: return jsonify({"success": True, "message": "监控服务已停止"}) else: return jsonify({"success": False, "message": "停止监控服务失败"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/monitor/status') def get_monitor_status(): """获取监控服务状态""" try: health = get_assistant().get_system_health() return jsonify({ "monitor_status": health.get("monitor_status", "unknown"), "health_score": health.get("health_score", 0), "active_alerts": health.get("active_alerts", 0) }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/check-alerts', methods=['POST']) def check_alerts(): """手动检查预警""" try: alerts = get_assistant().check_alerts() return jsonify({ "success": True, "alerts": alerts, "count": len(alerts) }) except Exception as e: return jsonify({"error": str(e)}), 500 # 实时对话相关路由 @app.route('/api/chat/session', methods=['POST']) def create_chat_session(): """创建对话会话""" try: data = request.get_json() user_id = data.get('user_id', 'anonymous') work_order_id = data.get('work_order_id') session_id = get_chat_manager().create_session(user_id, work_order_id) return jsonify({ "success": True, "session_id": session_id, "message": "会话创建成功" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/message', methods=['POST']) def send_chat_message(): """发送聊天消息""" try: data = request.get_json() session_id = data.get('session_id') message = data.get('message') if not session_id or not message: return jsonify({"error": "缺少必要参数"}), 400 result = get_chat_manager().process_message(session_id, message) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/history/') def get_chat_history(session_id): """获取对话历史""" try: history = get_chat_manager().get_session_history(session_id) return jsonify({ "success": True, "history": history }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/work-order', methods=['POST']) def create_work_order(): """创建工单""" try: data = request.get_json() session_id = data.get('session_id') title = data.get('title') description = data.get('description') category = data.get('category', '技术问题') priority = data.get('priority', 'medium') if not session_id or not title or not description: return jsonify({"error": "缺少必要参数"}), 400 result = get_chat_manager().create_work_order(session_id, title, description, category, priority) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/work-order/') def get_work_order_status(work_order_id): """获取工单状态""" try: result = get_chat_manager().get_work_order_status(work_order_id) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/session/', methods=['DELETE']) def end_chat_session(session_id): """结束对话会话""" try: success = get_chat_manager().end_session(session_id) return jsonify({ "success": success, "message": "会话已结束" if success else "结束会话失败" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/chat/sessions') def get_active_sessions(): """获取活跃会话列表""" try: sessions = chat_manager.get_active_sessions() return jsonify({ "success": True, "sessions": sessions }) except Exception as e: return jsonify({"error": str(e)}), 500 # Agent相关API @app.route('/api/agent/status') def get_agent_status(): """获取Agent状态""" try: status = agent_assistant.get_agent_status() return jsonify(status) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/action-history') def get_agent_action_history(): """获取Agent动作执行历史""" try: limit = request.args.get('limit', 50, type=int) history = agent_assistant.get_action_history(limit) return jsonify({ "success": True, "history": history, "count": len(history) }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/trigger-sample', methods=['POST']) def trigger_sample_action(): """触发示例动作""" try: import asyncio result = asyncio.run(agent_assistant.trigger_sample_actions()) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/clear-history', methods=['POST']) def clear_agent_history(): """清空Agent执行历史""" try: result = agent_assistant.clear_execution_history() return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/llm-stats') def get_llm_stats(): """获取LLM使用统计""" try: stats = agent_assistant.get_llm_usage_stats() return jsonify({ "success": True, "stats": stats }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/toggle', methods=['POST']) def toggle_agent_mode(): """切换Agent模式""" try: data = request.get_json() enabled = data.get('enabled', True) success = agent_assistant.toggle_agent_mode(enabled) return jsonify({ "success": success, "message": f"Agent模式已{'启用' if enabled else '禁用'}" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/monitoring/start', methods=['POST']) def start_agent_monitoring(): """启动Agent监控""" try: success = agent_assistant.start_proactive_monitoring() return jsonify({ "success": success, "message": "Agent监控已启动" if success else "启动失败" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/monitoring/stop', methods=['POST']) def stop_agent_monitoring(): """停止Agent监控""" try: success = agent_assistant.stop_proactive_monitoring() return jsonify({ "success": success, "message": "Agent监控已停止" if success else "停止失败" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/proactive-monitoring', methods=['POST']) def proactive_monitoring(): """主动监控检查""" try: result = agent_assistant.run_proactive_monitoring() return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/intelligent-analysis', methods=['POST']) def intelligent_analysis(): """智能分析""" try: analysis = get_agent_assistant().run_intelligent_analysis() return jsonify({"success": True, "analysis": analysis}) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/chat', methods=['POST']) def agent_chat(): """Agent对话接口""" try: data = request.get_json() message = data.get('message', '') context = data.get('context', {}) if not message: return jsonify({"error": "消息不能为空"}), 400 # 使用Agent助手处理消息 agent_assistant = get_agent_assistant() # 模拟Agent处理(实际应该调用真正的Agent处理逻辑) import asyncio result = asyncio.run(agent_assistant.process_message_agent( message=message, user_id=context.get('user_id', 'admin'), work_order_id=None, enable_proactive=True )) return jsonify({ "success": True, "response": result.get('response', 'Agent已处理您的请求'), "actions": result.get('actions', []), "status": result.get('status', 'completed') }) except Exception as e: return jsonify({"error": str(e)}), 500 # Agent 工具统计与自定义工具 @app.route('/api/agent/tools/stats') def get_agent_tools_stats(): try: tools = agent_assistant.agent_core.tool_manager.get_available_tools() performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() return jsonify({ "success": True, "tools": tools, "performance": performance }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/tools/register', methods=['POST']) def register_custom_tool(): """注册自定义工具(仅登记元数据,函数为占位符)""" try: data = request.get_json() or {} name = data.get('name') description = data.get('description', '') if not name: return jsonify({"error": "缺少工具名称"}), 400 def _placeholder_tool(**kwargs): return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs} agent_assistant.agent_core.tool_manager.register_tool( name, _placeholder_tool, metadata={"description": description, "custom": True} ) return jsonify({"success": True, "message": "工具已注册"}) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/agent/tools/unregister/', methods=['DELETE']) def unregister_custom_tool(name): try: success = agent_assistant.agent_core.tool_manager.unregister_tool(name) return jsonify({"success": success}) except Exception as e: return jsonify({"error": str(e)}), 500 # 分析相关API @app.route('/api/analytics') def get_analytics(): """获取分析数据""" try: # 支持多种参数 time_range = request.args.get('timeRange', request.args.get('days', '30')) dimension = request.args.get('dimension', 'workorders') analytics = generate_db_analytics(int(time_range), dimension) return jsonify(analytics) except Exception as e: return jsonify({"error": str(e)}), 500 def generate_db_analytics(days: int, dimension: str) -> dict: """基于数据库生成真实分析数据(优化版)""" # 使用优化后的查询 return query_optimizer.get_analytics_optimized(days) @app.route('/api/analytics/export') def export_analytics(): """导出分析报告""" try: # 生成Excel报告(使用数据库真实数据) analytics = generate_db_analytics(30, 'workorders') # 创建工作簿 from openpyxl import Workbook from openpyxl.styles import Font 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) from flask import send_file 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/vehicle/data') def get_vehicle_data(): """获取车辆数据""" try: vehicle_id = request.args.get('vehicle_id') vehicle_vin = request.args.get('vehicle_vin') data_type = request.args.get('data_type') limit = request.args.get('limit', 10, type=int) if vehicle_vin: data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) elif vehicle_id: data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit) else: data = vehicle_manager.search_vehicle_data(limit=limit) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/vehicle/data/vin//latest') def get_latest_vehicle_data_by_vin(vehicle_vin): """按VIN获取车辆最新数据""" try: data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/vehicle/data//latest') def get_latest_vehicle_data(vehicle_id): """获取车辆最新数据""" try: data = vehicle_manager.get_latest_vehicle_data(vehicle_id) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/vehicle/data//summary') def get_vehicle_summary(vehicle_id): """获取车辆数据摘要""" try: summary = vehicle_manager.get_vehicle_summary(vehicle_id) return jsonify(summary) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/vehicle/data', methods=['POST']) def add_vehicle_data(): """添加车辆数据""" try: data = request.get_json() success = vehicle_manager.add_vehicle_data( vehicle_id=data['vehicle_id'], data_type=data['data_type'], data_value=data['data_value'], vehicle_vin=data.get('vehicle_vin') ) return jsonify({"success": success, "message": "数据添加成功" if success else "添加失败"}) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/vehicle/init-sample-data', methods=['POST']) def init_sample_vehicle_data(): """初始化示例车辆数据""" try: success = vehicle_manager.add_sample_vehicle_data() return jsonify({"success": success, "message": "示例数据初始化成功" if success else "初始化失败"}) except Exception as e: return jsonify({"error": str(e)}), 500 # API测试相关接口 @app.route('/api/test/connection', methods=['POST']) def test_api_connection(): """测试API连接""" try: data = request.get_json() api_provider = data.get('api_provider', 'openai') api_base_url = data.get('api_base_url', '') api_key = data.get('api_key', '') model_name = data.get('model_name', 'qwen-turbo') # 这里可以调用LLM客户端进行连接测试 # 暂时返回模拟结果 return jsonify({ "success": True, "message": f"API连接测试成功 - {api_provider}", "response_time": "150ms", "model_status": "可用" }) except Exception as e: return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/test/model', methods=['POST']) def test_model_response(): """测试模型回答""" try: data = request.get_json() test_message = data.get('test_message', '你好,请简单介绍一下你自己') # 这里可以调用LLM客户端进行回答测试 # 暂时返回模拟结果 return jsonify({ "success": True, "test_message": test_message, "response": "你好!我是TSP智能助手,基于大语言模型构建的智能客服系统。我可以帮助您解决车辆相关问题,提供技术支持和服务。", "response_time": "1.2s", "tokens_used": 45 }) except Exception as e: return jsonify({"success": False, "error": str(e)}), 500 if __name__ == '__main__': import time app.config['START_TIME'] = time.time() app.config['SERVER_PORT'] = 5000 app.config['WEBSOCKET_PORT'] = 8765 app.run(debug=True, host='0.0.0.0', port=5000)