117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import clickhouse from '@/lib/clickhouse';
|
|
import type { ApiResponse } from '@/lib/types';
|
|
|
|
interface UtmData {
|
|
utm_value: string;
|
|
clicks: number;
|
|
visitors: number;
|
|
avg_time_spent: number;
|
|
bounces: number;
|
|
conversions: number;
|
|
}
|
|
|
|
// 格式化日期时间字符串为ClickHouse支持的格式
|
|
const formatDateTime = (dateStr: string): string => {
|
|
return dateStr.replace('T', ' ').replace('Z', '');
|
|
};
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const searchParams = request.nextUrl.searchParams;
|
|
|
|
// 获取过滤参数
|
|
const startTime = searchParams.get('startTime');
|
|
const endTime = searchParams.get('endTime');
|
|
const linkId = searchParams.get('linkId');
|
|
|
|
// 获取UTM类型参数
|
|
const utmType = searchParams.get('utmType') || 'source';
|
|
|
|
// 构建WHERE子句
|
|
let whereClause = '';
|
|
const conditions = [];
|
|
|
|
if (startTime) {
|
|
conditions.push(`event_time >= toDateTime('${formatDateTime(startTime)}')`);
|
|
}
|
|
|
|
if (endTime) {
|
|
conditions.push(`event_time <= toDateTime('${formatDateTime(endTime)}')`);
|
|
}
|
|
|
|
if (linkId) {
|
|
conditions.push(`link_id = '${linkId}'`);
|
|
}
|
|
|
|
if (conditions.length > 0) {
|
|
whereClause = `WHERE ${conditions.join(' AND ')}`;
|
|
}
|
|
|
|
// 确定要分组的UTM字段
|
|
let utmField;
|
|
switch (utmType) {
|
|
case 'source':
|
|
utmField = 'utm_source';
|
|
break;
|
|
case 'medium':
|
|
utmField = 'utm_medium';
|
|
break;
|
|
case 'campaign':
|
|
utmField = 'utm_campaign';
|
|
break;
|
|
case 'term':
|
|
utmField = 'utm_term';
|
|
break;
|
|
case 'content':
|
|
utmField = 'utm_content';
|
|
break;
|
|
default:
|
|
utmField = 'utm_source';
|
|
}
|
|
|
|
// 构建SQL查询
|
|
const query = `
|
|
SELECT
|
|
${utmField} AS utm_value,
|
|
COUNT(*) AS clicks,
|
|
uniqExact(visitor_id) AS visitors,
|
|
round(AVG(time_spent_sec), 2) AS avg_time_spent,
|
|
countIf(is_bounce = 1) AS bounces,
|
|
countIf(conversion_type IN ('visit', 'stay', 'interact', 'signup', 'subscription', 'purchase')) AS conversions
|
|
FROM shorturl_analytics.events
|
|
${whereClause}
|
|
${whereClause ? 'AND' : 'WHERE'} ${utmField} != ''
|
|
GROUP BY utm_value
|
|
ORDER BY clicks DESC
|
|
LIMIT 100
|
|
`;
|
|
|
|
// 执行查询
|
|
const result = await clickhouse.query({
|
|
query,
|
|
format: 'JSONEachRow',
|
|
});
|
|
|
|
// 获取查询结果
|
|
const rows = await result.json();
|
|
const data = rows as UtmData[];
|
|
|
|
// 返回数据
|
|
const response: ApiResponse<UtmData[]> = {
|
|
success: true,
|
|
data
|
|
};
|
|
|
|
return NextResponse.json(response);
|
|
} catch (error) {
|
|
console.error('Error fetching UTM data:', error);
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
|
};
|
|
|
|
return NextResponse.json(response, { status: 500 });
|
|
}
|
|
} |