From 3b0146fe4966f96bcfcf6e97accda8b42eb96607 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Thu, 30 Oct 2025 18:22:11 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20ConceptStatsPanel?= =?UTF-8?q?=20API=20Mock=20=E6=95=B0=E6=8D=AE=E6=A0=BC=E5=BC=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决控制台 "无法访问概念统计API" 错误,完善 Mock Service Worker 的统计数据返回格式 **问题原因** 1. Mock 模式下,`/statistics` 端点返回的数据格式不完整 2. 缺少必需的 `success` 和 `data` 包装层 3. 缺少 5 个必需字段:`hot_concepts`, `cold_concepts`, `active_concepts`, `volatile_concepts`, `momentum_concepts` **修复内容** 1. 创建 `generateConceptStats()` 函数(lines 50-104) - 生成热门概念(涨幅前5) - 生成冷门概念(跌幅前5) - 生成活跃概念(新闻+研报最多) - 生成波动概念(波动率最高) - 生成动量概念(连续上涨天数最多) 2. 更新 `http://111.198.58.126:16801/statistics` handler(lines 273-300) - 返回完整的统计数据格式 - 包装为 `{ success: true, data: {...} }` - 支持 `min_stock_count`, `days`, `start_date`, `end_date` 参数 3. 新增 `/concept-api/statistics` handler(lines 302-329) - 覆盖 nginx 代理路由 - 与直接 API 返回相同格式的数据 - 确保两个端点都能正常工作 **数据格式** ```json { "success": true, "data": { "hot_concepts": [...], "cold_concepts": [...], "active_concepts": [...], "volatile_concepts": [...], "momentum_concepts": [...] }, "note": "Mock 数据", "params": { ... }, "updated_at": "2025-10-30T..." } ``` **测试结果** - ✅ 编译成功 - ✅ ConceptStatsPanel 可以正确接收 Mock 数据 - ✅ 不再显示 "无法访问概念统计API" 错误 - ✅ 两个 API 端点(代理 + 直接)都已覆盖 **文件修改** - src/mocks/handlers/concept.js (+79 lines) - 新增 generateConceptStats() 函数 - 更新 /statistics handler - 新增 /concept-api/statistics handler --- src/mocks/handlers/concept.js | 109 +++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/src/mocks/handlers/concept.js b/src/mocks/handlers/concept.js index 6d99371c..20d96f37 100644 --- a/src/mocks/handlers/concept.js +++ b/src/mocks/handlers/concept.js @@ -47,6 +47,62 @@ const generatePopularConcepts = (size = 20) => { return results; }; +// 生成完整的概念统计数据(用于 ConceptStatsPanel) +const generateConceptStats = () => { + // 热门概念(涨幅最大的前5) + const hot_concepts = [ + { name: '小米大模型', change_pct: 18.76, stock_count: 12, news_count: 35 }, + { name: '人工智能', change_pct: 15.67, stock_count: 45, news_count: 23 }, + { name: '新能源汽车', change_pct: 12.34, stock_count: 38, news_count: 18 }, + { name: '芯片概念', change_pct: 9.87, stock_count: 52, news_count: 31 }, + { name: '5G通信', change_pct: 8.45, stock_count: 29, news_count: 15 }, + ]; + + // 冷门概念(跌幅最大的前5) + const cold_concepts = [ + { name: '房地产', change_pct: -8.76, stock_count: 33, news_count: 12 }, + { name: '煤炭开采', change_pct: -6.54, stock_count: 25, news_count: 8 }, + { name: '传统零售', change_pct: -5.43, stock_count: 19, news_count: 6 }, + { name: '钢铁冶炼', change_pct: -4.21, stock_count: 28, news_count: 9 }, + { name: '纺织服装', change_pct: -3.98, stock_count: 15, news_count: 4 }, + ]; + + // 活跃概念(新闻+研报最多的前5) + const active_concepts = [ + { name: '人工智能', news_count: 89, report_count: 15, total_mentions: 104 }, + { name: '芯片概念', news_count: 76, report_count: 12, total_mentions: 88 }, + { name: '新能源汽车', news_count: 65, report_count: 18, total_mentions: 83 }, + { name: '生物医药', news_count: 54, report_count: 9, total_mentions: 63 }, + { name: '量子科技', news_count: 41, report_count: 7, total_mentions: 48 }, + ]; + + // 波动最大的概念(前5) + const volatile_concepts = [ + { name: '区块链', volatility: 23.45, avg_change: 3.21, max_change: 12.34 }, + { name: '元宇宙', volatility: 21.87, avg_change: 2.98, max_change: 11.76 }, + { name: '虚拟现实', volatility: 19.65, avg_change: -1.23, max_change: 9.87 }, + { name: '游戏概念', volatility: 18.32, avg_change: 4.56, max_change: 10.45 }, + { name: '在线教育', volatility: 17.89, avg_change: -2.11, max_change: 8.76 }, + ]; + + // 动量概念(连续上涨的前5) + const momentum_concepts = [ + { name: '数字经济', consecutive_days: 5, total_change: 18.76, avg_daily: 3.75 }, + { name: '云计算', consecutive_days: 4, total_change: 14.32, avg_daily: 3.58 }, + { name: '物联网', consecutive_days: 4, total_change: 12.89, avg_daily: 3.22 }, + { name: '大数据', consecutive_days: 3, total_change: 11.45, avg_daily: 3.82 }, + { name: '工业互联网', consecutive_days: 3, total_change: 9.87, avg_daily: 3.29 }, + ]; + + return { + hot_concepts, + cold_concepts, + active_concepts, + volatile_concepts, + momentum_concepts + }; +}; + // 概念相关的 Handlers export const conceptHandlers = [ // 搜索概念(热门概念) @@ -214,23 +270,60 @@ export const conceptHandlers = [ } }), - // 获取统计数据 + // 获取统计数据(直接访问外部 API) http.get('http://111.198.58.126:16801/statistics', async ({ request }) => { await delay(300); const url = new URL(request.url); const minStockCount = parseInt(url.searchParams.get('min_stock_count') || '3'); const days = parseInt(url.searchParams.get('days') || '7'); + const startDate = url.searchParams.get('start_date'); + const endDate = url.searchParams.get('end_date'); - console.log('[Mock Concept] 获取统计数据:', { minStockCount, days }); + console.log('[Mock Concept] 获取统计数据 (直接API):', { minStockCount, days, startDate, endDate }); + + // 生成完整的统计数据 + const statsData = generateConceptStats(); return HttpResponse.json({ - total_concepts: 150, - active_concepts: 120, - avg_stock_count: 25, - top_concepts: generatePopularConcepts(10), - min_stock_count: minStockCount, - days: days, + success: true, + data: statsData, + note: 'Mock 数据', + params: { + min_stock_count: minStockCount, + days: days, + start_date: startDate, + end_date: endDate + }, + updated_at: new Date().toISOString() + }); + }), + + // 获取统计数据(通过 nginx 代理) + http.get('/concept-api/statistics', async ({ request }) => { + await delay(300); + + const url = new URL(request.url); + const minStockCount = parseInt(url.searchParams.get('min_stock_count') || '3'); + const days = parseInt(url.searchParams.get('days') || '7'); + const startDate = url.searchParams.get('start_date'); + const endDate = url.searchParams.get('end_date'); + + console.log('[Mock Concept] 获取统计数据 (nginx代理):', { minStockCount, days, startDate, endDate }); + + // 生成完整的统计数据 + const statsData = generateConceptStats(); + + return HttpResponse.json({ + success: true, + data: statsData, + note: 'Mock 数据(通过 nginx 代理)', + params: { + min_stock_count: minStockCount, + days: days, + start_date: startDate, + end_date: endDate + }, updated_at: new Date().toISOString() }); }),