fix: 修复 ConceptStatsPanel API Mock 数据格式问题

解决控制台 "无法访问概念统计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
This commit is contained in:
zdl
2025-10-30 18:22:11 +08:00
parent 20cb83b792
commit 3b0146fe49

View File

@@ -47,6 +47,62 @@ const generatePopularConcepts = (size = 20) => {
return results; 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 // 概念相关的 Handlers
export const conceptHandlers = [ export const conceptHandlers = [
// 搜索概念(热门概念) // 搜索概念(热门概念)
@@ -214,23 +270,60 @@ export const conceptHandlers = [
} }
}), }),
// 获取统计数据 // 获取统计数据(直接访问外部 API
http.get('http://111.198.58.126:16801/statistics', async ({ request }) => { http.get('http://111.198.58.126:16801/statistics', async ({ request }) => {
await delay(300); await delay(300);
const url = new URL(request.url); const url = new URL(request.url);
const minStockCount = parseInt(url.searchParams.get('min_stock_count') || '3'); const minStockCount = parseInt(url.searchParams.get('min_stock_count') || '3');
const days = parseInt(url.searchParams.get('days') || '7'); 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({ return HttpResponse.json({
total_concepts: 150, success: true,
active_concepts: 120, data: statsData,
avg_stock_count: 25, note: 'Mock 数据',
top_concepts: generatePopularConcepts(10), params: {
min_stock_count: minStockCount, min_stock_count: minStockCount,
days: days, 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() updated_at: new Date().toISOString()
}); });
}), }),