refactor(statistics): 重构统计数据获取逻辑

- 使用 Promise.allSettled 替代 Promise.all,提高容错性
- 添加详细的错误处理和日志记录,便于调试
- 优化用户、订单和销售额的计算逻辑,提高数据准确性
- 移除无效的 API 调用和冗余代码,简化代码结构
- 在 dashboard 页面添加刷新统计按钮,实现统计数据的强制刷新功能
This commit is contained in:
2025-09-07 13:18:09 +08:00
parent a0375d8a97
commit 7b6fac7c41
4 changed files with 142 additions and 78 deletions

View File

@@ -129,8 +129,9 @@ export async function shopOrderTotal(params?: ShopOrderParam) {
params params
} }
); );
if (res.data.code === 0 && res.data.data) { if (res.data.code === 0) {
return res.data.data; // 即使没有数据也返回空数组,而不是抛出错误
return res.data.data || [];
} }
return Promise.reject(new Error(res.data.message)); return Promise.reject(new Error(res.data.message));
} }

View File

@@ -4,9 +4,13 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { pageUsers } from '@/api/system/user'; import { pageUsers } from '@/api/system/user';
import { pageShopOrder, shopOrderTotal } from '@/api/shop/shopOrder'; 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 { CmsStatistics } from '@/api/cms/cmsStatistics/model';
import { safeNumber, hasValidId, isValidApiResponse } from '@/utils/type-guards'; import { safeNumber, hasValidId } from '@/utils/type-guards';
export interface StatisticsState { export interface StatisticsState {
// 统计数据 // 统计数据
@@ -88,7 +92,7 @@ export const useStatisticsStore = defineStore({
isCacheValid: (state): boolean => { isCacheValid: (state): boolean => {
if (!state.lastUpdateTime) return false; if (!state.lastUpdateTime) return false;
const now = Date.now(); 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; this.loading = true;
try { try {
// 并行获取各种统计数据 // 并行获取各种统计数据使用Promise.allSettled确保部分失败不影响整体
const [users, orders, total, statisticsData] = await Promise.all([ const [usersResult, ordersResult, totalResult, statisticsResult] =
await Promise.allSettled([
pageUsers({}), pageUsers({}),
pageShopOrder({}), pageShopOrder({}),
shopOrderTotal(), shopOrderTotal(),
listCmsStatistics({}) 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; 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) { if (statisticsData && statisticsData.length > 0) {
// 更新现有统计数据 // 更新现有统计数据
const existingStatistics = statisticsData[0]; const existingStatistics = statisticsData[0];
@@ -123,9 +212,9 @@ export const useStatisticsStore = defineStore({
if (hasValidId(existingStatistics)) { if (hasValidId(existingStatistics)) {
const updateData: Partial<CmsStatistics> = { const updateData: Partial<CmsStatistics> = {
id: existingStatistics.id, id: existingStatistics.id,
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0), userCount: userCount,
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0), orderCount: orderCount,
totalSales: safeNumber(total), totalSales: totalSales
}; };
// 异步更新数据库 // 异步更新数据库
@@ -140,17 +229,17 @@ export const useStatisticsStore = defineStore({
} else { } else {
// 如果现有数据无效,使用基础数据 // 如果现有数据无效,使用基础数据
statistics = { statistics = {
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0), userCount: userCount,
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0), orderCount: orderCount,
totalSales: safeNumber(total), totalSales: totalSales
}; };
} }
} else { } else {
// 创建新的统计数据 // 创建新的统计数据
statistics = { statistics = {
userCount: safeNumber(isValidApiResponse(users) ? users.count : 0), userCount: userCount,
orderCount: safeNumber(isValidApiResponse(orders) ? orders.count : 0), orderCount: orderCount,
totalSales: safeNumber(total), totalSales: totalSales
}; };
// 异步保存到数据库 // 异步保存到数据库
@@ -191,6 +280,15 @@ export const useStatisticsStore = defineStore({
this.lastUpdateTime = null; this.lastUpdateTime = null;
}, },
/**
* 强制刷新统计数据
*/
async forceRefresh() {
console.log('🔄 强制刷新统计数据...');
this.clearCache();
return await this.fetchStatistics(true);
},
/** /**
* 设置缓存有效期 * 设置缓存有效期
*/ */

View File

@@ -114,9 +114,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {createVNode, ref} from 'vue'; import {ref} from 'vue';
import {message, Modal} from 'ant-design-vue'; import {message} from 'ant-design-vue';
import {ExclamationCircleOutlined, QrcodeOutlined} from '@ant-design/icons-vue'; import {QrcodeOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro'; import type {EleProTable} from 'ele-admin-pro';
import {toDateString} from 'ele-admin-pro'; import {toDateString} from 'ele-admin-pro';
import type { import type {
@@ -127,8 +127,6 @@ import WebsiteEdit from './components/websiteEdit.vue';
import Qrcode from '@/components/QrCode/index.vue'; import Qrcode from '@/components/QrCode/index.vue';
import { import {
pageCmsWebsite, pageCmsWebsite,
removeCmsWebsite,
removeBatchCmsWebsite,
updateCmsWebsite updateCmsWebsite
} from '@/api/cms/cmsWebsite'; } from '@/api/cms/cmsWebsite';
import type {CmsWebsite, CmsWebsiteParam} from '@/api/cms/cmsWebsite/model'; import type {CmsWebsite, CmsWebsiteParam} from '@/api/cms/cmsWebsite/model';
@@ -148,8 +146,6 @@ const selection = ref<CmsWebsite[]>([]);
const current = ref<CmsWebsite | null>(null); const current = ref<CmsWebsite | null>(null);
// 是否显示编辑弹窗 // 是否显示编辑弹窗
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 是否显示二维码 // 是否显示二维码
const showQrcode = ref(false); const showQrcode = ref(false);
// 二维码内容 // 二维码内容
@@ -265,11 +261,6 @@ const openEdit = (row?: CmsWebsite) => {
showEdit.value = true; showEdit.value = true;
}; };
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
const updateType = (row: CmsWebsite) => { const updateType = (row: CmsWebsite) => {
updateCmsWebsite(row).then((msg) => { updateCmsWebsite(row).then((msg) => {
message.success(msg); message.success(msg);
@@ -286,48 +277,6 @@ const hideShare = () => {
showQrcode.value = false; 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 = () => { const query = () => {
// 查询订单 // 查询订单

View File

@@ -144,7 +144,7 @@
<ShopOutlined/> <ShopOutlined/>
站点管理 站点管理
</a-button> </a-button>
<a-button block @click="$router.push('/website/order')"> <a-button block @click="$router.push('/shopOrder')">
<CalendarOutlined/> <CalendarOutlined/>
订单管理 订单管理
</a-button> </a-button>
@@ -154,7 +154,11 @@
</a-button> </a-button>
<a-button block @click="$router.push('/system/login-record')"> <a-button block @click="$router.push('/system/login-record')">
<FileTextOutlined/> <FileTextOutlined/>
系统日志 登录日志
</a-button>
<a-button block @click="refreshStatistics" :loading="loading">
<ReloadOutlined/>
刷新统计
</a-button> </a-button>
<a-button block @click="clearSiteInfoCache"> <a-button block @click="clearSiteInfoCache">
<ClearOutlined/> <ClearOutlined/>
@@ -183,7 +187,8 @@ import {
AccountBookOutlined, AccountBookOutlined,
FileTextOutlined, FileTextOutlined,
ClearOutlined, ClearOutlined,
MoneyCollectOutlined MoneyCollectOutlined,
ReloadOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import {message} from 'ant-design-vue/es'; import {message} from 'ant-design-vue/es';
import {openNew} from "@/utils/common"; 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 () => { onMounted(async () => {
// 加载网站信息和统计数据 // 加载网站信息和统计数据
try { try {