This commit is contained in:
2025-03-25 20:54:02 +08:00
parent 92d82b18a0
commit efdfe8bf8e
17 changed files with 1553 additions and 235 deletions

View File

@@ -91,57 +91,71 @@ export async function getEventsSummary(params: {
ORDER BY count DESC
`;
const [baseResult, browserResults, osResults] = await Promise.all([
executeQuerySingle<{
totalEvents: number;
uniqueVisitors: number;
totalConversions: number;
averageTimeSpent: number;
mobileCount: number;
desktopCount: number;
tabletCount: number;
otherCount: number;
}>(baseQuery),
executeQuery<{ name: string; count: number }>(browserQuery),
executeQuery<{ name: string; count: number }>(osQuery)
]);
if (!baseResult) {
throw new Error('Failed to get events summary');
try {
const [baseResult, browserResults, osResults] = await Promise.all([
executeQuerySingle<{
totalEvents: number;
uniqueVisitors: number;
totalConversions: number;
averageTimeSpent: number;
mobileCount: number;
desktopCount: number;
tabletCount: number;
otherCount: number;
}>(baseQuery),
executeQuery<{ name: string; count: number }>(browserQuery),
executeQuery<{ name: string; count: number }>(osQuery)
]);
if (!baseResult) {
throw new Error('Failed to get events summary');
}
// 安全转换数字类型
const safeNumber = (value: any): number => {
if (value === null || value === undefined) return 0;
const num = Number(value);
return isNaN(num) ? 0 : num;
};
// 计算百分比
const calculatePercentage = (count: number, total: number) => {
if (!total) return 0; // 防止除以零
return Number(((count / total) * 100).toFixed(2));
};
// 处理浏览器数据
const browsers = browserResults.map(item => ({
name: item.name || 'Unknown',
count: safeNumber(item.count),
percentage: calculatePercentage(safeNumber(item.count), safeNumber(baseResult.totalEvents))
}));
// 处理操作系统数据
const operatingSystems = osResults.map(item => ({
name: item.name || 'Unknown',
count: safeNumber(item.count),
percentage: calculatePercentage(safeNumber(item.count), safeNumber(baseResult.totalEvents))
}));
return {
totalEvents: safeNumber(baseResult.totalEvents),
uniqueVisitors: safeNumber(baseResult.uniqueVisitors),
totalConversions: safeNumber(baseResult.totalConversions),
averageTimeSpent: baseResult.averageTimeSpent ? Number(baseResult.averageTimeSpent.toFixed(2)) : 0,
deviceTypes: {
mobile: safeNumber(baseResult.mobileCount),
desktop: safeNumber(baseResult.desktopCount),
tablet: safeNumber(baseResult.tabletCount),
other: safeNumber(baseResult.otherCount)
},
browsers,
operatingSystems
};
} catch (error) {
console.error('Error in getEventsSummary:', error);
throw error;
}
// 计算百分比
const calculatePercentage = (count: number, total: number) =>
Number(((count / total) * 100).toFixed(2));
// 处理浏览器数据
const browsers = browserResults.map(item => ({
name: item.name,
count: item.count,
percentage: calculatePercentage(item.count, baseResult.totalEvents)
}));
// 处理操作系统数据
const operatingSystems = osResults.map(item => ({
name: item.name,
count: item.count,
percentage: calculatePercentage(item.count, baseResult.totalEvents)
}));
return {
totalEvents: baseResult.totalEvents,
uniqueVisitors: baseResult.uniqueVisitors,
totalConversions: baseResult.totalConversions,
averageTimeSpent: Number(baseResult.averageTimeSpent.toFixed(2)),
deviceTypes: {
mobile: baseResult.mobileCount,
desktop: baseResult.desktopCount,
tablet: baseResult.tabletCount,
other: baseResult.otherCount
},
browsers,
operatingSystems
};
}
// 获取时间序列数据
@@ -263,8 +277,10 @@ export async function getDeviceAnalytics(params: {
}
// 计算百分比
const calculatePercentage = (count: number) =>
Number(((count / totalResult.total) * 100).toFixed(2));
const calculatePercentage = (count: number) => {
if (!totalResult || totalResult.total === 0) return 0;
return Number(((count / totalResult.total) * 100).toFixed(2));
};
return {
deviceTypes: deviceTypes.map(item => ({

View File

@@ -3,10 +3,10 @@ import type { EventsQueryParams } from './types';
// ClickHouse 客户端配置
const clickhouse = createClient({
url: process.env.CLICKHOUSE_URL || 'http://localhost:8123',
username: process.env.CLICKHOUSE_USER || 'admin',
password: process.env.CLICKHOUSE_PASSWORD || 'your_secure_password',
database: process.env.CLICKHOUSE_DB || 'shorturl_analytics'
url: process.env.CLICKHOUSE_URL,
username: process.env.CLICKHOUSE_USER ,
password: process.env.CLICKHOUSE_PASSWORD ,
database: process.env.CLICKHOUSE_DATABASE
});
// 构建日期过滤条件