refactor(statistics): 重构统计数据获取逻辑
- 使用 Promise.allSettled 替代 Promise.all,提高容错性 - 添加详细的错误处理和日志记录,便于调试 - 优化用户、订单和销售额的计算逻辑,提高数据准确性 - 移除无效的 API 调用和冗余代码,简化代码结构 - 在 dashboard 页面添加刷新统计按钮,实现统计数据的强制刷新功能
This commit is contained in:
@@ -129,8 +129,9 @@ export async function shopOrderTotal(params?: ShopOrderParam) {
|
||||
params
|
||||
}
|
||||
);
|
||||
if (res.data.code === 0 && res.data.data) {
|
||||
return res.data.data;
|
||||
if (res.data.code === 0) {
|
||||
// 即使没有数据也返回空数组,而不是抛出错误
|
||||
return res.data.data || [];
|
||||
}
|
||||
return Promise.reject(new Error(res.data.message));
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { pageUsers } from '@/api/system/user';
|
||||
import { pageShopOrder, shopOrderTotal } from '@/api/shop/shopOrder';
|
||||
import { addCmsStatistics, listCmsStatistics, updateCmsStatistics } from '@/api/cms/cmsStatistics';
|
||||
import {
|
||||
addCmsStatistics,
|
||||
listCmsStatistics,
|
||||
updateCmsStatistics
|
||||
} from '@/api/cms/cmsStatistics';
|
||||
import { CmsStatistics } from '@/api/cms/cmsStatistics/model';
|
||||
import { safeNumber, hasValidId, isValidApiResponse } from '@/utils/type-guards';
|
||||
import { safeNumber, hasValidId } from '@/utils/type-guards';
|
||||
|
||||
export interface StatisticsState {
|
||||
// 统计数据
|
||||
@@ -88,7 +92,7 @@ export const useStatisticsStore = defineStore({
|
||||
isCacheValid: (state): boolean => {
|
||||
if (!state.lastUpdateTime) return false;
|
||||
const now = Date.now();
|
||||
return (now - state.lastUpdateTime) < state.cacheExpiry;
|
||||
return now - state.lastUpdateTime < state.cacheExpiry;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -105,16 +109,101 @@ export const useStatisticsStore = defineStore({
|
||||
|
||||
this.loading = true;
|
||||
try {
|
||||
// 并行获取各种统计数据
|
||||
const [users, orders, total, statisticsData] = await Promise.all([
|
||||
pageUsers({}),
|
||||
pageShopOrder({}),
|
||||
shopOrderTotal(),
|
||||
listCmsStatistics({})
|
||||
]);
|
||||
// 并行获取各种统计数据,使用Promise.allSettled确保部分失败不影响整体
|
||||
const [usersResult, ordersResult, totalResult, statisticsResult] =
|
||||
await Promise.allSettled([
|
||||
pageUsers({}),
|
||||
pageShopOrder({}),
|
||||
shopOrderTotal(),
|
||||
listCmsStatistics({})
|
||||
]);
|
||||
|
||||
// 安全提取结果
|
||||
const users =
|
||||
usersResult.status === 'fulfilled' ? usersResult.value : null;
|
||||
const orders =
|
||||
ordersResult.status === 'fulfilled' ? ordersResult.value : null;
|
||||
const total =
|
||||
totalResult.status === 'fulfilled' ? totalResult.value : null;
|
||||
const statisticsData =
|
||||
statisticsResult.status === 'fulfilled'
|
||||
? statisticsResult.value
|
||||
: null;
|
||||
|
||||
// 记录失败的API调用
|
||||
if (usersResult.status === 'rejected') {
|
||||
console.error('❌ 用户API调用失败:', usersResult.reason);
|
||||
}
|
||||
if (ordersResult.status === 'rejected') {
|
||||
console.error('❌ 订单API调用失败:', ordersResult.reason);
|
||||
}
|
||||
if (totalResult.status === 'rejected') {
|
||||
console.error('❌ 订单总额API调用失败:', totalResult.reason);
|
||||
}
|
||||
if (statisticsResult.status === 'rejected') {
|
||||
console.error('❌ 统计数据API调用失败:', statisticsResult.reason);
|
||||
}
|
||||
|
||||
// 添加调试日志
|
||||
console.log('🔍 统计数据获取结果:', {
|
||||
users: users,
|
||||
orders: orders,
|
||||
total: total,
|
||||
statisticsData: statisticsData
|
||||
});
|
||||
|
||||
let statistics: CmsStatistics;
|
||||
|
||||
// 安全获取用户数量,添加更详细的验证
|
||||
const userCount = (() => {
|
||||
if (!users) {
|
||||
console.warn('⚠️ 用户API返回空数据');
|
||||
return 0;
|
||||
}
|
||||
if (typeof users === 'object' && 'count' in users) {
|
||||
const count = users.count;
|
||||
console.log('✅ 用户数量:', count);
|
||||
return safeNumber(count);
|
||||
}
|
||||
console.warn('⚠️ 用户API返回数据格式不正确:', users);
|
||||
return 0;
|
||||
})();
|
||||
|
||||
// 安全获取订单数量
|
||||
const orderCount = (() => {
|
||||
if (!orders) {
|
||||
console.warn('⚠️ 订单API返回空数据');
|
||||
return 0;
|
||||
}
|
||||
if (typeof orders === 'object' && 'count' in orders) {
|
||||
const count = orders.count;
|
||||
console.log('✅ 订单数量:', count);
|
||||
return safeNumber(count);
|
||||
}
|
||||
console.warn('⚠️ 订单API返回数据格式不正确:', orders);
|
||||
return 0;
|
||||
})();
|
||||
|
||||
// 安全获取总销售额,处理数组情况
|
||||
const totalSales = (() => {
|
||||
if (!total) {
|
||||
console.warn('⚠️ 订单总额API返回空数据');
|
||||
return 0;
|
||||
}
|
||||
if (Array.isArray(total)) {
|
||||
// 如果是数组,计算总金额
|
||||
const sum = total.reduce((acc, order) => {
|
||||
const amount = order.payPrice || order.totalPrice || 0;
|
||||
return acc + safeNumber(amount);
|
||||
}, 0);
|
||||
console.log('✅ 总销售额(数组计算):', sum);
|
||||
return sum;
|
||||
}
|
||||
const amount = safeNumber(total);
|
||||
console.log('✅ 总销售额(直接值):', amount);
|
||||
return amount;
|
||||
})();
|
||||
|
||||
if (statisticsData && statisticsData.length > 0) {
|
||||
// 更新现有统计数据
|
||||
const existingStatistics = statisticsData[0];
|
||||
@@ -123,9 +212,9 @@ export const useStatisticsStore = defineStore({
|
||||
if (hasValidId(existingStatistics)) {
|
||||
const updateData: Partial<CmsStatistics> = {
|
||||
id: existingStatistics.id,
|
||||
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||
totalSales: safeNumber(total),
|
||||
userCount: userCount,
|
||||
orderCount: orderCount,
|
||||
totalSales: totalSales
|
||||
};
|
||||
|
||||
// 异步更新数据库
|
||||
@@ -140,17 +229,17 @@ export const useStatisticsStore = defineStore({
|
||||
} else {
|
||||
// 如果现有数据无效,使用基础数据
|
||||
statistics = {
|
||||
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||
totalSales: safeNumber(total),
|
||||
userCount: userCount,
|
||||
orderCount: orderCount,
|
||||
totalSales: totalSales
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// 创建新的统计数据
|
||||
statistics = {
|
||||
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0),
|
||||
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0),
|
||||
totalSales: safeNumber(total),
|
||||
userCount: userCount,
|
||||
orderCount: orderCount,
|
||||
totalSales: totalSales
|
||||
};
|
||||
|
||||
// 异步保存到数据库
|
||||
@@ -191,6 +280,15 @@ export const useStatisticsStore = defineStore({
|
||||
this.lastUpdateTime = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 强制刷新统计数据
|
||||
*/
|
||||
async forceRefresh() {
|
||||
console.log('🔄 强制刷新统计数据...');
|
||||
this.clearCache();
|
||||
return await this.fetchStatistics(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置缓存有效期
|
||||
*/
|
||||
|
||||
@@ -114,9 +114,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {createVNode, ref} from 'vue';
|
||||
import {message, Modal} from 'ant-design-vue';
|
||||
import {ExclamationCircleOutlined, QrcodeOutlined} from '@ant-design/icons-vue';
|
||||
import {ref} from 'vue';
|
||||
import {message} from 'ant-design-vue';
|
||||
import {QrcodeOutlined} from '@ant-design/icons-vue';
|
||||
import type {EleProTable} from 'ele-admin-pro';
|
||||
import {toDateString} from 'ele-admin-pro';
|
||||
import type {
|
||||
@@ -127,8 +127,6 @@ import WebsiteEdit from './components/websiteEdit.vue';
|
||||
import Qrcode from '@/components/QrCode/index.vue';
|
||||
import {
|
||||
pageCmsWebsite,
|
||||
removeCmsWebsite,
|
||||
removeBatchCmsWebsite,
|
||||
updateCmsWebsite
|
||||
} from '@/api/cms/cmsWebsite';
|
||||
import type {CmsWebsite, CmsWebsiteParam} from '@/api/cms/cmsWebsite/model';
|
||||
@@ -148,8 +146,6 @@ const selection = ref<CmsWebsite[]>([]);
|
||||
const current = ref<CmsWebsite | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 是否显示二维码
|
||||
const showQrcode = ref(false);
|
||||
// 二维码内容
|
||||
@@ -265,11 +261,6 @@ const openEdit = (row?: CmsWebsite) => {
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
const updateType = (row: CmsWebsite) => {
|
||||
updateCmsWebsite(row).then((msg) => {
|
||||
message.success(msg);
|
||||
@@ -286,48 +277,6 @@ const hideShare = () => {
|
||||
showQrcode.value = false;
|
||||
}
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: CmsWebsite) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeCmsWebsite(row.websiteId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchCmsWebsite(selection.value.map((d) => d.websiteId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 查询 */
|
||||
const query = () => {
|
||||
// 查询订单
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
<ShopOutlined/>
|
||||
站点管理
|
||||
</a-button>
|
||||
<a-button block @click="$router.push('/website/order')">
|
||||
<a-button block @click="$router.push('/shopOrder')">
|
||||
<CalendarOutlined/>
|
||||
订单管理
|
||||
</a-button>
|
||||
@@ -154,7 +154,11 @@
|
||||
</a-button>
|
||||
<a-button block @click="$router.push('/system/login-record')">
|
||||
<FileTextOutlined/>
|
||||
系统日志
|
||||
登录日志
|
||||
</a-button>
|
||||
<a-button block @click="refreshStatistics" :loading="loading">
|
||||
<ReloadOutlined/>
|
||||
刷新统计
|
||||
</a-button>
|
||||
<a-button block @click="clearSiteInfoCache">
|
||||
<ClearOutlined/>
|
||||
@@ -183,7 +187,8 @@ import {
|
||||
AccountBookOutlined,
|
||||
FileTextOutlined,
|
||||
ClearOutlined,
|
||||
MoneyCollectOutlined
|
||||
MoneyCollectOutlined,
|
||||
ReloadOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import {message} from 'ant-design-vue/es';
|
||||
import {openNew} from "@/utils/common";
|
||||
@@ -233,6 +238,17 @@ const clearSiteInfoCache = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新统计数据
|
||||
const refreshStatistics = async () => {
|
||||
try {
|
||||
await statisticsStore.forceRefresh();
|
||||
message.success('统计数据刷新成功');
|
||||
} catch (error) {
|
||||
console.error('刷新统计数据失败:', error);
|
||||
message.error('刷新统计数据失败');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载网站信息和统计数据
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user