mirror of https://github.com/Mai-with-u/MaiBot.git
better:美化统计界面
parent
837ecf702b
commit
fd19170543
|
|
@ -2,6 +2,7 @@ data/
|
||||||
data1/
|
data1/
|
||||||
mongodb/
|
mongodb/
|
||||||
NapCat.Framework.Windows.Once/
|
NapCat.Framework.Windows.Once/
|
||||||
|
NapCat.Framework.Windows.OneKey/
|
||||||
log/
|
log/
|
||||||
logs/
|
logs/
|
||||||
out/
|
out/
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ class HeartFChatting:
|
||||||
asyncio.create_task(extract_and_store_jargon(self.stream_id))
|
asyncio.create_task(extract_and_store_jargon(self.stream_id))
|
||||||
# 添加聊天内容概括任务 - 累积、打包和压缩聊天记录
|
# 添加聊天内容概括任务 - 累积、打包和压缩聊天记录
|
||||||
# 注意:后台循环已在start()中启动,这里作为额外触发点,在有思考时立即处理
|
# 注意:后台循环已在start()中启动,这里作为额外触发点,在有思考时立即处理
|
||||||
asyncio.create_task(self.chat_history_summarizer.process())
|
# asyncio.create_task(self.chat_history_summarizer.process())
|
||||||
|
|
||||||
|
|
||||||
cycle_timers, thinking_id = self.start_cycle()
|
cycle_timers, thinking_id = self.start_cycle()
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,6 @@ class ChatHistorySummarizer:
|
||||||
|
|
||||||
if not new_messages:
|
if not new_messages:
|
||||||
# 没有新消息,检查是否需要打包
|
# 没有新消息,检查是否需要打包
|
||||||
logger.info(f"{self.log_prefix} 无新增消息,尝试对现有批次执行打包检查")
|
|
||||||
if self.current_batch and self.current_batch.messages:
|
if self.current_batch and self.current_batch.messages:
|
||||||
await self._check_and_package(current_time)
|
await self._check_and_package(current_time)
|
||||||
self.last_check_time = current_time
|
self.last_check_time = current_time
|
||||||
|
|
@ -113,17 +112,13 @@ class ChatHistorySummarizer:
|
||||||
# 有新消息,更新最后检查时间
|
# 有新消息,更新最后检查时间
|
||||||
self.last_check_time = current_time
|
self.last_check_time = current_time
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f"{self.log_prefix} 获取到 {len(new_messages)} 条新消息,最新消息时间戳: {new_messages[-1].time if new_messages else 'N/A'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 如果有当前批次,添加新消息
|
# 如果有当前批次,添加新消息
|
||||||
if self.current_batch:
|
if self.current_batch:
|
||||||
before_count = len(self.current_batch.messages)
|
before_count = len(self.current_batch.messages)
|
||||||
self.current_batch.messages.extend(new_messages)
|
self.current_batch.messages.extend(new_messages)
|
||||||
self.current_batch.end_time = current_time
|
self.current_batch.end_time = current_time
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 扩展现有批次: {before_count} -> {len(self.current_batch.messages)} 条消息,时间范围: {self.current_batch.start_time:.2f} - {self.current_batch.end_time:.2f}"
|
f"{self.log_prefix} 批次更新: {before_count} -> {len(self.current_batch.messages)} 条消息"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 创建新批次
|
# 创建新批次
|
||||||
|
|
@ -133,7 +128,7 @@ class ChatHistorySummarizer:
|
||||||
end_time=current_time,
|
end_time=current_time,
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 创建新批次: 消息数 {len(new_messages)},时间范围: {self.current_batch.start_time:.2f} - {self.current_batch.end_time:.2f}"
|
f"{self.log_prefix} 新建批次: {len(new_messages)} 条消息"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 检查是否需要打包
|
# 检查是否需要打包
|
||||||
|
|
|
||||||
|
|
@ -820,48 +820,70 @@ class StatisticOutputTask(AsyncTask):
|
||||||
<strong>统计时段: </strong>
|
<strong>统计时段: </strong>
|
||||||
{start_time.strftime("%Y-%m-%d %H:%M:%S")} ~ {now.strftime("%Y-%m-%d %H:%M:%S")}
|
{start_time.strftime("%Y-%m-%d %H:%M:%S")} ~ {now.strftime("%Y-%m-%d %H:%M:%S")}
|
||||||
</p>
|
</p>
|
||||||
<p class=\"info-item\"><strong>总在线时间: </strong>{_format_online_time(stat_data[ONLINE_TIME])}</p>
|
<div class=\"kpi-cards\">
|
||||||
<p class=\"info-item\"><strong>总消息数: </strong>{stat_data[TOTAL_MSG_CNT]}</p>
|
<div class=\"kpi-card\">
|
||||||
<p class=\"info-item\"><strong>总请求数: </strong>{stat_data[TOTAL_REQ_CNT]}</p>
|
<div class=\"kpi-title\">总在线时间</div>
|
||||||
<p class=\"info-item\"><strong>总花费: </strong>{stat_data[TOTAL_COST]:.2f} ¥</p>
|
<div class=\"kpi-value\">{_format_online_time(stat_data[ONLINE_TIME])}</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"kpi-card\">
|
||||||
|
<div class=\"kpi-title\">总消息数</div>
|
||||||
|
<div class=\"kpi-value\">{stat_data[TOTAL_MSG_CNT]}</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"kpi-card\">
|
||||||
|
<div class=\"kpi-title\">总请求数</div>
|
||||||
|
<div class=\"kpi-value\">{stat_data[TOTAL_REQ_CNT]}</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"kpi-card\">
|
||||||
|
<div class=\"kpi-title\">总花费</div>
|
||||||
|
<div class=\"kpi-value\">{stat_data[TOTAL_COST]:.2f} ¥</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>按模型分类统计</h2>
|
<h2>按模型分类统计</h2>
|
||||||
<table>
|
<div class=\"table-wrap\">
|
||||||
<thead><tr><th>模型名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr></thead>
|
<table>
|
||||||
<tbody>
|
<thead><tr><th>模型名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr></thead>
|
||||||
{model_rows}
|
<tbody>
|
||||||
</tbody>
|
{model_rows}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>按模块分类统计</h2>
|
<h2>按模块分类统计</h2>
|
||||||
<table>
|
<div class=\"table-wrap\">
|
||||||
<thead>
|
<table>
|
||||||
<tr><th>模块名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr>
|
<thead>
|
||||||
</thead>
|
<tr><th>模块名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{module_rows}
|
<tbody>
|
||||||
</tbody>
|
{module_rows}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>按请求类型分类统计</h2>
|
<h2>按请求类型分类统计</h2>
|
||||||
<table>
|
<div class=\"table-wrap\">
|
||||||
<thead>
|
<table>
|
||||||
<tr><th>请求类型</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr>
|
<thead>
|
||||||
</thead>
|
<tr><th>请求类型</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th><th>平均耗时(秒)</th><th>标准差(秒)</th></tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{type_rows}
|
<tbody>
|
||||||
</tbody>
|
{type_rows}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>聊天消息统计</h2>
|
<h2>聊天消息统计</h2>
|
||||||
<table>
|
<div class=\"table-wrap\">
|
||||||
<thead>
|
<table>
|
||||||
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
|
<thead>
|
||||||
</thead>
|
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{chat_rows_html}
|
<tbody>
|
||||||
</tbody>
|
{chat_rows_html}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>数据分布图表</h2>
|
<h2>数据分布图表</h2>
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 20px; margin-top: 20px;">
|
<div style="display: flex; flex-wrap: wrap; gap: 20px; margin-top: 20px;">
|
||||||
|
|
@ -1085,21 +1107,22 @@ class StatisticOutputTask(AsyncTask):
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f4f7f6;
|
background-color: #faf7ff;
|
||||||
color: #333;
|
color: #3a2f57;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
background-color: #fff;
|
background-color: #ffffff;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
box-shadow: 0 10px 28px rgba(122, 98, 182, 0.12);
|
||||||
|
border: 1px solid #e5dcff;
|
||||||
}
|
}
|
||||||
h1, h2 {
|
h1, h2 {
|
||||||
color: #2c3e50;
|
color: #473673;
|
||||||
border-bottom: 2px solid #3498db;
|
border-bottom: 2px solid #9f8efb;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1115,33 +1138,62 @@ class StatisticOutputTask(AsyncTask):
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.info-item {
|
.info-item {
|
||||||
background-color: #ecf0f1;
|
background-color: #f3eeff;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
}
|
}
|
||||||
.info-item strong {
|
.info-item strong {
|
||||||
color: #2980b9;
|
color: #7162bf;
|
||||||
}
|
}
|
||||||
|
/* 新增:顶部工具条与按钮 */
|
||||||
|
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
|
||||||
|
.toolbar .right { display: flex; gap: 8px; align-items: center; }
|
||||||
|
.btn {
|
||||||
|
border: 1px solid #e3daff;
|
||||||
|
background-color: #fbf9ff;
|
||||||
|
color: #4a3c75;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
.btn:hover { border-color: #9f8efb; color: #7c6bcf; background-color: #f1ecff; }
|
||||||
|
/* 新增:KPI 卡片 */
|
||||||
|
.kpi-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 12px 0 6px; }
|
||||||
|
.kpi-card {
|
||||||
|
background: linear-gradient(145deg, #ffffff 0%, #f6f2ff 100%);
|
||||||
|
border: 1px solid #e3dbff;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
box-shadow: 0 6px 16px rgba(113, 98, 191, 0.1);
|
||||||
|
}
|
||||||
|
.kpi-title { font-size: 12px; color: #8579a6; letter-spacing: .3px; margin-bottom: 6px; }
|
||||||
|
.kpi-value { font-size: 20px; font-weight: 700; letter-spacing: .2px; color: #8b5cf6; }
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
/* 新增:表格包裹容器,支持横向滚动 */
|
||||||
|
.table-wrap { width: 100%; overflow-x: auto; border-radius: 6px; }
|
||||||
th, td {
|
th, td {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #e6ddff;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
background-color: #3498db;
|
background-color: #9f8efb;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
background-color: #f9f9f9;
|
background-color: #f6f1ff;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -1151,25 +1203,32 @@ class StatisticOutputTask(AsyncTask):
|
||||||
}
|
}
|
||||||
.tabs {
|
.tabs {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #ecf0f1;
|
background: #f9f6ff;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
border: 1px solid #e4dcff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 18px rgba(120, 101, 179, 0.08);
|
||||||
}
|
}
|
||||||
.tabs button {
|
.tabs button {
|
||||||
background: inherit; border: none; outline: none;
|
background: inherit; border: none; outline: none;
|
||||||
padding: 14px 16px; cursor: pointer;
|
padding: 12px 14px; cursor: pointer;
|
||||||
transition: 0.3s; font-size: 16px;
|
transition: 0.2s; font-size: 15px;
|
||||||
|
color: #52467a;
|
||||||
}
|
}
|
||||||
.tabs button:hover {
|
.tabs button:hover {
|
||||||
background-color: #d4dbdc;
|
background-color: #efe9ff;
|
||||||
}
|
}
|
||||||
.tabs button.active {
|
.tabs button.active {
|
||||||
background-color: #b3bbbd;
|
background-color: rgba(159, 142, 251, 0.25);
|
||||||
|
color: #6253a9;
|
||||||
}
|
}
|
||||||
.tab-content {
|
.tab-content {
|
||||||
display: none;
|
display: none;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #fff;
|
background-color: #fefcff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #e4dcff;
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
}
|
}
|
||||||
.tab-content.active {
|
.tab-content.active {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -1180,14 +1239,19 @@ class StatisticOutputTask(AsyncTask):
|
||||||
"""
|
"""
|
||||||
+ f"""
|
+ f"""
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>MaiBot运行统计报告</h1>
|
<div class="toolbar">
|
||||||
<p class="info-item"><strong>统计截止时间:</strong> {now.strftime("%Y-%m-%d %H:%M:%S")}</p>
|
<h1 style="margin: 0;">MaiBot运行统计报告</h1>
|
||||||
|
<div class="right">
|
||||||
|
<span class="info-item" style="margin: 0;"><strong>统计截止时间:</strong> {now.strftime("%Y-%m-%d %H:%M:%S")}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
{joined_tab_list}
|
{joined_tab_list}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{joined_tab_content}
|
{joined_tab_content}
|
||||||
|
<div class="footer">Made with ❤️ by MaiBot • 本页会定期自动覆盖生成</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
+ """
|
+ """
|
||||||
|
|
@ -1321,16 +1385,16 @@ class StatisticOutputTask(AsyncTask):
|
||||||
|
|
||||||
# 生成不同颜色的调色板
|
# 生成不同颜色的调色板
|
||||||
colors = [
|
colors = [
|
||||||
"#3498db",
|
"#8b5cf6",
|
||||||
"#e74c3c",
|
"#9f8efb",
|
||||||
"#2ecc71",
|
"#b5a6ff",
|
||||||
"#f39c12",
|
"#c7bbff",
|
||||||
"#9b59b6",
|
"#d9ceff",
|
||||||
"#1abc9c",
|
"#a78bfa",
|
||||||
"#34495e",
|
"#9073d8",
|
||||||
"#e67e22",
|
"#bfaefc",
|
||||||
"#95a5a6",
|
"#cabdfd",
|
||||||
"#f1c40f",
|
"#e6e0ff",
|
||||||
]
|
]
|
||||||
|
|
||||||
# 默认使用24小时数据生成数据集
|
# 默认使用24小时数据生成数据集
|
||||||
|
|
@ -1512,7 +1576,7 @@ class StatisticOutputTask(AsyncTask):
|
||||||
|
|
||||||
function createChart(chartType, data, timeRange) {{
|
function createChart(chartType, data, timeRange) {{
|
||||||
const config = chartConfigs[chartType];
|
const config = chartConfigs[chartType];
|
||||||
const colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#34495e', '#e67e22', '#95a5a6', '#f1c40f'];
|
const colors = ['#8b5cf6', '#9f8efb', '#b5a6ff', '#c7bbff', '#d9ceff', '#a78bfa', '#9073d8', '#bfaefc', '#cabdfd', '#e6e0ff'];
|
||||||
|
|
||||||
let datasets = [];
|
let datasets = [];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue