diff --git a/admin/dashboard/blueprints/messages.py b/admin/dashboard/blueprints/messages.py index 601ed85..94d5651 100644 --- a/admin/dashboard/blueprints/messages.py +++ b/admin/dashboard/blueprints/messages.py @@ -92,3 +92,39 @@ def get_groups(): except Exception as e: logger.error(f"获取群组列表失败: {e}") return jsonify({'error': str(e)}), 500 + + +@messages_bp.route('/api/hourly_message_trend', methods=['GET']) +@login_required +def get_hourly_message_trend(): + """获取按小时聊天趋势数据API""" + try: + server = current_app.dashboard_server + # 获取查询参数 + group_id = request.args.get('group_id') + days = int(request.args.get('days', 1)) + + # 调用数据库方法获取按小时消息趋势数据 + trend_data = server.message_storage.get_hourly_message_trend(group_id=group_id, days=days) + + # 格式化数据为前端需要的格式 + hours = [] + counts = [] + for item in trend_data: + hours.append(item['hour_slot']) + counts.append(item['message_count']) + + # 获取群组名称 + group_name = server.contact_manager.get_nickname(group_id) if group_id else "所有群组" + + return jsonify({ + 'success': True, + 'data': { + 'hours': hours, + 'counts': counts, + 'group_name': group_name + } + }) + except Exception as e: + logger.error(f"获取按小时聊天趋势数据失败: {e}") + return jsonify({'success': False, 'error': str(e)}), 500 \ No newline at end of file diff --git a/admin/dashboard/templates/index.html b/admin/dashboard/templates/index.html index 83e8b01..623f1a7 100644 --- a/admin/dashboard/templates/index.html +++ b/admin/dashboard/templates/index.html @@ -143,6 +143,29 @@ + + + + +
+ 按小时聊天趋势 +
+ + + + + + + + +
+
+
+ +
+
+
+
@@ -261,6 +284,23 @@ disk_usage: 0, uptime: 0, timestamp: '-' + }, + groups: [], + selectedGroupForHourlyTrend: '', + hourlyTrendDays: 1, + hourlyMessageTrendData: { + hours: [], + counts: [] + }, + hourlyMessageTrendLoading: true, + charts: { + pluginChart: null, + successRateChart: null, + trendChart: null, + cpuChart: null, + memoryChart: null, + diskChart: null, + hourlyMessageTrendChart: null // 添加这一行 } } }, @@ -287,6 +327,7 @@ this.loadSystemInfo(); // 加载当前用户信息 this.loadCurrentUserInfo(); + this.loadGroups(); // 添加这一行 // 设置定时刷新系统信息(每30秒) this.systemInfoTimer = setInterval(this.loadSystemInfo, 30000); }, @@ -598,6 +639,87 @@ } } }); + }, + loadGroups() { + axios.get('/api/groups') + .then(response => { + if (response.data && response.data.groups) { + this.groups = response.data.groups; + if (this.groups.length > 0) { + this.selectedGroupForHourlyTrend = this.groups[0].group_id; + this.loadHourlyMessageTrend(); + } + } + }) + .catch(error => { + console.error('加载群组列表失败:', error); + this.$message.error('加载群组列表失败'); + }); + }, + loadHourlyMessageTrend() { + if (!this.selectedGroupForHourlyTrend) return; + + this.hourlyMessageTrendLoading = true; + axios.get('/api/hourly_message_trend', { + params: { + group_id: this.selectedGroupForHourlyTrend, + days: this.hourlyTrendDays + } + }) + .then(response => { + if (response.data && response.data.success) { + this.hourlyMessageTrendData = response.data.data; + this.$nextTick(() => { + this.renderHourlyMessageTrendChart(); + }); + } else { + this.$message.error(response.data.error || '加载按小时聊天趋势数据失败'); + } + this.hourlyMessageTrendLoading = false; + }) + .catch(error => { + console.error('加载按小时聊天趋势数据失败:', error); + this.$message.error('加载按小时聊天趋势数据失败'); + this.hourlyMessageTrendLoading = false; + }); + }, + renderHourlyMessageTrendChart() { + const ctx = document.getElementById('hourlyMessageTrendChart').getContext('2d'); + + // 销毁旧图表 + if (this.charts.hourlyMessageTrendChart) { + this.charts.hourlyMessageTrendChart.destroy(); + } + + // 创建新图表 + this.charts.hourlyMessageTrendChart = new Chart(ctx, { + type: 'line', + data: { + labels: this.hourlyMessageTrendData.hours, + datasets: [{ + label: '消息数量', + data: this.hourlyMessageTrendData.counts, + fill: false, + backgroundColor: 'rgba(255, 99, 132, 0.6)', + borderColor: 'rgba(255, 99, 132, 1)', + tension: 0.1 + }] + }, + options: { + responsive: true, + scales: { + y: { + beginAtZero: true + } + }, + plugins: { + title: { + display: true, + text: `${this.hourlyMessageTrendData.group_name || '未知群组'} 的聊天趋势` + } + } + } + }); } } }); diff --git a/db/message_storage.py b/db/message_storage.py index 1497f9a..9cd8723 100644 --- a/db/message_storage.py +++ b/db/message_storage.py @@ -208,3 +208,37 @@ class MessageStorageDB(BaseDBOperator): # 使用已有的日志记录方式 print(f"更新消息图片路径出错: {e}") return False + + + def get_hourly_message_trend(self, group_id: str = None, days: int = 1) -> List[Dict]: + """获取指定群组的按小时消息趋势数据 + + Args: + group_id: 群组ID,如果为None则获取所有群组的数据 + days: 获取最近几天的数据,默认1天 + + Returns: + 包含小时和消息数量的列表 + """ + sql = """ + SELECT + DATE_FORMAT(timestamp, '%Y-%m-%d %H:00') as hour_slot, + COUNT(*) as message_count + FROM messages + WHERE timestamp >= DATE_SUB(NOW(), INTERVAL %s DAY) + """ + + params = [days] + + # 如果指定了群组ID,则添加群组筛选条件 + if group_id: + sql += "AND group_id = %s " + params.append(group_id) + + # 按小时分组并排序 + sql += """ + GROUP BY hour_slot + ORDER BY hour_slot + """ + + return self.execute_query(sql, tuple(params)) or []