From 364a9dfc41834e8c4a36c1b2d333eef08e3728de Mon Sep 17 00:00:00 2001 From: William Tso Date: Fri, 14 Mar 2025 19:15:12 +0800 Subject: [PATCH] rm mock data --- .../src/controllers/analyticsController.ts | 255 ++++++------------ backend/src/services/analyticsService.ts | 94 +------ web/src/components/Analytics.tsx | 14 +- 3 files changed, 94 insertions(+), 269 deletions(-) diff --git a/backend/src/controllers/analyticsController.ts b/backend/src/controllers/analyticsController.ts index ad002ea..22fac4c 100644 --- a/backend/src/controllers/analyticsController.ts +++ b/backend/src/controllers/analyticsController.ts @@ -192,159 +192,111 @@ export class AnalyticsController { * @param c Hono Context * @returns Response with post performance data */ - async getPostPerformance(c: Context) { - const requestId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`; + async getPostPerformance(c: Context): Promise { const startTime = Date.now(); - + // 解析查询参数 + const kolId = c.req.query('kolId'); + const platform = c.req.query('platform'); + const startDate = c.req.query('startDate'); + const endDate = c.req.query('endDate'); + const sortBy = c.req.query('sortBy'); + const sortOrder = c.req.query('sortOrder'); + const limit = c.req.query('limit'); + const offset = c.req.query('offset'); + + // 记录请求参数 + logger.info('Post performance data requested', { + kolId, platform, startDate, endDate, + sortBy, sortOrder, limit, offset + }); + try { - // Get query parameters - const kolId = c.req.query('kolId'); // Optional KOL filter - const platform = c.req.query('platform'); // Optional platform filter - const startDate = c.req.query('startDate'); // Optional start date - const endDate = c.req.query('endDate'); // Optional end date - const sortBy = c.req.query('sortBy') || 'publish_date'; // Default sort by publish date - const sortOrder = c.req.query('sortOrder') || 'desc'; // Default to descending order - const limit = parseInt(c.req.query('limit') || '20', 10); // Default limit to 20 posts - const offset = parseInt(c.req.query('offset') || '0', 10); // Default offset to 0 - const useMockData = c.req.query('useMockData') === 'true'; // 允许用户强制使用模拟数据 - - logger.info(`[${requestId}] Post performance request received`, { - kolId, - platform, - startDate, - endDate, - sortBy, - sortOrder, - limit, - offset, - useMockData, - userAgent: c.req.header('user-agent'), - ip: c.req.header('x-forwarded-for') || 'unknown' - }); - - // 如果强制使用模拟数据,直接生成并返回 - if (useMockData) { - logger.info(`[${requestId}] Using mock data as requested`); - const mockPosts = this.generateMockPostData(limit, platform, kolId); - - return c.json({ - success: true, - data: mockPosts, - pagination: { - limit, - offset, - total: 100 // 模拟总数 - }, - is_mock_data: true - }); - } - - // Validate sort order - if (!['asc', 'desc'].includes(sortOrder)) { - logger.warn(`[${requestId}] Invalid sortOrder: ${sortOrder}`); - return c.json({ - success: false, - error: 'Invalid sortOrder. Must be asc or desc.' - }, 400); - } - - // Validate sort field - const validSortFields = ['publish_date', 'views', 'likes', 'comments', 'shares', 'sentiment_score']; - if (!validSortFields.includes(sortBy)) { - logger.warn(`[${requestId}] Invalid sortBy: ${sortBy}`); - return c.json({ - success: false, - error: `Invalid sortBy. Must be one of: ${validSortFields.join(', ')}` - }, 400); - } - - // Validate date formats if provided + // 验证时间范围 if (startDate && !this.isValidDateFormat(startDate)) { - logger.warn(`[${requestId}] Invalid startDate format: ${startDate}`); - return c.json({ + return c.json({ success: false, - error: 'Invalid startDate format. Use YYYY-MM-DD.' + error: 'Invalid startDate format. Expected YYYY-MM-DD' }, 400); } if (endDate && !this.isValidDateFormat(endDate)) { - logger.warn(`[${requestId}] Invalid endDate format: ${endDate}`); - return c.json({ + return c.json({ success: false, - error: 'Invalid endDate format. Use YYYY-MM-DD.' + error: 'Invalid endDate format. Expected YYYY-MM-DD' }, 400); } - // Get post performance data from service - const data = await analyticsService.getPostPerformance( - kolId, - platform, - startDate, - endDate, - sortBy, - sortOrder, - limit, - offset + // 验证排序字段 + const validSortFields = ['publish_date', 'views', 'likes', 'comments', 'shares', 'sentiment_score']; + if (sortBy && !validSortFields.includes(sortBy)) { + return c.json({ + success: false, + error: `Invalid sortBy field. Expected one of: ${validSortFields.join(', ')}` + }, 400); + } + + // 验证排序顺序 + if (sortOrder && !['asc', 'desc', 'ASC', 'DESC'].includes(sortOrder)) { + return c.json({ + success: false, + error: 'Invalid sortOrder. Expected: asc or desc' + }, 400); + } + + // 验证限制数量和偏移量 + const parsedLimit = limit ? parseInt(limit, 10) : 10; + if (isNaN(parsedLimit) || parsedLimit < 1 || parsedLimit > 100) { + return c.json({ + success: false, + error: 'Invalid limit. Expected a number between 1 and 100' + }, 400); + } + + const parsedOffset = offset ? parseInt(offset, 10) : 0; + if (isNaN(parsedOffset) || parsedOffset < 0) { + return c.json({ + success: false, + error: 'Invalid offset. Expected a non-negative number' + }, 400); + } + + // 获取帖文表现数据 + const { posts, total } = await analyticsService.getPostPerformance( + kolId || undefined, + platform || undefined, + startDate || undefined, + endDate || undefined, + sortBy || 'publish_date', + sortOrder || 'desc', + parsedLimit, + parsedOffset ); - // 检查返回的数据是否包含真实数据(通过检查post_id的格式) - const realDataCount = data.posts.filter(post => - !post.post_id.startsWith('mock-') - ).length; - - const isMockData = realDataCount === 0 && data.posts.length > 0; - - // Log successful response + // 返回结果 const duration = Date.now() - startTime; - logger.info(`[${requestId}] Post performance response sent successfully`, { - duration, - resultCount: data.posts.length, - totalPosts: data.total, - realDataCount, - mockDataCount: data.posts.length - realDataCount, - isMockData + logger.info(`Post performance data returned (${duration}ms)`, { + totalPosts: total, + returnedPosts: posts.length }); - // Return the data return c.json({ success: true, - data: data.posts, - pagination: { - limit, - offset, - total: data.total - }, - is_mock_data: isMockData + data: posts, + meta: { + total, + returned: posts.length, + limit: parsedLimit, + offset: parsedOffset + } }); } catch (error) { - // Log error const duration = Date.now() - startTime; - logger.error(`[${requestId}] Error fetching post performance data (${duration}ms)`, error); - - try { - // 发生错误时尝试返回模拟数据 - const mockPosts = this.generateMockPostData(20); - logger.info(`[${requestId}] Returning mock data due to error`); - - return c.json({ - success: true, - data: mockPosts, - pagination: { - limit: 20, - offset: 0, - total: 100 - }, - is_mock_data: true, - original_error: error instanceof Error ? error.message : 'Unknown error' - }); - } catch (mockError) { - // 如果连模拟数据生成都失败,返回错误响应 - return c.json({ - success: false, - error: 'Failed to fetch post performance data', - message: error instanceof Error ? error.message : 'Unknown error' - }, 500); - } + logger.error(`Error getting post performance data (${duration}ms)`, error); + + return c.json({ + success: false, + error: 'Failed to retrieve post performance data' + }, 500); } } @@ -361,47 +313,6 @@ export class AnalyticsController { return date instanceof Date && !isNaN(date.getTime()); } - /** - * 生成模拟贴文数据 - */ - private generateMockPostData(count: number, platform?: string, kolId?: string): any[] { - const platforms = platform ? [platform] : ['instagram', 'youtube', 'tiktok', 'facebook', 'twitter']; - const kolIds = kolId ? [kolId] : Array.from({length: 10}, (_, i) => `mock-kol-${i+1}`); - const kolNames = Array.from({length: 10}, (_, i) => `模拟KOL ${i+1}`); - - return Array.from({length: count}, (_, i) => { - const selectedPlatform = platforms[Math.floor(Math.random() * platforms.length)]; - const kolIndex = Math.floor(Math.random() * kolIds.length); - const selectedKolId = kolIds[kolIndex]; - const selectedKolName = kolId ? `指定KOL` : kolNames[kolIndex % kolNames.length]; - - const publishDate = new Date(); - publishDate.setDate(publishDate.getDate() - Math.floor(Math.random() * 90)); - - const views = Math.floor(Math.random() * 10000) + 1000; - const likes = Math.floor(views * (Math.random() * 0.2 + 0.05)); - const comments = Math.floor(likes * (Math.random() * 0.2 + 0.02)); - const shares = Math.floor(likes * (Math.random() * 0.1 + 0.01)); - - return { - post_id: `mock-post-${i+1}`, - title: `模拟贴文 ${i+1} (${selectedPlatform})`, - kol_id: selectedKolId, - kol_name: selectedKolName, - platform: selectedPlatform, - publish_date: publishDate.toISOString(), - metrics: { - views, - likes, - comments, - shares - }, - sentiment_score: parseFloat((Math.random() * 1.6 - 0.6).toFixed(2)), - post_url: `https://${selectedPlatform}.com/post/mock-${i+1}` - }; - }); - } - /** * 获取概览卡片数据 * 返回包含留言总数、平均互动率和情感分析三个卡片数据 diff --git a/backend/src/services/analyticsService.ts b/backend/src/services/analyticsService.ts index 93fa374..eb20e5b 100644 --- a/backend/src/services/analyticsService.ts +++ b/backend/src/services/analyticsService.ts @@ -838,7 +838,7 @@ export class AnalyticsService { // 合并数据,生成最终结果 const transformedPosts: PostPerformanceData[] = postsData.map(post => { - // 获取帖文的指标数据,如果没有则使用空值或模拟数据 + // 获取帖文的指标数据 const metrics = metricsMap[post.post_id] || {}; const postMetrics = { views: Number(metrics.views || 0), @@ -847,16 +847,10 @@ export class AnalyticsService { shares: Number(metrics.shares || 0) }; - // 有真实数据则使用真实数据,否则生成模拟数据 - const hasRealMetrics = postMetrics.views > 0 || postMetrics.likes > 0 || - postMetrics.comments > 0 || postMetrics.shares > 0; - - const finalMetrics = hasRealMetrics ? postMetrics : this.generateMockMetrics(); - - // 同样,有真实情感分数则使用真实数据,否则生成模拟数据 + // 获取情感分数 const sentimentScore = metrics.sentiment_score !== undefined ? Number(metrics.sentiment_score) - : this.generateMockSentimentScore(); + : 0; // 默认为0(中性) return { post_id: post.post_id, @@ -865,7 +859,7 @@ export class AnalyticsService { kol_name: post.kol_name || '未知KOL', platform: post.platform || 'unknown', publish_date: post.publish_date, - metrics: finalMetrics, + metrics: postMetrics, sentiment_score: sentimentScore, post_url: post.post_url || `https://${post.platform || 'example'}.com/post/${post.post_id}` }; @@ -893,19 +887,11 @@ export class AnalyticsService { }); } - // 统计真实数据vs模拟数据的比例 - const realDataCount = transformedPosts.filter(post => - post.metrics.views > 0 || post.metrics.likes > 0 || - post.metrics.comments > 0 || post.metrics.shares > 0 - ).length; - const duration = Date.now() - startTime; logger.info('KOL post performance data fetched successfully', { duration, resultCount: transformedPosts.length, - totalPosts: total, - realDataCount, - mockDataCount: transformedPosts.length - realDataCount + totalPosts: total }); return { @@ -916,73 +902,13 @@ export class AnalyticsService { const duration = Date.now() - startTime; logger.error(`Error in getPostPerformance (${duration}ms)`, error); - // 发生错误时,尝试返回模拟数据 - try { - const mockPosts = this.generateMockPostPerformanceData(limit); - logger.info('Returning mock data due to error', { - mockDataCount: mockPosts.length, - error: error instanceof Error ? error.message : 'Unknown error' - }); - - return { - posts: mockPosts, - total: 100 // 模拟总数 - }; - } catch (mockError) { - // 如果连模拟数据都无法生成,则抛出原始错误 - throw error; - } + // 发生错误时返回空数据 + return { + posts: [], + total: 0 + }; } } - - /** - * 生成模拟贴文互动指标 - */ - private generateMockMetrics(): {views: number, likes: number, comments: number, shares: number} { - // 生成在合理范围内的随机数 - const views = Math.floor(Math.random() * 10000) + 1000; - const likes = Math.floor(views * (Math.random() * 0.2 + 0.05)); // 5-25% 的观看转化为点赞 - const comments = Math.floor(likes * (Math.random() * 0.2 + 0.02)); // 2-22% 的点赞转化为评论 - const shares = Math.floor(likes * (Math.random() * 0.1 + 0.01)); // 1-11% 的点赞转化为分享 - - return { views, likes, comments, shares }; - } - - /** - * 生成模拟情感分数 (-1 到 1 之间) - */ - private generateMockSentimentScore(): number { - // 生成-1到1之间的随机数,倾向于正面情绪 - return parseFloat((Math.random() * 1.6 - 0.6).toFixed(2)); - } - - /** - * 生成完整的模拟贴文表现数据 - */ - private generateMockPostPerformanceData(count: number): PostPerformanceData[] { - const platforms = ['instagram', 'youtube', 'tiktok', 'facebook', 'twitter']; - const mockPosts: PostPerformanceData[] = []; - - for (let i = 0; i < count; i++) { - const platform = platforms[Math.floor(Math.random() * platforms.length)]; - const publishDate = new Date(); - publishDate.setDate(publishDate.getDate() - Math.floor(Math.random() * 90)); - - mockPosts.push({ - post_id: `mock-post-${i+1}`, - title: `模拟贴文 ${i+1}`, - kol_id: `mock-kol-${Math.floor(Math.random() * 10) + 1}`, - kol_name: `模拟KOL ${Math.floor(Math.random() * 10) + 1}`, - platform, - publish_date: publishDate.toISOString(), - metrics: this.generateMockMetrics(), - sentiment_score: this.generateMockSentimentScore(), - post_url: `https://${platform}.com/post/mock-${i+1}` - }); - } - - return mockPosts; - } /** * 获取概览卡片数据 diff --git a/web/src/components/Analytics.tsx b/web/src/components/Analytics.tsx index f939ce2..3cf7302 100644 --- a/web/src/components/Analytics.tsx +++ b/web/src/components/Analytics.tsx @@ -383,11 +383,7 @@ const Analytics: React.FC = () => { const fetchAnalyticsData = async () => { try { setLoading(true); - - // 删除平台分布的硬编码数据,使用API数据替代 - - // 删除情感分析的硬编码数据,使用API数据替代 - + // Set mock status data setStatusData([ { name: 'approved', value: 45, color: '#10B981' }, @@ -395,14 +391,6 @@ const Analytics: React.FC = () => { { name: 'rejected', value: 25, color: '#EF4444' } ]); - // 删除热门文章的硬编码数据,使用API数据替代 - // setPopularArticles([ - // { id: '1', title: 'How to Increase Engagement', views: 1200, engagement: 85, platform: 'Facebook' }, - // { id: '2', title: 'Top 10 Marketing Strategies', views: 980, engagement: 72, platform: 'LinkedIn' }, - // { id: '3', title: 'Social Media in 2023', views: 850, engagement: 68, platform: 'Twitter' }, - // { id: '4', title: 'Building Your Brand Online', views: 750, engagement: 63, platform: 'Instagram' }, - // { id: '5', title: 'Content Creation Tips', views: 620, engagement: 57, platform: 'YouTube' } - // ]); // 从API获取漏斗数据 try {