新增消息趋势分析
This commit is contained in:
@@ -92,3 +92,39 @@ def get_groups():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取群组列表失败: {e}")
|
logger.error(f"获取群组列表失败: {e}")
|
||||||
return jsonify({'error': str(e)}), 500
|
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
|
||||||
@@ -143,6 +143,29 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 添加一个新行用于显示按小时聊天趋势图 -->
|
||||||
|
<el-row :gutter="20" style="margin-top: 20px;">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card shadow="hover" v-loading="hourlyMessageTrendLoading">
|
||||||
|
<div slot="header" style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<span>按小时聊天趋势</span>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<el-select v-model="selectedGroupForHourlyTrend" placeholder="选择群组" style="width: 200px; margin-right: 10px;" @change="loadHourlyMessageTrend">
|
||||||
|
<el-option v-for="group in groups" :key="group.group_id" :label="group.group_name" :value="group.group_id"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="hourlyTrendDays" placeholder="选择时间范围" style="width: 120px;" @change="loadHourlyMessageTrend">
|
||||||
|
<el-option :value="1" label="最近1天"></el-option>
|
||||||
|
<el-option :value="2" label="最近2天"></el-option>
|
||||||
|
<el-option :value="3" label="最近3天"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="hourlyMessageTrendChart" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
<!-- 系统状态卡片移到顶部 -->
|
<!-- 系统状态卡片移到顶部 -->
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@@ -261,6 +284,23 @@
|
|||||||
disk_usage: 0,
|
disk_usage: 0,
|
||||||
uptime: 0,
|
uptime: 0,
|
||||||
timestamp: '-'
|
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.loadSystemInfo();
|
||||||
// 加载当前用户信息
|
// 加载当前用户信息
|
||||||
this.loadCurrentUserInfo();
|
this.loadCurrentUserInfo();
|
||||||
|
this.loadGroups(); // 添加这一行
|
||||||
// 设置定时刷新系统信息(每30秒)
|
// 设置定时刷新系统信息(每30秒)
|
||||||
this.systemInfoTimer = setInterval(this.loadSystemInfo, 30000);
|
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 || '未知群组'} 的聊天趋势`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -208,3 +208,37 @@ class MessageStorageDB(BaseDBOperator):
|
|||||||
# 使用已有的日志记录方式
|
# 使用已有的日志记录方式
|
||||||
print(f"更新消息图片路径出错: {e}")
|
print(f"更新消息图片路径出错: {e}")
|
||||||
return False
|
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 []
|
||||||
|
|||||||
Reference in New Issue
Block a user