🐛 修复功能调用统计空白 (#1694)
BIN
resources/template/bar_chart/background/0.jpg
Normal file
|
After Width: | Height: | Size: 393 KiB |
BIN
resources/template/bar_chart/background/1.jpg
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
resources/template/bar_chart/background/10.jpg
Normal file
|
After Width: | Height: | Size: 654 KiB |
BIN
resources/template/bar_chart/background/11.jpg
Normal file
|
After Width: | Height: | Size: 365 KiB |
BIN
resources/template/bar_chart/background/2.jpg
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
resources/template/bar_chart/background/3.jpg
Normal file
|
After Width: | Height: | Size: 415 KiB |
BIN
resources/template/bar_chart/background/4.jpg
Normal file
|
After Width: | Height: | Size: 608 KiB |
BIN
resources/template/bar_chart/background/5.jpg
Normal file
|
After Width: | Height: | Size: 499 KiB |
BIN
resources/template/bar_chart/background/6.jpg
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
resources/template/bar_chart/background/7.jpg
Normal file
|
After Width: | Height: | Size: 745 KiB |
BIN
resources/template/bar_chart/background/8.jpg
Normal file
|
After Width: | Height: | Size: 349 KiB |
BIN
resources/template/bar_chart/background/9.jpg
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
@ -11,7 +11,10 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- 图表容器 -->
|
<!-- 图表容器 -->
|
||||||
<div id="main" style="width: 1000px;height:500px;"></div>
|
<div class="container">
|
||||||
|
<div id="main" style="height: 1000px; width: 1000px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
// 基于准备好的dom,初始化echarts实例
|
// 基于准备好的dom,初始化echarts实例
|
||||||
@ -19,25 +22,41 @@
|
|||||||
|
|
||||||
// 指定图表的配置项和数据
|
// 指定图表的配置项和数据
|
||||||
var option = {
|
var option = {
|
||||||
|
title: {
|
||||||
|
text: {{data.title | tojson}},
|
||||||
|
left: 'center',
|
||||||
|
top: 'top',
|
||||||
|
textStyle: {
|
||||||
|
color: '#333',
|
||||||
|
fontSize: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
graphic: {
|
||||||
|
type: 'image',
|
||||||
|
id: 'background',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
z: -10,
|
||||||
|
bounding: 'raw',
|
||||||
|
origin: [0, 0],
|
||||||
|
|
||||||
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
boundaryGap: [0, 0.01]
|
boundaryGap: [0, 0.01]
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: {
|
data: {{data.category_data | tojson}}
|
||||||
{
|
|
||||||
data.category_data | tojson
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
data: {
|
data: {{data.data | tojson}},
|
||||||
{
|
|
||||||
data.data | tojson
|
|
||||||
}
|
|
||||||
},
|
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
|
label: {
|
||||||
|
show: true, // 显示标签
|
||||||
|
position: 'right', // 数字显示在柱子上方
|
||||||
|
formatter: '{c}' // 显示数值
|
||||||
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
// 为每个柱子设置随机颜色
|
// 为每个柱子设置随机颜色
|
||||||
color: function () {
|
color: function () {
|
||||||
@ -57,3 +76,33 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 1000px;
|
||||||
|
height: 1000px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-image: url({{data.background_image|tojson}});
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-size: cover;
|
||||||
|
filter: blur(5px);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
/* 其他内容样式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -2,18 +2,17 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
from tortoise.functions import Count
|
from tortoise.functions import Count
|
||||||
|
|
||||||
from zhenxun.models.group_console import GroupConsole
|
from zhenxun.utils.enum import PluginType
|
||||||
from zhenxun.models.group_member_info import GroupInfoUser
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
|
||||||
from zhenxun.models.statistics import Statistics
|
from zhenxun.models.statistics import Statistics
|
||||||
|
from zhenxun.utils.image_utils import BuildImage
|
||||||
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
from zhenxun.utils.echart_utils import ChartUtils
|
from zhenxun.utils.echart_utils import ChartUtils
|
||||||
from zhenxun.utils.echart_utils.models import Barh
|
from zhenxun.utils.echart_utils.models import Barh
|
||||||
from zhenxun.utils.enum import PluginType
|
from zhenxun.models.group_console import GroupConsole
|
||||||
from zhenxun.utils.image_utils import BuildImage, BuildMat, MatType
|
from zhenxun.models.group_member_info import GroupInfoUser
|
||||||
|
|
||||||
|
|
||||||
class StatisticsManage:
|
class StatisticsManage:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_statistics(
|
async def get_statistics(
|
||||||
cls,
|
cls,
|
||||||
@ -28,12 +27,12 @@ class StatisticsManage:
|
|||||||
if search_type == "day":
|
if search_type == "day":
|
||||||
day = 1
|
day = 1
|
||||||
day_type = "日"
|
day_type = "日"
|
||||||
if search_type == "week":
|
elif search_type == "month":
|
||||||
day = 7
|
|
||||||
day_type = "周"
|
|
||||||
if search_type == "month":
|
|
||||||
day = 30
|
day = 30
|
||||||
day_type = "月"
|
day_type = "月"
|
||||||
|
elif search_type == "week":
|
||||||
|
day = 7
|
||||||
|
day_type = "周"
|
||||||
if day_type:
|
if day_type:
|
||||||
day_type += f"({day}天)"
|
day_type += f"({day}天)"
|
||||||
title = ""
|
title = ""
|
||||||
@ -53,7 +52,7 @@ class StatisticsManage:
|
|||||||
else:
|
else:
|
||||||
title = "功能调用统计"
|
title = "功能调用统计"
|
||||||
if is_global and not user_id:
|
if is_global and not user_id:
|
||||||
title = "全局 " + title
|
title = f"全局 {title}"
|
||||||
return await cls.get_global_statistics(plugin_name, day, title)
|
return await cls.get_global_statistics(plugin_name, day, title)
|
||||||
if user_id:
|
if user_id:
|
||||||
return await cls.get_my_statistics(user_id, group_id, day, title)
|
return await cls.get_my_statistics(user_id, group_id, day, title)
|
||||||
@ -76,9 +75,11 @@ class StatisticsManage:
|
|||||||
.group_by("plugin_name")
|
.group_by("plugin_name")
|
||||||
.values_list("plugin_name", "count")
|
.values_list("plugin_name", "count")
|
||||||
)
|
)
|
||||||
if not data_list:
|
return (
|
||||||
return "统计数据为空..."
|
await cls.__build_image(data_list, title)
|
||||||
return await cls.__build_image(data_list, title)
|
if data_list
|
||||||
|
else "统计数据为空..."
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_my_statistics(
|
async def get_my_statistics(
|
||||||
@ -95,9 +96,11 @@ class StatisticsManage:
|
|||||||
.group_by("plugin_name")
|
.group_by("plugin_name")
|
||||||
.values_list("plugin_name", "count")
|
.values_list("plugin_name", "count")
|
||||||
)
|
)
|
||||||
if not data_list:
|
return (
|
||||||
return "统计数据为空..."
|
await cls.__build_image(data_list, title)
|
||||||
return await cls.__build_image(data_list, title)
|
if data_list
|
||||||
|
else "统计数据为空..."
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_group_statistics(cls, group_id: str, day: int | None, title: str):
|
async def get_group_statistics(cls, group_id: str, day: int | None, title: str):
|
||||||
@ -110,9 +113,11 @@ class StatisticsManage:
|
|||||||
.group_by("plugin_name")
|
.group_by("plugin_name")
|
||||||
.values_list("plugin_name", "count")
|
.values_list("plugin_name", "count")
|
||||||
)
|
)
|
||||||
if not data_list:
|
return (
|
||||||
return "统计数据为空..."
|
await cls.__build_image(data_list, title)
|
||||||
return await cls.__build_image(data_list, title)
|
if data_list
|
||||||
|
else "统计数据为空..."
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def __build_image(cls, data_list: list[tuple[str, int]], title: str):
|
async def __build_image(cls, data_list: list[tuple[str, int]], title: str):
|
||||||
@ -126,5 +131,5 @@ class StatisticsManage:
|
|||||||
for plugin in plugin_info:
|
for plugin in plugin_info:
|
||||||
x_index.append(plugin.name)
|
x_index.append(plugin.name)
|
||||||
data.append(module2count.get(plugin.module, 0))
|
data.append(module2count.get(plugin.module, 0))
|
||||||
barh = Barh(data=data, category_data=x_index)
|
barh = Barh(data=data, category_data=x_index, title=title)
|
||||||
return await ChartUtils.barh(barh)
|
return await ChartUtils.barh(barh)
|
||||||
|
|||||||
@ -1,22 +1,30 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
|
||||||
from nonebot_plugin_htmlrender import template_to_pic
|
from nonebot_plugin_htmlrender import template_to_pic
|
||||||
|
|
||||||
from zhenxun.configs.path_config import TEMPLATE_PATH
|
|
||||||
from zhenxun.utils._build_image import BuildImage
|
from zhenxun.utils._build_image import BuildImage
|
||||||
|
from zhenxun.configs.path_config import TEMPLATE_PATH
|
||||||
|
|
||||||
from .models import Barh
|
from .models import Barh
|
||||||
|
|
||||||
|
BACKGROUND_PATH = TEMPLATE_PATH / "bar_chart" / "background"
|
||||||
|
|
||||||
|
|
||||||
class ChartUtils:
|
class ChartUtils:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def barh(cls, data: Barh) -> BuildImage:
|
async def barh(cls, data: Barh) -> BuildImage:
|
||||||
"""横向统计图"""
|
"""横向统计图"""
|
||||||
|
to_json = data.dict()
|
||||||
|
to_json["background_image"] = (
|
||||||
|
f"./background/{random.choice(os.listdir(BACKGROUND_PATH))}"
|
||||||
|
)
|
||||||
pic = await template_to_pic(
|
pic = await template_to_pic(
|
||||||
template_path=str((TEMPLATE_PATH / "bar_chart").absolute()),
|
template_path=str((TEMPLATE_PATH / "bar_chart").absolute()),
|
||||||
template_name="main.html",
|
template_name="main.html",
|
||||||
templates={"data": data},
|
templates={"data": to_json},
|
||||||
pages={
|
pages={
|
||||||
"viewport": {"width": 1000, "height": 500},
|
"viewport": {"width": 1000, "height": 1000},
|
||||||
"base_url": f"file://{TEMPLATE_PATH}",
|
"base_url": f"file://{TEMPLATE_PATH}",
|
||||||
},
|
},
|
||||||
wait=2,
|
wait=2,
|
||||||
|
|||||||
@ -2,8 +2,9 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
|
|
||||||
class Barh(BaseModel):
|
class Barh(BaseModel):
|
||||||
|
|
||||||
category_data: list[str]
|
category_data: list[str]
|
||||||
"""坐标轴数据"""
|
"""坐标轴数据"""
|
||||||
data: list[int | float]
|
data: list[int | float]
|
||||||
"""实际数据"""
|
"""实际数据"""
|
||||||
|
title: str
|
||||||
|
"""标题"""
|
||||||
|
|||||||