feat: 添加mock数据

This commit is contained in:
zdl
2025-10-24 12:32:36 +08:00
parent bcf81f4d47
commit 13c3c74b92
3 changed files with 341 additions and 5 deletions

View File

@@ -545,3 +545,236 @@ export function getEventRelatedStocks(eventId) {
const count = 3 + (parseInt(eventId) % 4); const count = 3 + (parseInt(eventId) % 4);
return generateRelatedStocks(eventId, count); return generateRelatedStocks(eventId, count);
} }
// ==================== Mock 事件列表数据 ====================
// 事件类型池
const eventTypes = ['政策发布', '行业动向', '公司公告', '市场研判', '技术突破', '财报发布', '投融资', '高管变动'];
// 行业池
const industries = ['半导体', '新能源', '人工智能', '医药', '消费', '金融', '房地产', '通信', '互联网', '军工', '化工', '机械'];
// 事件标题模板
const eventTitleTemplates = [
'{industry}行业迎来重大政策利好',
'{company}发布{quarter}财报,业绩超预期',
'{industry}板块集体大涨,{company}涨停',
'央行宣布{policy},影响{industry}行业',
'{company}与{partner}达成战略合作',
'{industry}技术取得重大突破',
'{company}拟投资{amount}亿元布局{industry}',
'国家发改委:支持{industry}产业发展',
'{industry}龙头{company}涨价{percent}%',
'{company}回购股份,彰显信心',
];
// 生成随机公司名
function generateCompanyName(industry) {
const prefixes = ['华为', '中兴', '阿里', '腾讯', '比亚迪', '宁德时代', '隆基', '恒瑞', '茅台', '五粮液', '海康', '中芯'];
const suffixes = ['科技', '集团', '股份', '控股', '实业', ''];
const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
const suffix = suffixes[Math.floor(Math.random() * suffixes.length)];
return `${prefix}${suffix}`;
}
// 生成事件标题
function generateEventTitle(industry, seed) {
const template = eventTitleTemplates[seed % eventTitleTemplates.length];
return template
.replace('{industry}', industry)
.replace('{company}', generateCompanyName(industry))
.replace('{partner}', generateCompanyName(industry))
.replace('{quarter}', ['一季度', '半年度', '三季度', '年度'][seed % 4])
.replace('{policy}', ['降准0.5%', '降息25BP', 'MLF下调', '提高赤字率'][seed % 4])
.replace('{amount}', [50, 100, 200, 500][seed % 4])
.replace('{percent}', [5, 10, 15, 20][seed % 4]);
}
// 生成事件描述
function generateEventDescription(industry, importance, seed) {
const impacts = {
S: '重大利好预计将对行业格局产生深远影响相关概念股有望持续受益。机构预计该事件将带动行业整体估值提升15-20%,龙头企业市值增长空间广阔。',
A: '重要利好,市场情绪积极,短期内资金流入明显。分析师普遍认为该事件将推动行业景气度上行,相关公司业绩有望超预期增长。',
B: '中性偏好,对部分细分领域有一定促进作用。虽然不是行业性机会,但优质标的仍有结构性行情,建议关注业绩确定性强的公司。',
C: '影响有限,市场反应平淡,但长期来看仍有积极意义。事件对行业发展方向有指引作用,关注后续政策跟进和落地情况。',
};
const details = [
`根据最新消息,${industry}领域将获得新一轮政策支持,产业链相关企业订单饱满。`,
`${industry}板块近期表现活跃,多只个股创出年内新高,资金持续流入。`,
`行业专家指出,${industry}产业正处于高速发展期,市场空间广阔,龙头企业优势明显。`,
`券商研报显示,${industry}行业估值处于历史低位,当前具备较高配置价值。`,
];
return impacts[importance] + details[seed % details.length];
}
// 生成关键词
function generateKeywords(industry, seed) {
const commonKeywords = ['政策', '利好', '业绩', '涨停', '龙头', '突破', '合作', '投资'];
const industryKeywords = {
'半导体': ['芯片', '晶圆', '封测', 'AI芯片', '国产替代'],
'新能源': ['电池', '光伏', '储能', '新能源车', '锂电'],
'人工智能': ['大模型', 'AI应用', '算力', '数据', '机器学习'],
'医药': ['创新药', 'CRO', '医疗器械', '生物制药', '仿制药'],
'消费': ['白酒', '食品', '家电', '零售', '免税'],
};
const keywords = [
...commonKeywords.slice(seed % 3, seed % 3 + 3),
...(industryKeywords[industry] || []).slice(0, 2)
];
return keywords.slice(0, 5);
}
/**
* 生成 Mock 事件列表
* @param {Object} params - 查询参数
* @returns {Object} - {events: [], pagination: {}}
*/
export function generateMockEvents(params = {}) {
const {
page = 1,
per_page = 10,
sort = 'new',
importance = 'all',
date_range = '',
q = '',
industry_code = '',
} = params;
// 生成100个事件用于测试
const totalEvents = 100;
const allEvents = [];
const importanceLevels = ['S', 'A', 'B', 'C'];
const baseDate = new Date('2025-01-15');
for (let i = 0; i < totalEvents; i++) {
const industry = industries[i % industries.length];
const imp = importanceLevels[i % importanceLevels.length];
const eventType = eventTypes[i % eventTypes.length];
// 生成随机日期最近30天内
const createdAt = new Date(baseDate);
createdAt.setDate(createdAt.getDate() - (i % 30));
// 生成随机热度和收益率
const hotScore = Math.max(50, 100 - i);
const relatedAvgChg = (Math.random() * 20 - 5).toFixed(2); // -5% 到 15%
const relatedMaxChg = (Math.random() * 30).toFixed(2); // 0% 到 30%
allEvents.push({
id: i + 1,
title: generateEventTitle(industry, i),
description: generateEventDescription(industry, imp, i),
content: generateEventDescription(industry, imp, i),
event_type: eventType,
importance: imp,
status: 'published',
created_at: createdAt.toISOString(),
updated_at: createdAt.toISOString(),
hot_score: hotScore,
view_count: Math.floor(Math.random() * 10000),
related_avg_chg: parseFloat(relatedAvgChg),
related_max_chg: parseFloat(relatedMaxChg),
keywords: generateKeywords(industry, i),
is_ai_generated: i % 4 === 0, // 25% 的事件是AI生成
industry: industry,
});
}
// 筛选
let filteredEvents = allEvents;
// 重要性筛选
if (importance && importance !== 'all') {
filteredEvents = filteredEvents.filter(e => e.importance === importance);
}
// 关键词搜索
if (q) {
const query = q.toLowerCase();
filteredEvents = filteredEvents.filter(e =>
e.title.toLowerCase().includes(query) ||
e.description.toLowerCase().includes(query) ||
e.keywords.some(k => k.toLowerCase().includes(query))
);
}
// 行业筛选
if (industry_code) {
filteredEvents = filteredEvents.filter(e =>
e.industry.includes(industry_code) || e.keywords.includes(industry_code)
);
}
// 日期范围筛选
if (date_range) {
const [startStr, endStr] = date_range.split(' 至 ');
if (startStr && endStr) {
const start = new Date(startStr);
const end = new Date(endStr);
filteredEvents = filteredEvents.filter(e => {
const eventDate = new Date(e.created_at);
return eventDate >= start && eventDate <= end;
});
}
}
// 排序
if (sort === 'hot') {
filteredEvents.sort((a, b) => b.hot_score - a.hot_score);
} else if (sort === 'returns') {
filteredEvents.sort((a, b) => b.related_avg_chg - a.related_avg_chg);
} else {
// 默认按时间排序 (new)
filteredEvents.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
}
// 分页
const start = (page - 1) * per_page;
const end = start + per_page;
const paginatedEvents = filteredEvents.slice(start, end);
return {
events: paginatedEvents,
pagination: {
page: page,
per_page: per_page,
total: filteredEvents.length,
total_pages: Math.ceil(filteredEvents.length / per_page),
},
};
}
/**
* 生成热点事件
* @param {number} limit - 返回数量
* @returns {Array} - 热点事件列表
*/
export function generateHotEvents(limit = 5) {
const { events } = generateMockEvents({ sort: 'hot', per_page: limit });
return events;
}
/**
* 生成热门关键词
* @param {number} limit - 返回数量
* @returns {Array} - 热门关键词列表
*/
export function generatePopularKeywords(limit = 20) {
const allKeywords = [
'人工智能', '芯片', '新能源', '锂电池', '光伏', '储能',
'消费', '白酒', '医药', 'CRO', '半导体', '国产替代',
'军工', '航空', '5G', '通信', '互联网', '云计算',
'大数据', '区块链', '元宇宙', '新基建', '数字经济',
];
return allKeywords.slice(0, limit).map((keyword, index) => ({
keyword,
count: Math.max(10, 100 - index * 3),
trend: index % 3 === 0 ? 'up' : index % 3 === 1 ? 'down' : 'stable',
}));
}

View File

@@ -2,13 +2,116 @@
// 事件相关的 Mock API Handlers // 事件相关的 Mock API Handlers
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
import { getEventRelatedStocks } from '../data/events'; import { getEventRelatedStocks, generateMockEvents, generateHotEvents, generatePopularKeywords } from '../data/events';
import { getMockFutureEvents, getMockEventCountsForMonth } from '../data/account'; import { getMockFutureEvents, getMockEventCountsForMonth } from '../data/account';
// 模拟网络延迟 // 模拟网络延迟
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms)); const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
export const eventHandlers = [ export const eventHandlers = [
// ==================== 事件列表相关 ====================
// 获取事件列表
http.get('/api/events/', async ({ request }) => {
await delay(500);
const url = new URL(request.url);
const params = {
page: parseInt(url.searchParams.get('page') || '1'),
per_page: parseInt(url.searchParams.get('per_page') || '10'),
sort: url.searchParams.get('sort') || 'new',
importance: url.searchParams.get('importance') || 'all',
date_range: url.searchParams.get('date_range') || '',
q: url.searchParams.get('q') || '',
industry_code: url.searchParams.get('industry_code') || '',
industry_classification: url.searchParams.get('industry_classification') || '',
};
console.log('[Mock] 获取事件列表:', params);
try {
const result = generateMockEvents(params);
return HttpResponse.json({
success: true,
data: result,
message: '获取成功'
});
} catch (error) {
console.error('[Mock] 获取事件列表失败:', error);
return HttpResponse.json(
{
success: false,
error: '获取事件列表失败',
data: { events: [], pagination: {} }
},
{ status: 500 }
);
}
}),
// 获取热点事件
http.get('/api/events/hot', async ({ request }) => {
await delay(300);
const url = new URL(request.url);
const limit = parseInt(url.searchParams.get('limit') || '5');
console.log('[Mock] 获取热点事件, limit:', limit);
try {
const hotEvents = generateHotEvents(limit);
return HttpResponse.json({
success: true,
data: hotEvents,
message: '获取成功'
});
} catch (error) {
console.error('[Mock] 获取热点事件失败:', error);
return HttpResponse.json(
{
success: false,
error: '获取热点事件失败',
data: []
},
{ status: 500 }
);
}
}),
// 获取热门关键词
http.get('/api/events/keywords/popular', async ({ request }) => {
await delay(300);
const url = new URL(request.url);
const limit = parseInt(url.searchParams.get('limit') || '20');
console.log('[Mock] 获取热门关键词, limit:', limit);
try {
const keywords = generatePopularKeywords(limit);
return HttpResponse.json({
success: true,
data: keywords,
message: '获取成功'
});
} catch (error) {
console.error('[Mock] 获取热门关键词失败:', error);
return HttpResponse.json(
{
success: false,
error: '获取热门关键词失败',
data: []
},
{ status: 500 }
);
}
}),
// ==================== 事件详情相关 ====================
// 获取事件相关股票 // 获取事件相关股票
http.get('/api/events/:eventId/stocks', async ({ params }) => { http.get('/api/events/:eventId/stocks', async ({ params }) => {
await delay(300); await delay(300);

View File

@@ -2,7 +2,7 @@
// 行业分类相关的 Mock API Handlers // 行业分类相关的 Mock API Handlers
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
import { industryTreeData } from '../data/industries'; import { industryData } from '../../data/industryData';
// 模拟网络延迟 // 模拟网络延迟
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms)); const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
@@ -15,14 +15,14 @@ export const industryHandlers = [
const url = new URL(request.url); const url = new URL(request.url);
const classification = url.searchParams.get('classification'); const classification = url.searchParams.get('classification');
console.log('[Mock] 获取行业分类树形数据', { classification }); console.log('[Mock] 获取行业分类树形数据(真实数据)', { classification });
try { try {
let data = industryTreeData; let data = industryData;
// 如果指定了分类体系,只返回该体系的数据 // 如果指定了分类体系,只返回该体系的数据
if (classification) { if (classification) {
data = industryTreeData.filter(item => item.value === classification); data = industryData.filter(item => item.value === classification);
} }
return HttpResponse.json({ return HttpResponse.json({