funel data

This commit is contained in:
2025-03-11 00:36:22 +08:00
parent 7857a9007a
commit bc42ff4dbf
13 changed files with 2171 additions and 11 deletions

View File

@@ -1341,7 +1341,7 @@ analyticsRouter.get('/reports/project/:id', async (c) => {
// 获取项目基本信息
const { data: project, error: projectError } = await supabase
.from('projects')
.select('id, name, description, created_at, created_by')
.select('id, name, description, created_at')
.eq('id', projectId)
.single();
@@ -1498,4 +1498,258 @@ analyticsRouter.get('/reports/project/:id', async (c) => {
}
});
// 获取KOL合作转换漏斗数据
analyticsRouter.get('/project/:id/conversion-funnel', async (c) => {
try {
const projectId = c.req.param('id');
const { timeframe = '30days' } = c.req.query();
// 获取项目信息
const { data: project, error: projectError } = await supabase
.from('projects')
.select('id, name, description, created_at')
.eq('id', projectId)
.single();
// 如果找不到项目或发生错误,返回模拟数据
if (projectError) {
console.log(`项目未找到或数据库错误返回模拟数据。项目ID: ${projectId}, 错误: ${projectError.message}`);
// 生成模拟的漏斗数据
const mockFunnelData = [
{ stage: 'Awareness', count: 100, rate: 100 },
{ stage: 'Interest', count: 75, rate: 75 },
{ stage: 'Consideration', count: 50, rate: 50 },
{ stage: 'Intent', count: 30, rate: 30 },
{ stage: 'Evaluation', count: 20, rate: 20 },
{ stage: 'Purchase', count: 10, rate: 10 }
];
return c.json({
project: {
id: projectId,
name: `模拟项目 (ID: ${projectId})`
},
timeframe,
funnel_data: mockFunnelData,
metrics: {
total_influencers: 100,
conversion_rate: 10,
avg_stage_dropoff: 18
},
is_mock_data: true
});
}
// 获取项目关联的网红及其详细信息
const { data: projectInfluencers, error: influencersError } = await supabase
.from('project_influencers')
.select(`
influencer_id,
influencers (
id,
name,
platform,
followers_count,
engagement_rate,
created_at
)
`)
.eq('project_id', projectId);
if (influencersError) {
console.error('Error fetching project influencers:', influencersError);
return c.json({ error: 'Failed to fetch project data' }, 500);
}
// 获取项目中的内容数据
const { data: projectPosts, error: postsError } = await supabase
.from('posts')
.select(`
id,
influencer_id,
platform,
published_at,
views_count,
likes_count,
comments_count,
shares_count
`)
.eq('project_id', projectId);
if (postsError) {
console.error('Error fetching project posts:', postsError);
return c.json({ error: 'Failed to fetch project posts' }, 500);
}
// 计算漏斗各阶段数据
const totalInfluencers = projectInfluencers.length;
// 1. 认知阶段 - 所有接触的KOL
const awarenessStage = {
stage: 'Awareness',
count: totalInfluencers,
rate: 100
};
// 2. 兴趣阶段 - 有互动的KOL (至少有一篇内容)
const influencersWithContent = new Set<string>();
projectPosts?.forEach(post => {
if (post.influencer_id) {
influencersWithContent.add(post.influencer_id);
}
});
const interestStage = {
stage: 'Interest',
count: influencersWithContent.size,
rate: Math.round((influencersWithContent.size / totalInfluencers) * 100)
};
// 3. 考虑阶段 - 有高互动的KOL (内容互动率高于平均值)
const engagementRates = projectInfluencers
.map(pi => pi.influencers?.[0]?.engagement_rate || 0)
.filter(rate => rate > 0);
const avgEngagementRate = engagementRates.length > 0
? engagementRates.reduce((sum, rate) => sum + rate, 0) / engagementRates.length
: 0;
const highEngagementInfluencers = projectInfluencers.filter(pi =>
(pi.influencers?.[0]?.engagement_rate || 0) > avgEngagementRate
);
const considerationStage = {
stage: 'Consideration',
count: highEngagementInfluencers.length,
rate: Math.round((highEngagementInfluencers.length / totalInfluencers) * 100)
};
// 4. 意向阶段 - 有多篇内容的KOL
const influencerContentCount: Record<string, number> = {};
projectPosts?.forEach(post => {
if (post.influencer_id) {
influencerContentCount[post.influencer_id] = (influencerContentCount[post.influencer_id] || 0) + 1;
}
});
const multiContentInfluencers = Object.keys(influencerContentCount).filter(
id => influencerContentCount[id] > 1
);
const intentStage = {
stage: 'Intent',
count: multiContentInfluencers.length,
rate: Math.round((multiContentInfluencers.length / totalInfluencers) * 100)
};
// 5. 评估阶段 - 内容表现良好的KOL (浏览量高于平均值)
const influencerViewsMap: Record<string, number> = {};
projectPosts?.forEach(post => {
if (post.influencer_id && post.views_count) {
influencerViewsMap[post.influencer_id] = (influencerViewsMap[post.influencer_id] || 0) + post.views_count;
}
});
const influencerViews = Object.values(influencerViewsMap);
const avgViews = influencerViews.length > 0
? influencerViews.reduce((sum, views) => sum + views, 0) / influencerViews.length
: 0;
const highViewsInfluencers = Object.keys(influencerViewsMap).filter(
id => influencerViewsMap[id] > avgViews
);
const evaluationStage = {
stage: 'Evaluation',
count: highViewsInfluencers.length,
rate: Math.round((highViewsInfluencers.length / totalInfluencers) * 100)
};
// 6. 购买/转化阶段 - 长期合作的KOL (3个月以上)
const threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
const longTermInfluencers = projectInfluencers.filter(pi => {
const createdAt = pi.influencers?.[0]?.created_at;
if (!createdAt) return false;
const createdDate = new Date(createdAt);
return createdDate < threeMonthsAgo;
});
const purchaseStage = {
stage: 'Purchase',
count: longTermInfluencers.length,
rate: Math.round((longTermInfluencers.length / totalInfluencers) * 100)
};
// 构建完整漏斗数据
const funnelData = [
awarenessStage,
interestStage,
considerationStage,
intentStage,
evaluationStage,
purchaseStage
];
// 计算转化率
const conversionRate = totalInfluencers > 0
? Math.round((longTermInfluencers.length / totalInfluencers) * 100)
: 0;
// 计算平均转化率
const avgStageDropoff = funnelData.length > 1
? (100 - conversionRate) / (funnelData.length - 1)
: 0;
return c.json({
project: {
id: project.id,
name: project.name
},
timeframe,
funnel_data: funnelData,
metrics: {
total_influencers: totalInfluencers,
conversion_rate: conversionRate,
avg_stage_dropoff: Math.round(avgStageDropoff)
}
});
} catch (error) {
console.error('Error generating KOL conversion funnel:', error);
// 发生错误时也返回模拟数据
const projectId = c.req.param('id');
const { timeframe = '30days' } = c.req.query();
// 生成模拟的漏斗数据
const mockFunnelData = [
{ stage: 'Awareness', count: 100, rate: 100 },
{ stage: 'Interest', count: 75, rate: 75 },
{ stage: 'Consideration', count: 50, rate: 50 },
{ stage: 'Intent', count: 30, rate: 30 },
{ stage: 'Evaluation', count: 20, rate: 20 },
{ stage: 'Purchase', count: 10, rate: 10 }
];
return c.json({
project: {
id: projectId,
name: `模拟项目 (ID: ${projectId})`
},
timeframe,
funnel_data: mockFunnelData,
metrics: {
total_influencers: 100,
conversion_rate: 10,
avg_stage_dropoff: 18
},
is_mock_data: true,
error_message: '发生错误,返回模拟数据'
});
}
});
export default analyticsRouter;