addanalysis

This commit is contained in:
2025-12-27 13:51:08 +08:00
parent a718ac7874
commit b392f9cf0d
7 changed files with 265 additions and 126 deletions

View File

@@ -130,6 +130,12 @@ export namespace AgentApi {
withdraw_no?: string;
}
// 提现统计数据
export interface WithdrawalStatistics {
total_withdrawal_amount: number;
today_withdrawal_amount: number;
}
// 代理上级抽佣相关接口
export interface AgentCommissionDeductionListItem {
id: number;
@@ -446,6 +452,15 @@ async function updateAgentMembershipConfig(
);
}
/**
* 获取提现统计数据
*/
async function getWithdrawalStatistics() {
return requestClient.get<AgentApi.WithdrawalStatistics>(
'/agent/agent-withdrawal/statistics',
);
}
export {
getAgentCommissionDeductionList,
getAgentCommissionList,
@@ -457,6 +472,7 @@ export {
getAgentRewardList,
getAgentWithdrawalList,
getMembershipRechargeOrderList,
getWithdrawalStatistics,
updateAgentMembershipConfig,
updateAgentProductionConfig,
};

View File

@@ -11,6 +11,7 @@ export namespace OrderApi {
payment_platform: 'alipay' | 'appleiap' | 'wechat';
payment_scene: 'app' | 'h5' | 'mini_program' | 'public_account';
amount: number;
sales_cost: number;
status: 'closed' | 'failed' | 'paid' | 'pending' | 'refunded';
query_state: 'cleaned' | 'failed' | 'pending' | 'processing' | 'success';
create_time: string;
@@ -34,6 +35,20 @@ export namespace OrderApi {
refund_no: string;
amount: number;
}
// 退款统计数据
export interface RefundStatistics {
total_refund_amount: number;
today_refund_amount: number;
}
// 收入统计数据
export interface IncomeStatistics {
total_revenue_amount: number;
today_revenue_amount: number;
total_profit_amount: number;
today_profit_amount: number;
}
}
/**
@@ -57,4 +72,18 @@ async function refundOrder(id: number, data: OrderApi.RefundOrderRequest) {
);
}
export { getOrderList, refundOrder };
/**
* 获取退款统计数据
*/
async function getRefundStatistics() {
return requestClient.get<OrderApi.RefundStatistics>('/order/refund-statistics');
}
/**
* 获取收入统计数据
*/
async function getIncomeStatistics() {
return requestClient.get<OrderApi.IncomeStatistics>('/order/revenue-statistics');
}
export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics };

View File

@@ -5,28 +5,14 @@ import { onMounted, ref } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { statsHistory, statsTotal } from '#/api/promotion/analytics';
import { getOrderList } from '#/api/order/order';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
// 获取30天前的日期
const getDateString = (daysAgo: number) => {
const date = new Date();
date.setDate(date.getDate() - daysAgo);
return date.toISOString().split('T')[0];
};
onMounted(async () => {
try {
// 获取趋势数据
const endDate = getDateString(0); // 今天
const startDate = getDateString(29); // 29天前
const trendData = await statsHistory({ start_date: startDate, end_date: endDate });
// 获取统计数据
const statsData = await statsTotal();
// 准备图表数据
const dates = Array.from({ length: 30 }).map((_, index) => {
const date = new Date();
@@ -34,47 +20,37 @@ onMounted(async () => {
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
});
// 如果有历史数据,使用历史数据;否则使用模拟数据
let orderData = Array(30).fill(0);
if (trendData && trendData.length > 0) {
// 将历史数据按日期排序并映射到数组
const sortedData = trendData.sort((a, b) =>
new Date(a.stats_date).getTime() - new Date(b.stats_date).getTime()
);
sortedData.forEach((item) => {
const itemDate = new Date(item.stats_date);
const today = new Date();
const daysDiff = Math.floor((today.getTime() - itemDate.getTime()) / (1000 * 60 * 60 * 24));
// 准备日期数组,用于查询订单数据
const queryDates = Array.from({ length: 30 }).map((_, index) => {
const date = new Date();
date.setDate(date.getDate() - 29 + index);
return date.toISOString().split('T')[0]; // YYYY-MM-DD格式
});
// 获取每日订单数据
const orderDataPromises = queryDates.map(async (date) => {
try {
// 获取当天的开始和结束时间
const startTime = `${date} 00:00:00`;
const endTime = `${date} 23:59:59`;
if (daysDiff >= 0 && daysDiff < 30) {
// 使用实际日期索引
orderData[29 - daysDiff] = item.pay_count || 0;
}
});
} else {
// 没有历史数据时,使用统计数据生成模拟数据
const todayPayCount = statsData?.today_pay_count || 0;
const totalPayCount = statsData?.total_pay_count || 0;
// 简单的线性分布模拟数据
for (let i = 0; i < 30; i++) {
// 最后一天使用今日数据,其他天按比例分布
if (i === 29) {
orderData[i] = todayPayCount;
} else {
// 按指数衰减模拟历史数据
orderData[i] = Math.max(0, Math.floor(todayPayCount * Math.exp(-0.1 * (29 - i))));
}
// 获取当天的订单数据
const response = await getOrderList({
page: 1,
pageSize: 1,
create_time_start: startTime,
create_time_end: endTime
});
return response.total || 0;
} catch (error) {
console.error(`获取${date}的订单数据失败:`, error);
return 0;
}
// 确保总和不超过总计数
const sum = orderData.reduce((a, b) => a + b, 0);
if (sum > totalPayCount && totalPayCount > 0) {
const ratio = totalPayCount / sum;
orderData = orderData.map(val => Math.floor(val * ratio));
}
}
});
// 等待所有请求完成
const orderData = await Promise.all(orderDataPromises);
// 计算Y轴最大值
const maxValue = Math.max(...orderData) || 10;
@@ -89,13 +65,17 @@ onMounted(async () => {
},
series: [
{
barMaxWidth: 80,
data: orderData,
type: 'bar',
type: 'line',
name: '订单数',
smooth: true,
itemStyle: {
color: '#4f9cff',
},
areaStyle: {
opacity: 0.3,
color: '#4f9cff',
},
},
],
tooltip: {
@@ -113,6 +93,7 @@ onMounted(async () => {
xAxis: {
data: dates,
type: 'category',
boundaryGap: false,
},
yAxis: {
max: Math.ceil(maxValue * 1.2), // 比最大值大20%作为Y轴上限
@@ -134,13 +115,17 @@ onMounted(async () => {
},
series: [
{
barMaxWidth: 80,
data: Array(30).fill(0),
type: 'bar',
type: 'line',
name: '订单数',
smooth: true,
itemStyle: {
color: '#4f9cff',
},
areaStyle: {
opacity: 0.3,
color: '#4f9cff',
},
},
],
tooltip: {
@@ -158,6 +143,7 @@ onMounted(async () => {
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
}),
type: 'category',
boundaryGap: false,
},
yAxis: {
max: 10,

View File

@@ -22,40 +22,55 @@ import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
import AnalyticsVisits from './analytics-visits.vue';
import { getAgentList } from '#/api/agent';
import { getOrderList } from '#/api/order/order';
import { getProductList } from '#/api/product-manage/product';
import { getAgentList, getWithdrawalStatistics } from '#/api/agent';
import { getOrderList, getRefundStatistics, getIncomeStatistics } from '#/api/order/order';
import { getPlatformUserList } from '#/api/platform-user';
// 初始化概览数据
const overviewItems = ref<AnalysisOverviewItem[]>([
{
icon: SvgCardIcon,
title: '平台用户数',
totalTitle: '总用户数',
totalValue: 0,
title: '用户数',
value: 0,
todaytitle: '今日新增用户数',
todayValue: 0,
Subtitle: '总代理数',
SubValue: 0,
todaySubtitle: '今日新增代理数',
todaySubValue: 0,
},
{
icon: SvgCakeIcon,
title: '推广访问量',
totalTitle: '总推广访问量',
totalValue: 0,
title: '总订单数',
value: 0,
todaytitle: '今日新增订单数',
todayValue: 0,
Subtitle: '代理总订单量',
SubValue: 0,
todaySubtitle: '今日新增代理订单量',
todaySubValue: 0,
},
{
icon: SvgDownloadIcon,
title: '产品数量',
totalTitle: '总产品数量',
totalValue: 0,
title: '总收入',
value: 0,
todaytitle: '今日新增收入',
todayValue: 0,
Subtitle: '总利润',
SubValue: 0,
todaySubtitle: '今日新增利润',
todaySubValue: 0,
},
{
icon: SvgBellIcon,
title: '代理数量',
totalTitle: '总代理数量',
totalValue: 0,
title: '总提现金额',
value: 0,
todaytitle: '今日新增提现金额',
todayValue: 0,
Subtitle: '总退款金额',
SubValue: 0,
todaySubtitle: '今日新增退款金额',
todaySubValue: 0,
},
]);
@@ -73,51 +88,128 @@ const chartTabs: TabOption[] = [
// 获取统计数据
async function fetchStatistics() {
try {
// 获取平台用户数据
// 获取今日的开始和结束时间
const today = new Date();
// 将时间格式化为后端期望的格式 (YYYY-MM-DD HH:MM:SS)
const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString().replace('T', ' ').substring(0, 19);
const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).toISOString().replace('T', ' ').substring(0, 19);
// 获取平台用户数据(总数)
const platformUserResponse = await getPlatformUserList({ page: 1, pageSize: 1 });
const platformUserTotal = platformUserResponse.total || 0;
// 获取今日新增用户数
// 由于平台用户API不支持时间过滤我们需要获取更多数据并在前端过滤
const newUserResponse = await getPlatformUserList({ page: 1, pageSize: 1000 });
const newUserCount = newUserResponse.items?.filter(user => {
const userCreateTime = new Date(user.create_time);
return userCreateTime >= new Date(startTime) && userCreateTime < new Date(endTime);
}).length || 0;
// 获取订单数据
const orderResponse = await getOrderList({ page: 1, pageSize: 1 });
const orderTotal = orderResponse.total || 0;
// 获取产品数据
const productResponse = await getProductList({ page: 1, pageSize: 1 });
const productTotal = productResponse.total || 0;
// 获取代理订单数据
const agentOrderResponse = await getOrderList({ page: 1, pageSize: 1, is_agent_order: true });
const agentOrderTotal = agentOrderResponse.total || 0;
// 获取代理数据
// 获取今日新增订单数
const todayOrderResponse = await getOrderList({
page: 1,
pageSize: 1000,
create_time_start: startTime,
create_time_end: endTime
});
const todayOrderTotal = todayOrderResponse.total || 0;
// 获取今日新增代理订单数
const todayAgentOrderResponse = await getOrderList({
page: 1,
pageSize: 1000,
is_agent_order: true,
create_time_start: startTime,
create_time_end: endTime
});
const todayAgentOrderTotal = todayAgentOrderResponse.total || 0;
// Product data is no longer needed for order statistics
// 获取代理数据(总数)
const agentResponse = await getAgentList({ page: 1, pageSize: 1 });
const agentTotal = agentResponse.total || 0;
// 获取今日新增代理数
const newAgentResponse = await getAgentList({
page: 1,
pageSize: 100,
create_time_start: startTime,
create_time_end: endTime
});
const newAgentCount = newAgentResponse.total || 0;
// 获取提现统计数据
const withdrawalStatsResponse = await getWithdrawalStatistics();
const totalWithdrawalAmount = withdrawalStatsResponse.total_withdrawal_amount || 0;
const todayWithdrawalAmount = withdrawalStatsResponse.today_withdrawal_amount || 0;
// 获取退款统计数据
const refundStatsResponse = await getRefundStatistics();
const totalRefundAmount = refundStatsResponse.total_refund_amount || 0;
const todayRefundAmount = refundStatsResponse.today_refund_amount || 0;
// 获取收入统计数据
const incomeStatsResponse = await getIncomeStatistics();
const totalIncome = incomeStatsResponse.total_revenue_amount || 0;
const todayIncome = incomeStatsResponse.today_revenue_amount || 0;
const totalProfit = incomeStatsResponse.total_profit_amount || 0;
const todayProfit = incomeStatsResponse.today_profit_amount || 0;
// 更新概览数据
overviewItems.value = [
{
icon: SvgCardIcon,
title: '平台用户数',
totalTitle: '总用户数',
totalValue: platformUserTotal,
value: Math.min(100, platformUserTotal), // 显示最近的100个作为今日新增
title: '用户数',
value: platformUserTotal,
todaytitle: '今日新增用户数',
todayValue: newUserCount,
Subtitle: '总代理数',
SubValue: agentTotal,
todaySubtitle: '今日新增代理数',
todaySubValue: newAgentCount,
},
{
icon: SvgCakeIcon,
title: '推广访问量',
totalTitle: '总推广访问量',
totalValue: orderTotal * 10, // 假设每个订单平均带来10次访问
value: Math.min(1000, orderTotal), // 显示最近的1000个作为今日新增
title: '总订单数',
value: orderTotal,
todaytitle: '今日新增订单数',
todayValue: todayOrderTotal,
Subtitle: '总代理订单量',
SubValue: agentOrderTotal,
todaySubtitle: '今日新增代理订单量',
todaySubValue: todayAgentOrderTotal,
},
{
icon: SvgDownloadIcon,
title: '产品数量',
totalTitle: '总产品数量',
totalValue: productTotal,
value: Math.min(10, productTotal), // 显示最近的10个作为今日新增
title: '总收入',
value: totalIncome,
todaytitle: '今日新增收入',
todayValue: todayIncome,
Subtitle: '总利润',
SubValue: totalProfit,
todaySubtitle: '今日新增利润',
todaySubValue: todayProfit,
},
{
icon: SvgBellIcon,
title: '代理数量',
totalTitle: '总代理数量',
totalValue: agentTotal,
value: Math.min(50, agentTotal), // 显示最近的50个作为今日新增
title: '总提现金额',
value: totalWithdrawalAmount,
todaytitle: '今日新增提现金额',
todayValue: todayWithdrawalAmount,
Subtitle: '总退款金额',
SubValue: totalRefundAmount,
todaySubtitle: '今日新增退款金额',
todaySubValue: todayRefundAmount,
},
];
} catch (error) {

View File

@@ -53,6 +53,14 @@ export function useColumns<T = OrderApi.Order>(
return sceneMap[row.payment_scene] || row.payment_scene;
},
},
{
field: 'sales_cost',
title: '成本价',
width: 120,
formatter: ({ row }) => {
return `¥${row.sales_cost.toFixed(2)}`;
},
},
{
field: 'amount',
title: '金额',