"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const hono_1 = require("hono"); const auth_1 = require("../middlewares/auth"); const clickhouse_1 = __importDefault(require("../utils/clickhouse")); const supabase_1 = __importDefault(require("../utils/supabase")); const communityRouter = new hono_1.Hono(); // Apply auth middleware to all routes communityRouter.use('*', auth_1.authMiddleware); // 创建新项目 communityRouter.post('/projects', async (c) => { try { const { name, description, start_date, end_date } = await c.req.json(); const user = c.get('user'); if (!name) { return c.json({ error: 'Project name is required' }, 400); } // 在Supabase中创建项目 const { data, error } = await supabase_1.default .from('projects') .insert({ name, description, start_date, end_date, created_by: user.id }) .select() .single(); if (error) { console.error('Error creating project:', error); return c.json({ error: 'Failed to create project' }, 500); } return c.json({ message: 'Project created successfully', project: data }, 201); } catch (error) { console.error('Error creating project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 获取项目列表 communityRouter.get('/projects', async (c) => { try { const user = c.get('user'); // 从Supabase获取项目列表 const { data, error } = await supabase_1.default .from('projects') .select('*') .eq('created_by', user.id) .order('created_at', { ascending: false }); if (error) { console.error('Error fetching projects:', error); return c.json({ error: 'Failed to fetch projects' }, 500); } return c.json(data || []); } catch (error) { console.error('Error fetching projects:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 获取项目详情 communityRouter.get('/projects/:id', async (c) => { try { const projectId = c.req.param('id'); // 从Supabase获取项目详情 const { data, error } = await supabase_1.default .from('projects') .select('*') .eq('id', projectId) .single(); if (error) { console.error('Error fetching project:', error); return c.json({ error: 'Failed to fetch project' }, 500); } if (!data) { return c.json({ error: 'Project not found' }, 404); } return c.json(data); } catch (error) { console.error('Error fetching project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 更新项目 communityRouter.put('/projects/:id', async (c) => { try { const projectId = c.req.param('id'); const { name, description, start_date, end_date, status } = await c.req.json(); const user = c.get('user'); // 检查项目是否存在并属于当前用户 const { data: existingProject, error: fetchError } = await supabase_1.default .from('projects') .select('*') .eq('id', projectId) .eq('created_by', user.id) .single(); if (fetchError || !existingProject) { return c.json({ error: 'Project not found or you do not have permission to update it' }, 404); } // 更新项目 const { data, error } = await supabase_1.default .from('projects') .update({ name, description, start_date, end_date, status, updated_at: new Date().toISOString() }) .eq('id', projectId) .select() .single(); if (error) { console.error('Error updating project:', error); return c.json({ error: 'Failed to update project' }, 500); } return c.json({ message: 'Project updated successfully', project: data }); } catch (error) { console.error('Error updating project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 删除项目 communityRouter.delete('/projects/:id', async (c) => { try { const projectId = c.req.param('id'); const user = c.get('user'); // 检查项目是否存在并属于当前用户 const { data: existingProject, error: fetchError } = await supabase_1.default .from('projects') .select('*') .eq('id', projectId) .eq('created_by', user.id) .single(); if (fetchError || !existingProject) { return c.json({ error: 'Project not found or you do not have permission to delete it' }, 404); } // 删除项目 const { error } = await supabase_1.default .from('projects') .delete() .eq('id', projectId); if (error) { console.error('Error deleting project:', error); return c.json({ error: 'Failed to delete project' }, 500); } return c.json({ message: 'Project deleted successfully' }); } catch (error) { console.error('Error deleting project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 添加影响者到项目 communityRouter.post('/projects/:id/influencers', async (c) => { try { const projectId = c.req.param('id'); const { influencer_id, platform, external_id, name, profile_url } = await c.req.json(); const user = c.get('user'); // 检查项目是否存在并属于当前用户 const { data: existingProject, error: fetchError } = await supabase_1.default .from('projects') .select('*') .eq('id', projectId) .eq('created_by', user.id) .single(); if (fetchError || !existingProject) { return c.json({ error: 'Project not found or you do not have permission to update it' }, 404); } // 检查影响者是否已存在 let influencerData; if (influencer_id) { // 如果提供了影响者ID,检查是否存在 const { data, error } = await supabase_1.default .from('influencers') .select('*') .eq('influencer_id', influencer_id) .single(); if (!error && data) { influencerData = data; } } else if (external_id && platform) { // 如果提供了外部ID和平台,检查是否存在 const { data, error } = await supabase_1.default .from('influencers') .select('*') .eq('external_id', external_id) .eq('platform', platform) .single(); if (!error && data) { influencerData = data; } } // 如果影响者不存在,创建新的影响者 if (!influencerData) { if (!name || !platform) { return c.json({ error: 'Name and platform are required for new influencers' }, 400); } const { data, error } = await supabase_1.default .from('influencers') .insert({ name, platform, external_id, profile_url }) .select() .single(); if (error) { console.error('Error creating influencer:', error); return c.json({ error: 'Failed to create influencer' }, 500); } influencerData = data; } // 将影响者添加到项目 const { data: projectInfluencer, error } = await supabase_1.default .from('project_influencers') .insert({ project_id: projectId, influencer_id: influencerData.influencer_id }) .select() .single(); if (error) { console.error('Error adding influencer to project:', error); return c.json({ error: 'Failed to add influencer to project' }, 500); } return c.json({ message: 'Influencer added to project successfully', project_influencer: projectInfluencer, influencer: influencerData }, 201); } catch (error) { console.error('Error adding influencer to project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 获取项目的影响者列表 communityRouter.get('/projects/:id/influencers', async (c) => { try { const projectId = c.req.param('id'); // 从Supabase获取项目的影响者列表 const { data, error } = await supabase_1.default .from('project_influencers') .select(` project_id, influencers ( influencer_id, name, platform, profile_url, external_id, followers_count, video_count ) `) .eq('project_id', projectId); if (error) { console.error('Error fetching project influencers:', error); return c.json({ error: 'Failed to fetch project influencers' }, 500); } // 格式化数据 const influencers = data?.map(item => item.influencers) || []; return c.json(influencers); } catch (error) { console.error('Error fetching project influencers:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 从项目中移除影响者 communityRouter.delete('/projects/:projectId/influencers/:influencerId', async (c) => { try { const projectId = c.req.param('projectId'); const influencerId = c.req.param('influencerId'); const user = c.get('user'); // 检查项目是否存在并属于当前用户 const { data: existingProject, error: fetchError } = await supabase_1.default .from('projects') .select('*') .eq('id', projectId) .eq('created_by', user.id) .single(); if (fetchError || !existingProject) { return c.json({ error: 'Project not found or you do not have permission to update it' }, 404); } // 从项目中移除影响者 const { error } = await supabase_1.default .from('project_influencers') .delete() .eq('project_id', projectId) .eq('influencer_id', influencerId); if (error) { console.error('Error removing influencer from project:', error); return c.json({ error: 'Failed to remove influencer from project' }, 500); } return c.json({ message: 'Influencer removed from project successfully' }); } catch (error) { console.error('Error removing influencer from project:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 添加事件数据 communityRouter.post('/events', async (c) => { try { const { project_id, influencer_id, post_id, platform, event_type, metric_value, event_metadata } = await c.req.json(); if (!project_id || !influencer_id || !platform || !event_type || metric_value === undefined) { return c.json({ error: 'Project ID, influencer ID, platform, event type, and metric value are required' }, 400); } // 验证事件类型 const validEventTypes = [ 'follower_change', 'post_like_change', 'post_view_change', 'click', 'comment', 'share' ]; if (!validEventTypes.includes(event_type)) { return c.json({ error: `Invalid event type. Must be one of: ${validEventTypes.join(', ')}` }, 400); } // 验证平台 const validPlatforms = ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']; if (!validPlatforms.includes(platform)) { return c.json({ error: `Invalid platform. Must be one of: ${validPlatforms.join(', ')}` }, 400); } // 将事件数据插入ClickHouse await clickhouse_1.default.query({ query: ` INSERT INTO events ( project_id, influencer_id, post_id, platform, event_type, metric_value, event_metadata ) VALUES (?, ?, ?, ?, ?, ?, ?) `, values: [ project_id, influencer_id, post_id || null, platform, event_type, metric_value, event_metadata ? JSON.stringify(event_metadata) : '{}' ] }); return c.json({ message: 'Event data added successfully' }, 201); } catch (error) { console.error('Error adding event data:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 批量添加事件数据 communityRouter.post('/events/batch', async (c) => { try { const { events } = await c.req.json(); if (!Array.isArray(events) || events.length === 0) { return c.json({ error: 'Events array is required and must not be empty' }, 400); } // 验证事件类型和平台 const validEventTypes = [ 'follower_change', 'post_like_change', 'post_view_change', 'click', 'comment', 'share' ]; const validPlatforms = ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']; // 验证每个事件 for (const event of events) { const { project_id, influencer_id, platform, event_type, metric_value } = event; if (!project_id || !influencer_id || !platform || !event_type || metric_value === undefined) { return c.json({ error: 'Project ID, influencer ID, platform, event type, and metric value are required for all events' }, 400); } if (!validEventTypes.includes(event_type)) { return c.json({ error: `Invalid event type: ${event_type}. Must be one of: ${validEventTypes.join(', ')}` }, 400); } if (!validPlatforms.includes(platform)) { return c.json({ error: `Invalid platform: ${platform}. Must be one of: ${validPlatforms.join(', ')}` }, 400); } } // 准备批量插入数据 const values = events.map(event => `( '${event.project_id}', '${event.influencer_id}', ${event.post_id ? `'${event.post_id}'` : 'NULL'}, '${event.platform}', '${event.event_type}', ${event.metric_value}, '${event.event_metadata ? JSON.stringify(event.event_metadata) : '{}'}' )`).join(','); // 批量插入事件数据 await clickhouse_1.default.query({ query: ` INSERT INTO events ( project_id, influencer_id, post_id, platform, event_type, metric_value, event_metadata ) VALUES ${values} ` }); return c.json({ message: `${events.length} events added successfully` }, 201); } catch (error) { console.error('Error adding batch event data:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 添加帖子 communityRouter.post('/posts', async (c) => { try { const { influencer_id, platform, post_url, title, description, published_at } = await c.req.json(); if (!influencer_id || !platform || !post_url) { return c.json({ error: 'Influencer ID, platform, and post URL are required' }, 400); } // 验证平台 const validPlatforms = ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']; if (!validPlatforms.includes(platform)) { return c.json({ error: `Invalid platform. Must be one of: ${validPlatforms.join(', ')}` }, 400); } // 检查帖子是否已存在 const { data: existingPost, error: checkError } = await supabase_1.default .from('posts') .select('*') .eq('post_url', post_url) .single(); if (!checkError && existingPost) { return c.json({ error: 'Post with this URL already exists', post: existingPost }, 409); } // 创建新帖子 const { data, error } = await supabase_1.default .from('posts') .insert({ influencer_id, platform, post_url, title, description, published_at: published_at || new Date().toISOString() }) .select() .single(); if (error) { console.error('Error creating post:', error); return c.json({ error: 'Failed to create post' }, 500); } return c.json({ message: 'Post created successfully', post: data }, 201); } catch (error) { console.error('Error creating post:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 添加评论 communityRouter.post('/comments', async (c) => { try { const { post_id, user_id, content, sentiment_score } = await c.req.json(); if (!post_id || !content) { return c.json({ error: 'Post ID and content are required' }, 400); } // 创建新评论 const { data, error } = await supabase_1.default .from('comments') .insert({ post_id, user_id: user_id || c.get('user').id, content, sentiment_score: sentiment_score || 0 }) .select() .single(); if (error) { console.error('Error creating comment:', error); return c.json({ error: 'Failed to create comment' }, 500); } return c.json({ message: 'Comment created successfully', comment: data }, 201); } catch (error) { console.error('Error creating comment:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 获取项目的事件统计 communityRouter.get('/projects/:id/event-stats', async (c) => { try { const projectId = c.req.param('id'); // 从ClickHouse查询项目的事件统计 const result = await clickhouse_1.default.query({ query: ` SELECT event_type, COUNT(*) AS event_count, SUM(metric_value) AS total_value FROM events WHERE project_id = ? GROUP BY event_type ORDER BY event_count DESC `, values: [projectId] }); // 提取数据 const statsData = 'rows' in result ? result.rows : []; return c.json({ project_id: projectId, event_stats: statsData }); } catch (error) { console.error('Error fetching event stats:', error); return c.json({ error: 'Internal server error' }, 500); } }); // 获取项目的时间趋势 communityRouter.get('/projects/:id/time-trend', async (c) => { try { const projectId = c.req.param('id'); const { event_type, interval = 'day', days = '30' } = c.req.query(); if (!event_type) { return c.json({ error: 'Event type is required' }, 400); } // 验证事件类型 const validEventTypes = [ 'follower_change', 'post_like_change', 'post_view_change', 'click', 'comment', 'share' ]; if (!validEventTypes.includes(event_type)) { return c.json({ error: `Invalid event type. Must be one of: ${validEventTypes.join(', ')}` }, 400); } // 验证时间间隔 const validIntervals = ['hour', 'day', 'week', 'month']; if (!validIntervals.includes(interval)) { return c.json({ error: `Invalid interval. Must be one of: ${validIntervals.join(', ')}` }, 400); } // 构建时间间隔函数 let timeFunction; switch (interval) { case 'hour': timeFunction = 'toStartOfHour'; break; case 'day': timeFunction = 'toDate'; break; case 'week': timeFunction = 'toStartOfWeek'; break; case 'month': timeFunction = 'toStartOfMonth'; break; } // 从ClickHouse查询项目的时间趋势 const result = await clickhouse_1.default.query({ query: ` SELECT ${timeFunction}(timestamp) AS time_period, SUM(metric_value) AS value FROM events WHERE project_id = ? AND event_type = ? AND timestamp >= subtractDays(now(), ?) GROUP BY time_period ORDER BY time_period ASC `, values: [projectId, event_type, parseInt(days)] }); // 提取数据 const trendData = 'rows' in result ? result.rows : []; return c.json({ project_id: projectId, event_type, interval, days: parseInt(days), trend: trendData }); } catch (error) { console.error('Error fetching time trend:', error); return c.json({ error: 'Internal server error' }, 500); } }); exports.default = communityRouter;