feat: mp statistics

This commit is contained in:
xingyu4j
2025-10-22 18:03:32 +08:00
parent d0b66dbee4
commit cc937a88f1
4 changed files with 340 additions and 26 deletions

View File

@@ -1,13 +1,10 @@
import type { PageParam } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MpStatisticsApi {
/** 统计查询参数 */
export interface StatisticsQuery extends PageParam {
export interface StatisticsQuery {
accountId: number;
beginDate: string;
endDate: string;
date: Date[];
}
/** 消息发送概况数据 */

View File

@@ -0,0 +1,163 @@
import type { MpStatisticsApi } from '#/api/mp/statistics';
// 用户增减数据图表配置项
export function userSummaryOption(
data: MpStatisticsApi.UserSummary[],
dates: string[],
): any {
return {
color: ['#67C23A', '#E5323E'],
legend: {
data: ['新增用户', '取消关注的用户'],
},
tooltip: {},
xAxis: {
data: dates,
},
yAxis: {
minInterval: 1,
},
series: [
{
name: '新增用户',
type: 'bar',
label: {
show: true,
},
barGap: 0,
data: data.map((item) => item.newUser), // 新增用户的数据
},
{
name: '取消关注的用户',
type: 'bar',
label: {
show: true,
},
data: data.map((item) => item.cancelUser), // 取消关注的用户的数据
},
],
};
}
// 累计用户数据图表配置项
export function userCumulateOption(
data: MpStatisticsApi.UserCumulate[],
dates: string[],
): any {
return {
legend: {
data: ['累计用户量'],
},
xAxis: {
type: 'category',
data: dates,
},
yAxis: {
minInterval: 1,
},
series: [
{
name: '累计用户量',
data: data.map((item) => item.cumulateUser), // 累计用户量的数据
type: 'line',
smooth: true,
label: {
show: true,
},
},
],
};
}
// 消息发送概况数据图表配置项
export function upstreamMessageOption(
data: MpStatisticsApi.UpstreamMessage[],
dates: string[],
): any {
return {
color: ['#67C23A', '#E5323E'],
legend: {
data: ['用户发送人数', '用户发送条数'],
},
tooltip: {},
xAxis: {
data: dates, // X 轴的日期范围
},
yAxis: {
minInterval: 1,
},
series: [
{
name: '用户发送人数',
type: 'line',
smooth: true,
label: {
show: true,
},
data: data.map((item) => item.msgUser), // 用户发送人数的数据
},
{
name: '用户发送条数',
type: 'line',
smooth: true,
label: {
show: true,
},
data: data.map((item) => item.msgCount), // 用户发送条数的数据
},
],
};
}
// 接口分析况数据图表配置项
export function interfaceSummaryOption(
data: MpStatisticsApi.InterfaceSummary[],
dates: string[],
): any {
return {
color: ['#67C23A', '#E5323E', '#E6A23C', '#409EFF'],
legend: {
data: ['被动回复用户消息的次数', '失败次数', '最大耗时', '总耗时'],
},
tooltip: {},
xAxis: {
data: dates, // X 轴的日期范围
},
yAxis: {},
series: [
{
name: '被动回复用户消息的次数',
type: 'bar',
label: {
show: true,
},
barGap: 0,
data: data.map((item) => item.callbackCount), // 被动回复用户消息的次数的数据
},
{
name: '失败次数',
type: 'bar',
label: {
show: true,
},
data: data.map((item) => item.failCount), // 失败次数的数据
},
{
name: '最大耗时',
type: 'bar',
label: {
show: true,
},
data: data.map((item) => item.maxTimeCost), // 最大耗时的数据
},
{
name: '总耗时',
type: 'bar',
label: {
show: true,
},
data: data.map((item) => item.totalTimeCost), // 总耗时的数据
},
],
};
}

View File

@@ -0,0 +1,42 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { MpAccountApi } from '#/api/mp/account';
import { beginOfDay, endOfDay, formatDateTime } from '@vben/utils';
import { getSimpleAccountList } from '#/api/mp/account';
let accountList: MpAccountApi.AccountSimple[] = [];
getSimpleAccountList().then((data) => (accountList = data));
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'accountId',
label: '公众号',
component: 'Select',
componentProps: {
options: accountList.map((item) => ({
label: item.name,
value: item.id,
})),
placeholder: '请选择公众号',
allowClear: true,
},
defaultValue: accountList[0]?.id,
},
{
fieldName: 'dateRange',
label: '时间范围',
component: 'RangePicker',
componentProps: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
defaultValue: [
formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))),
formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))),
] as [Date, Date],
},
];
}

View File

@@ -1,28 +1,140 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import type { EchartsUIType } from '@vben/plugins/echarts';
import { Button } from 'ant-design-vue';
import { ref } from 'vue';
import { ContentWrap, Page } from '@vben/common-ui';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { addTime, betweenDay, formatDate } from '@vben/utils';
import { Card, message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
getInterfaceSummary,
getUpstreamMessage,
getUserCumulate,
getUserSummary,
} from '#/api/mp/statistics';
import { $t } from '#/locales';
import {
interfaceSummaryOption,
upstreamMessageOption,
userCumulateOption,
userSummaryOption,
} from './chart-options';
import { useGridFormSchema } from './data';
const userSummaryRef = ref<EchartsUIType>();
const { renderEcharts: renderUserSummaryEcharts } = useEcharts(userSummaryRef);
const userCumulateRef = ref<EchartsUIType>();
const { renderEcharts: renderUserCumulateEcharts } =
useEcharts(userCumulateRef);
const upstreamMessageRef = ref<EchartsUIType>();
const { renderEcharts: renderUpstreamMessageEcharts } =
useEcharts(upstreamMessageRef);
const interfaceSummaryRef = ref<EchartsUIType>();
const { renderEcharts: renderInterfaceSummaryEcharts } =
useEcharts(interfaceSummaryRef);
const [QueryForm] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
},
handleSubmit: onSubmit,
layout: 'horizontal',
schema: useGridFormSchema(),
submitButtonOptions: {
content: $t('common.query'),
},
wrapperClass: 'grid-cols-1 md:grid-cols-2',
});
function onSubmit(values: Record<string, any>) {
getSummary(values);
}
/** 加载数据 */
async function getSummary(values: Record<string, any>) {
const accountId = values.accountId;
if (!accountId) {
message.warning('请先选择公众号');
return;
}
const dateRange = values.dateRange;
if (!dateRange) {
message.warning('请先选择时间范围');
return;
}
// 必须选择 7 天内,因为公众号有时间跨度限制为 7
if (betweenDay(dateRange[0], dateRange[1]) >= 7) {
message.error('时间间隔 7 天以内,请重新选择');
return;
}
// 处理日期
const days = betweenDay(dateRange[0], dateRange[1]);
const dates = Array.from(
{ length: days },
(_, index) =>
formatDate(addTime(dateRange[0], index), 'YYYY-MM-DD') as string,
);
// 用户增减数据
const userSummaryData = await getUserSummary({
accountId,
date: dateRange,
});
renderUserSummaryEcharts(userSummaryOption(userSummaryData, dates));
// 累计用户数据
const userCumulateData = await getUserCumulate({
accountId,
date: dateRange,
});
renderUserCumulateEcharts(userCumulateOption(userCumulateData, dates));
// 消息发送概况数据
const upstreamMessageData = await getUpstreamMessage({
accountId,
date: dateRange,
});
renderUpstreamMessageEcharts(
upstreamMessageOption(upstreamMessageData, dates),
);
// 接口分析数据
const interfaceSummaryData = await getInterfaceSummary({
accountId,
date: dateRange,
});
renderInterfaceSummaryEcharts(
interfaceSummaryOption(interfaceSummaryData, dates),
);
}
</script>
<template>
<Page>
<Button
danger
type="link"
target="_blank"
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
>
该功能支持 Vue3 + element-plus 版本
</Button>
<br />
<Button
type="link"
target="_blank"
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mp/statistics/index"
>
可参考
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mp/statistics/index
代码pull request 贡献给我们
</Button>
<Page auto-content-height>
<ContentWrap class="h-full w-full">
<QueryForm />
<div class="flex h-1/3 w-full gap-4">
<Card class="h-full w-1/2" title="用户增减数据">
<EchartsUI ref="userSummaryRef" />
</Card>
<Card class="h-full w-1/2" title="累计用户数据">
<EchartsUI ref="userCumulateRef" />
</Card>
</div>
<div class="mt-4 flex h-1/3 w-full gap-4">
<Card class="h-full w-1/2" title="消息发送概况数据">
<EchartsUI ref="upstreamMessageRef" />
</Card>
<Card class="h-full w-1/2" title="接口分析数据">
<EchartsUI ref="interfaceSummaryRef" />
</Card>
</div>
</ContentWrap>
</Page>
</template>