From 3984cffe23c0f479278a018085669244e7cf06e7 Mon Sep 17 00:00:00 2001 From: Jeason <1710884619@qq.com> Date: Tue, 7 Apr 2026 13:12:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=95=B0=E6=8D=AE=E5=88=86=E6=9E=90?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=BF=AE=E5=A4=8D=20=20=E6=B7=BB=E5=8A=A0=20?= =?UTF-8?q?API=20=E7=AB=AF=E7=82=B9=20+=20=E7=A7=9F=E6=88=B7=E7=BB=B4?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增 GET /api/analytics 端点(之前不存在,前端一直请求 404) 2. query_optimizer.get_analytics_optimized 支持 tenant_id 参数 3. 工单/预警/对话查询按 tenant_id 过滤 4. 数据分析控制面板新增租户筛选下拉框 5. updateCharts 传递 tenant_id 参数 6. populateTenantSelectors 填充分析页租户筛选器 --- data/tsp_assistant.db | Bin 217088 -> 151552 bytes src/core/query_optimizer.py | 28 ++++++++----- src/integrations/feishu_longconn_service.py | 11 ++--- src/integrations/flexible_field_mapper.py | 6 +-- src/web/blueprints/analytics.py | 42 +++++++++++++------- src/web/static/js/dashboard.js | 6 +++ src/web/static/js/modules/system.js | 6 ++- src/web/templates/dashboard.html | 18 +++++---- 8 files changed, 71 insertions(+), 46 deletions(-) diff --git a/data/tsp_assistant.db b/data/tsp_assistant.db index 5d476b180b81e97b907b51ee55bcdcb9aa5e23f0..bd0e77a400affbf43be8a454ed48ed39176a2e6a 100644 GIT binary patch delta 1753 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 Dict[str, Any]: - """优化版分析数据查询""" + def get_analytics_optimized(self, days: int = 30, tenant_id: str = None) -> Dict[str, Any]: + """优化版分析数据查询(支持按租户筛选)""" start_time = time.time() try: with db_manager.get_session() as session: from datetime import datetime, timedelta - end_time = datetime.now() - start_time_query = end_time - timedelta(days=days-1) + # 查询工单 + wo_query = session.query(WorkOrder) + if tenant_id: + wo_query = wo_query.filter(WorkOrder.tenant_id == tenant_id) + workorders = wo_query.all() - # 批量查询所有需要的数据 - # 修改:查询所有工单,不限制时间范围 - workorders = session.query(WorkOrder).all() + # 查询预警 + alert_query = session.query(Alert) + if tenant_id: + alert_query = alert_query.filter(Alert.tenant_id == tenant_id) + alerts = alert_query.all() - # 修改:查询所有预警和对话,不限制时间范围 - alerts = session.query(Alert).all() - - conversations = session.query(Conversation).all() + # 查询对话 + conv_query = session.query(Conversation) + if tenant_id: + conv_query = conv_query.filter(Conversation.tenant_id == tenant_id) + conversations = conv_query.all() # 处理数据 analytics = self._process_analytics_data(workorders, alerts, conversations, days) diff --git a/src/integrations/feishu_longconn_service.py b/src/integrations/feishu_longconn_service.py index 6f7d1c7..e9ee7dc 100644 --- a/src/integrations/feishu_longconn_service.py +++ b/src/integrations/feishu_longconn_service.py @@ -294,28 +294,25 @@ class FeishuLongConnService: 这个方法会阻塞当前线程,持续监听飞书事件 """ logger.info("=" * 80) - logger.info("🚀 启动飞书长连接客户端") + logger.info(" 启动飞书长连接客户端") logger.info("=" * 80) logger.info(f"📋 配置信息:") logger.info(f" - App ID: {self.app_id}") - logger.info(f" - 模式: 事件订阅 2.0(长连接)") - logger.info(f" - 优势: 无需公网域名和 webhook 配置") - logger.info("=" * 80) - logger.info("💡 等待消息中... (按 Ctrl+C 停止)") + logger.info(" 等待消息中... (按 Ctrl+C 停止)") logger.info("=" * 80) try: # 创建长连接客户端 cli = lark.ws.Client(self.app_id, self.app_secret, event_handler=self.event_handler) - logger.info("🔌 正在建立与飞书服务器的连接...") + logger.info("正在建立与飞书服务器的连接...") # 启动长连接(会阻塞) cli.start() except KeyboardInterrupt: logger.info("") - logger.info("⏹️ 用户中断,停止飞书长连接客户端") + logger.info("用户中断,停止飞书长连接客户端") except Exception as e: logger.error(f"飞书长连接客户端异常: {e}", exc_info=True) raise diff --git a/src/integrations/flexible_field_mapper.py b/src/integrations/flexible_field_mapper.py index 4729e9b..242d553 100644 --- a/src/integrations/flexible_field_mapper.py +++ b/src/integrations/flexible_field_mapper.py @@ -341,7 +341,6 @@ class FlexibleFieldMapper: # 保存配置 self._save_current_config() - logger.info(f"添加字段映射: {feishu_field} -> {local_field}") return True except Exception as e: @@ -431,7 +430,6 @@ class FlexibleFieldMapper: 'mapped': True, 'value': value } - logger.debug(f"映射字段 {feishu_field} -> {local_field}: {str(value)[:50]}") else: conversion_stats['unmapped_fields'].append(feishu_field) conversion_stats['mapping_details'][feishu_field] = { @@ -439,9 +437,7 @@ class FlexibleFieldMapper: 'value': value, 'suggestions': self._suggest_mapping(feishu_field) } - logger.info(f"飞书字段 {feishu_field} 不存在于数据中") - logger.debug(f"字段转换完成: 已映射 {conversion_stats['mapped_fields']}, " - f"未映射 {len(conversion_stats['unmapped_fields'])}") + logger.debug(f"未映射 {len(conversion_stats['unmapped_fields'])}") return local_data, conversion_stats diff --git a/src/web/blueprints/analytics.py b/src/web/blueprints/analytics.py index 3e0447f..95739e0 100644 --- a/src/web/blueprints/analytics.py +++ b/src/web/blueprints/analytics.py @@ -4,49 +4,63 @@ 处理数据分析、报告生成等功能 """ -from flask import Blueprint, request, jsonify, send_file import os +import logging +from flask import Blueprint, request, jsonify, send_file +from src.core.query_optimizer import query_optimizer + +logger = logging.getLogger(__name__) analytics_bp = Blueprint('analytics', __name__, url_prefix='/api/analytics') +@analytics_bp.route('') +def get_analytics(): + """获取分析数据(支持租户筛选和时间范围)""" + try: + time_range = request.args.get('timeRange', '30') + tenant_id = request.args.get('tenant_id') + + try: + days = int(time_range) + except (ValueError, TypeError): + days = 30 + + analytics = query_optimizer.get_analytics_optimized(days, tenant_id=tenant_id) + return jsonify(analytics) + except Exception as e: + logger.error(f"获取分析数据失败: {e}") + return jsonify({"error": str(e)}), 500 + + @analytics_bp.route('/export') def export_analytics(): """导出分析报告""" try: - from src.web.service_manager import service_manager - from src.core.query_optimizer import query_optimizer from openpyxl import Workbook from openpyxl.styles import Font - # 生成Excel报告(使用数据库真实数据) analytics = query_optimizer.get_analytics_optimized(30) - # 创建工作簿 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['B4'] = analytics.get('workorders', {}).get('total', 0) ws['A5'] = '待处理' - ws['B5'] = analytics['workorders']['open'] + ws['B5'] = analytics.get('workorders', {}).get('open', 0) ws['A6'] = '已解决' - ws['B6'] = analytics['workorders']['resolved'] + ws['B6'] = analytics.get('workorders', {}).get('resolved', 0) - # 保存文件 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: + logger.error(f"导出分析报告失败: {e}") return jsonify({"error": str(e)}), 500 diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index d323abf..07ad26e 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -165,6 +165,12 @@ class TSPDashboard { if (woCreate) { woCreate.innerHTML = tenants.map(t => ``).join(''); } + // 数据分析租户筛选器 + const analyticsFilter = document.getElementById('analytics-tenant-filter'); + if (analyticsFilter) { + const cv = analyticsFilter.value; + analyticsFilter.innerHTML = '' + tenants.map(t => ``).join(''); + } } catch (e) { console.warn('加载租户列表失败:', e); } } diff --git a/src/web/static/js/modules/system.js b/src/web/static/js/modules/system.js index 3692548..35b16df 100644 --- a/src/web/static/js/modules/system.js +++ b/src/web/static/js/modules/system.js @@ -392,9 +392,11 @@ Object.assign(TSPDashboard.prototype, { const timeRange = document.getElementById('timeRange').value; const chartType = document.getElementById('chartType').value; const dataDimension = document.getElementById('dataDimension').value; + const tenantId = document.getElementById('analytics-tenant-filter')?.value || ''; - // 获取数据 - const response = await fetch(`/api/analytics?timeRange=${timeRange}&dimension=${dataDimension}`); + let url = `/api/analytics?timeRange=${timeRange}&dimension=${dataDimension}`; + if (tenantId) url += `&tenant_id=${encodeURIComponent(tenantId)}`; + const response = await fetch(url); const data = await response.json(); // 更新统计卡片 diff --git a/src/web/templates/dashboard.html b/src/web/templates/dashboard.html index d1df5b1..e30bdfa 100644 --- a/src/web/templates/dashboard.html +++ b/src/web/templates/dashboard.html @@ -1856,7 +1856,13 @@
-
+
+ + +
+
-
+
-
+
-
+