调整趋势图功能,加入了查询数据库,分析群聊聊天数量的功能

This commit is contained in:
liuwei
2025-03-26 13:41:07 +08:00
parent e83ea9c108
commit 65269e6f12
3 changed files with 185 additions and 5 deletions

View File

@@ -84,4 +84,27 @@ class MessageStorageDB(BaseDBOperator):
ON DUPLICATE KEY UPDATE count = VALUES(count)
"""
params = (group_id, wx_id, date, count)
return self.execute_update(sql, params)
return self.execute_update(sql, params)
def get_message_trend(self, group_id: str, days: int = 7) -> List[Dict]:
"""获取指定群组的消息趋势数据
Args:
group_id: 群组ID
days: 获取最近几天的数据默认7天
Returns:
包含日期和消息数量的列表
"""
sql = """
SELECT
DATE(timestamp) as date,
COUNT(*) as message_count
FROM messages
WHERE group_id = %s
AND timestamp >= DATE_SUB(CURDATE(), INTERVAL %s DAY)
GROUP BY DATE(timestamp)
ORDER BY date
"""
return self.execute_query(sql, (group_id, days)) or []

View File

@@ -1,11 +1,13 @@
import logging
from typing import Dict, Any, Optional
import threading
import time
import os
from datetime import datetime
from flask import Flask, render_template, request, jsonify, redirect, url_for, session, send_from_directory
from db.connection import DBConnectionManager
from db.message_storage import MessageStorageDB
from db.stats_db import StatsDBOperator
from utils.wechat.contact_manager import ContactManager
from robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus
@@ -24,6 +26,7 @@ class DashboardServer:
# 修正:使用单例模式获取数据库连接
self.db_manager = DBConnectionManager.get_instance()
self.stats_db = StatsDBOperator(self.db_manager)
self.message_storage = MessageStorageDB(self.db_manager)
# 获取联系人管理器实例
self.contact_manager = ContactManager.get_instance()
self.app = self._create_app()
@@ -286,6 +289,40 @@ class DashboardServer:
self.logger.info(f"看板主页/api/plugin_trend: {trend}")
return jsonify({"success": True, "data": trend})
@app.route('/api/robot/group/<group_id>/message_trend', methods=['GET'])
def get_group_message_trend(group_id):
"""获取群组消息趋势数据"""
try:
days = request.args.get('days', default=7, type=int)
# 获取消息存储实例
trend_data = self.message_storage.get_message_trend(group_id, days)
# 格式化数据为前端需要的格式
dates = []
counts = []
for item in trend_data:
# 将日期转换为字符串
if isinstance(item['date'], datetime):
date_str = item['date'].strftime('%Y-%m-%d')
else:
date_str = str(item['date'])
dates.append(date_str)
counts.append(item['message_count'])
return jsonify({
'success': True,
'data': {
'dates': dates,
'counts': counts
}
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
})
return app
def run(self) -> None:

View File

@@ -45,7 +45,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<el-table-column label="操作" width="280">
<template slot-scope="scope">
<el-button
size="mini"
@@ -59,6 +59,12 @@
@click="toggleRobotStatus(scope.row)">
{% raw %}{{ scope.row.robot_status === 'enabled' ? '关闭' : '启用' }}{% endraw %}
</el-button>
<el-button
size="mini"
type="info"
@click="viewMessageTrend(scope.row)">
消息趋势
</el-button>
</template>
</el-table-column>
</el-table>
@@ -130,6 +136,27 @@
<el-button type="primary" @click="submitAddGroup">确定</el-button>
</span>
</el-dialog>
<!-- 群组消息趋势图对话框 -->
<el-dialog
:title="currentGroupName + ' 消息趋势'"
:visible.sync="trendDialogVisible"
width="70%">
<div v-if="trendLoading" style="text-align: center; padding: 20px;">
<i class="el-icon-loading"></i>
<p>加载中...</p>
</div>
<div v-else>
<canvas id="messageTrendChart" style="width: 100%; height: 400px;"></canvas>
<div style="margin-top: 20px; text-align: center;">
<el-radio-group v-model="trendDays" @change="loadMessageTrend">
<el-radio-button :label="7">最近7天</el-radio-button>
<el-radio-button :label="14">最近14天</el-radio-button>
<el-radio-button :label="30">最近30天</el-radio-button>
</el-radio-group>
</div>
</div>
</el-dialog>
</div>
{% endblock %}
@@ -157,7 +184,12 @@
{ required: true, message: '请输入群组ID', trigger: 'blur' },
{ pattern: /^\S+$/, message: '群组ID不能包含空格', trigger: 'blur' }
]
}
},
// 趋势图相关数据
trendDialogVisible: false,
trendLoading: false,
trendDays: 7,
trendChart: null
}
},
computed: {
@@ -481,8 +513,96 @@
this.$message.error('批量移除失败: ' + error.message);
});
}).catch(() => {});
},
// 查看消息趋势
viewMessageTrend(group) {
this.currentGroupId = group.group_id;
this.currentGroupName = group.group_name || group.group_id;
this.trendDialogVisible = true;
this.loadMessageTrend();
},
loadMessageTrend() {
this.trendLoading = true;
axios.get(`/api/robot/group/${this.currentGroupId}/message_trend?days=${this.trendDays}`)
.then(response => {
if (response.data.success) {
this.renderTrendChart(response.data.data);
} else {
this.$message.error('加载消息趋势失败');
}
this.trendLoading = false;
})
.catch(error => {
console.error('加载消息趋势失败:', error);
this.$message.error('加载消息趋势失败: ' + error.message);
this.trendLoading = false;
});
},
renderTrendChart(data) {
// 确保DOM元素已经渲染
this.$nextTick(() => {
// 如果已有图表,先销毁
if (this.trendChart) {
this.trendChart.destroy();
}
// 获取canvas元素
const ctx = document.getElementById('messageTrendChart').getContext('2d');
// 创建新图表
this.trendChart = new Chart(ctx, {
type: 'line',
data: {
labels: data.dates,
datasets: [{
label: '消息数量',
data: data.counts,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 2,
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '群聊消息数量趋势'
},
tooltip: {
mode: 'index',
intersect: false
},
legend: {
position: 'top',
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '消息数量'
}
},
x: {
ticks: {
maxRotation: 45,
minRotation: 45
}
}
}
}
});
});
}
}
});
</script>
{% endblock %}
{% endblock %}