From 27b6fbf86f8a5f6d64c9050fbe65a9b7fb7b5c1e Mon Sep 17 00:00:00 2001 From: liuwei Date: Wed, 19 Mar 2025 13:17:48 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"=E7=9B=91=E6=8E=A7=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=BF=AE=E6=94=B9"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 455603de92e29d422bf0e77b15ba509824138cb8. --- db/stats_db.py | 294 ++++++++++++------- plugins/stats_dashboard/dashboard_server.py | 4 - plugins/stats_dashboard/templates/base.html | 15 +- plugins/stats_dashboard/templates/index.html | 107 ++----- 4 files changed, 216 insertions(+), 204 deletions(-) diff --git a/db/stats_db.py b/db/stats_db.py index b1e7a43..9d17703 100644 --- a/db/stats_db.py +++ b/db/stats_db.py @@ -353,120 +353,188 @@ class StatsDBOperator(BaseDBOperator): """ return self.execute_query(sql, (error_id,), fetch_one=True) - def get_dashboard_summary(self, days: int = 7) -> Dict[str, Any]: - """获取仪表盘摘要数据""" - try: - # 计算日期范围 - end_date = datetime.now() - start_date = end_date - timedelta(days=days) + def get_dashboard_summary(self, days: int = 7) -> Dict: + """获取仪表盘摘要数据 + + Args: + days: 统计天数 - # 获取总调用次数 - total_calls_query = """ - SELECT COUNT(*) as total_calls FROM plugin_calls - WHERE created_at >= ? AND created_at <= ? - """ - total_calls_result = self.db_manager.execute_query(total_calls_query, (start_date, end_date)) - total_calls = total_calls_result[0]['total_calls'] if total_calls_result else 0 - - # 获取成功率 - success_rate_query = """ - SELECT - ROUND((SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)), 2) as success_rate - FROM plugin_calls - WHERE created_at >= ? AND created_at <= ? - """ - success_rate_result = self.db_manager.execute_query(success_rate_query, (start_date, end_date)) - success_rate = success_rate_result[0]['success_rate'] if success_rate_result and success_rate_result[0]['success_rate'] is not None else 0 - - # 获取活跃用户数 - active_users_query = """ - SELECT COUNT(DISTINCT user_id) as active_users FROM plugin_calls - WHERE created_at >= ? AND created_at <= ? - """ - active_users_result = self.db_manager.execute_query(active_users_query, (start_date, end_date)) - active_users = active_users_result[0]['active_users'] if active_users_result else 0 - - # 获取活跃群组数 - active_groups_query = """ - SELECT COUNT(DISTINCT group_id) as active_groups FROM plugin_calls - WHERE created_at >= ? AND created_at <= ? AND group_id != '' - """ - active_groups_result = self.db_manager.execute_query(active_groups_query, (start_date, end_date)) - active_groups = active_groups_result[0]['active_groups'] if active_groups_result else 0 - - return { - "total_calls": total_calls, - "success_rate": success_rate, - "active_users": active_users, - "active_groups": active_groups - } - except Exception as e: - logging.error(f"获取仪表盘摘要数据出错: {e}") - return { - "total_calls": 0, - "success_rate": 0, - "active_users": 0, - "active_groups": 0 - } + Returns: + 仪表盘摘要数据 + """ + # 获取时间范围 + end_date = datetime.now() + start_date = end_date - timedelta(days=days) + start_date_str = start_date.strftime("%Y-%m-%d") + + # 1. 总调用次数 + total_calls_sql = """ + SELECT SUM(total_calls) as total_calls + FROM t_plugin_stats + WHERE stat_date >= %s + """ + total_calls_result = self.execute_query(total_calls_sql, (start_date_str,), fetch_one=True) + total_calls = total_calls_result['total_calls'] if total_calls_result and total_calls_result['total_calls'] else 0 + + # 2. 成功率 + success_rate_sql = """ + SELECT SUM(success_calls) as success_calls, SUM(total_calls) as total_calls + FROM t_plugin_stats + WHERE stat_date >= %s + """ + success_rate_result = self.execute_query(success_rate_sql, (start_date_str,), fetch_one=True) + success_rate = 0 + if success_rate_result and success_rate_result['total_calls']: + success_rate = (success_rate_result['success_calls'] / success_rate_result['total_calls']) * 100 + + # 3. 活跃用户数 + active_users_sql = """ + SELECT COUNT(DISTINCT user_id) as active_users + FROM t_user_stats + WHERE last_used_at >= %s + """ + active_users_result = self.execute_query(active_users_sql, (start_date_str,), fetch_one=True) + active_users = active_users_result['active_users'] if active_users_result else 0 + + # 4. 活跃群组数 + active_groups_sql = """ + SELECT COUNT(DISTINCT group_id) as active_groups + FROM t_group_stats + WHERE last_used_at >= %s + """ + active_groups_result = self.execute_query(active_groups_sql, (start_date_str,), fetch_one=True) + active_groups = active_groups_result['active_groups'] if active_groups_result else 0 + + # 5. 错误数量 + error_count_sql = """ + SELECT COUNT(*) as error_count + FROM t_error_logs + WHERE created_at >= %s + """ + error_count_result = self.execute_query(error_count_sql, (start_date_str,), fetch_one=True) + error_count = error_count_result['error_count'] if error_count_result else 0 + + # 6. 平均响应时间 + avg_response_time_sql = """ + SELECT AVG(avg_process_time) as avg_response_time + FROM t_plugin_stats + WHERE stat_date >= %s + """ + avg_response_time_result = self.execute_query(avg_response_time_sql, (start_date_str,), fetch_one=True) + avg_response_time = avg_response_time_result['avg_response_time'] if avg_response_time_result and avg_response_time_result['avg_response_time'] else 0 + + # 7. 最常用的插件 + top_plugins_sql = """ + SELECT plugin_name, SUM(total_calls) as total_calls + FROM t_plugin_stats + WHERE stat_date >= %s + GROUP BY plugin_name + ORDER BY total_calls DESC + LIMIT 5 + """ + top_plugins = self.execute_query(top_plugins_sql, (start_date_str,)) or [] + + # 8. 最活跃的用户 + top_users_sql = """ + SELECT user_id, SUM(total_calls) as total_calls + FROM t_user_stats + WHERE last_used_at >= %s + GROUP BY user_id + ORDER BY total_calls DESC + LIMIT 5 + """ + top_users = self.execute_query(top_users_sql, (start_date_str,)) or [] + + # 9. 最活跃的群组 + top_groups_sql = """ + SELECT group_id, SUM(total_calls) as total_calls + FROM t_group_stats + WHERE last_used_at >= %s + GROUP BY group_id + ORDER BY total_calls DESC + LIMIT 5 + """ + top_groups = self.execute_query(top_groups_sql, (start_date_str,)) or [] + + # 返回汇总数据 + return { + "total_calls": total_calls, + "success_rate": success_rate, + "active_users": active_users, + "active_groups": active_groups, + "error_count": error_count, + "avg_response_time": avg_response_time, + "top_plugins": top_plugins, + "top_users": top_users, + "top_groups": top_groups + } - def get_plugin_trend(self, plugin_name: str = '', days: int = 7) -> List[Dict[str, Any]]: - """获取插件调用趋势数据""" - try: - # 计算日期范围 - end_date = datetime.now() - start_date = end_date - timedelta(days=days) + def get_plugin_trend(self, plugin_name: str = "", days: int = 7) -> List[Dict]: + """获取插件使用趋势数据 + + Args: + plugin_name: 插件名称,为空则获取所有插件 + days: 统计天数 - # 构建查询条件 - params = [start_date, end_date] - plugin_condition = "" - if plugin_name: - plugin_condition = "AND plugin_name = ?" - params.append(plugin_name) - - # 按日期分组查询 - query = f""" - SELECT - date(created_at) as date, - COUNT(*) as call_count, - SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as success_count - FROM plugin_calls - WHERE created_at >= ? AND created_at <= ? {plugin_condition} - GROUP BY date(created_at) - ORDER BY date + Returns: + 插件使用趋势数据 + """ + # 获取时间范围内的每一天 + end_date = datetime.now().date() + start_date = end_date - timedelta(days=days-1) # 包含今天,所以减1 + + if plugin_name: + # 获取特定插件的趋势 + sql = """ + SELECT stat_date, SUM(total_calls) as total_calls, + SUM(success_calls) as success_calls, + SUM(failed_calls) as failed_calls + FROM t_plugin_stats + WHERE plugin_name = %s AND stat_date >= %s + GROUP BY stat_date + ORDER BY stat_date """ - - results = self.db_manager.execute_query(query, tuple(params)) - - # 确保每一天都有数据 - date_format = "%Y-%m-%d" - trend_data = [] - - # 创建日期范围内的所有日期 - current_date = start_date - while current_date <= end_date: - date_str = current_date.strftime(date_format) - - # 查找当前日期的数据 - day_data = next((item for item in results if item['date'] == date_str), None) - - if day_data: - trend_data.append({ - "date": date_str, - "call_count": day_data['call_count'], - "success_count": day_data['success_count'], - "success_rate": round(day_data['success_count'] * 100 / day_data['call_count'], 2) if day_data['call_count'] > 0 else 0 - }) - else: - trend_data.append({ - "date": date_str, - "call_count": 0, - "success_count": 0, - "success_rate": 0 - }) - - current_date += timedelta(days=1) - - return trend_data - except Exception as e: - logging.error(f"获取插件趋势数据出错: {e}") - return [] \ No newline at end of file + params = (plugin_name, start_date) + else: + # 获取所有插件的趋势 + sql = """ + SELECT stat_date, SUM(total_calls) as total_calls, + SUM(success_calls) as success_calls, + SUM(failed_calls) as failed_calls + FROM t_plugin_stats + WHERE stat_date >= %s + GROUP BY stat_date + ORDER BY stat_date + """ + params = (start_date,) + + results = self.execute_query(sql, params) or [] + + # 将结果转换为按日期的字典 + trend_by_date = {r['stat_date'].strftime('%Y-%m-%d'): r for r in results} + + # 确保每一天都有数据 + trend_data = [] + current_date = start_date + while current_date <= end_date: + date_str = current_date.strftime('%Y-%m-%d') + if date_str in trend_by_date: + data = trend_by_date[date_str] + trend_data.append({ + 'date': date_str, + 'total_calls': data['total_calls'], + 'success_calls': data['success_calls'], + 'failed_calls': data['failed_calls'], + 'success_rate': (data['success_calls'] / data['total_calls'] * 100) if data['total_calls'] > 0 else 0 + }) + else: + trend_data.append({ + 'date': date_str, + 'total_calls': 0, + 'success_calls': 0, + 'failed_calls': 0, + 'success_rate': 0 + }) + current_date += timedelta(days=1) + + return trend_data \ No newline at end of file diff --git a/plugins/stats_dashboard/dashboard_server.py b/plugins/stats_dashboard/dashboard_server.py index 0a82a30..0248c50 100644 --- a/plugins/stats_dashboard/dashboard_server.py +++ b/plugins/stats_dashboard/dashboard_server.py @@ -112,10 +112,6 @@ class DashboardServer: trend = self.stats_db.get_plugin_trend(plugin_name, days) return jsonify({"success": True, "data": trend}) - @app.route('/errors') - def errors(): - return render_template('errors.html') - return app def run(self) -> None: diff --git a/plugins/stats_dashboard/templates/base.html b/plugins/stats_dashboard/templates/base.html index 43041b5..da9a0cf 100644 --- a/plugins/stats_dashboard/templates/base.html +++ b/plugins/stats_dashboard/templates/base.html @@ -82,24 +82,25 @@ background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" - @select="handleSelect"> - + @select="handleSelect" + router> + 首页概览 - - + + 插件统计 - + 用户统计 - + 群组统计 - + 错误日志 diff --git a/plugins/stats_dashboard/templates/index.html b/plugins/stats_dashboard/templates/index.html index 283d691..dd11f5d 100644 --- a/plugins/stats_dashboard/templates/index.html +++ b/plugins/stats_dashboard/templates/index.html @@ -232,102 +232,49 @@ } // 准备数据 - const dates = trendData.map(item => item.date); - const callCounts = trendData.map(item => item.call_count); - const successRates = trendData.map(item => item.success_rate); + const labels = trendData.map(item => item.stat_date); + const totalData = trendData.map(item => item.total_calls); + const successData = trendData.map(item => item.success_calls); + const errorData = trendData.map(item => item.error_calls); - // 创建图表 + // 创建新图表 this.charts.trendChart = new Chart(ctx, { type: 'line', data: { - labels: dates, + labels: labels, datasets: [ { - label: '调用次数', - data: callCounts, - backgroundColor: 'rgba(54, 162, 235, 0.2)', + label: '总调用', + data: totalData, + fill: false, + backgroundColor: 'rgba(54, 162, 235, 0.6)', borderColor: 'rgba(54, 162, 235, 1)', - borderWidth: 1, - yAxisID: 'y-axis-1' + tension: 0.1 }, { - label: '成功率(%)', - data: successRates, - backgroundColor: 'rgba(255, 99, 132, 0.2)', + label: '成功调用', + data: successData, + fill: false, + backgroundColor: 'rgba(75, 192, 192, 0.6)', + borderColor: 'rgba(75, 192, 192, 1)', + tension: 0.1 + }, + { + label: '失败调用', + data: errorData, + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.6)', borderColor: 'rgba(255, 99, 132, 1)', - borderWidth: 1, - yAxisID: 'y-axis-2' + tension: 0.1 } ] }, options: { responsive: true, scales: { - yAxes: [ - { - id: 'y-axis-1', - type: 'linear', - position: 'left', - ticks: { - beginAtZero: true - }, - scaleLabel: { - display: true, - labelString: '调用次数' - } - }, - { - id: 'y-axis-2', - type: 'linear', - position: 'right', - ticks: { - beginAtZero: true, - max: 100 - }, - scaleLabel: { - display: true, - labelString: '成功率(%)' - } - } - ] - } - } - }); - }, - renderSuccessRateChart() { - const ctx = document.getElementById('successRateChart').getContext('2d'); - - // 销毁旧图表 - if (this.charts.successRateChart) { - this.charts.successRateChart.destroy(); - } - - // 准备数据 - const plugins = this.pluginStats.map(item => item.plugin_name); - const successRates = this.pluginStats.map(item => item.success_rate); - - // 创建图表 - this.charts.successRateChart = new Chart(ctx, { - type: 'bar', - data: { - labels: plugins, - datasets: [{ - label: '成功率(%)', - data: successRates, - backgroundColor: 'rgba(75, 192, 192, 0.2)', - borderColor: 'rgba(75, 192, 192, 1)', - borderWidth: 1 - }] - }, - options: { - responsive: true, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true, - max: 100 - } - }] + y: { + beginAtZero: true + } } } });