调整趋势图功能,加入了查询数据库,分析群聊聊天数量的功能
This commit is contained in:
@@ -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 []
|
||||
@@ -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:
|
||||
|
||||
@@ -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 %}
|
||||
Reference in New Issue
Block a user