From 13c3c74b92bcc54a679a80f09d0ad4139980c955 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Fri, 24 Oct 2025 12:32:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0mock=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mocks/data/events.js | 233 +++++++++++++++++++++++++++++++++ src/mocks/handlers/event.js | 105 ++++++++++++++- src/mocks/handlers/industry.js | 8 +- 3 files changed, 341 insertions(+), 5 deletions(-) diff --git a/src/mocks/data/events.js b/src/mocks/data/events.js index 7803eecf..3cd4a53d 100644 --- a/src/mocks/data/events.js +++ b/src/mocks/data/events.js @@ -545,3 +545,236 @@ export function getEventRelatedStocks(eventId) { const count = 3 + (parseInt(eventId) % 4); 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', + })); +} diff --git a/src/mocks/handlers/event.js b/src/mocks/handlers/event.js index 5db80d0a..d6822bcf 100644 --- a/src/mocks/handlers/event.js +++ b/src/mocks/handlers/event.js @@ -2,13 +2,116 @@ // 事件相关的 Mock API Handlers import { http, HttpResponse } from 'msw'; -import { getEventRelatedStocks } from '../data/events'; +import { getEventRelatedStocks, generateMockEvents, generateHotEvents, generatePopularKeywords } from '../data/events'; import { getMockFutureEvents, getMockEventCountsForMonth } from '../data/account'; // 模拟网络延迟 const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms)); 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 }) => { await delay(300); diff --git a/src/mocks/handlers/industry.js b/src/mocks/handlers/industry.js index 44999e5e..84674ba0 100644 --- a/src/mocks/handlers/industry.js +++ b/src/mocks/handlers/industry.js @@ -2,7 +2,7 @@ // 行业分类相关的 Mock API Handlers 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)); @@ -15,14 +15,14 @@ export const industryHandlers = [ const url = new URL(request.url); const classification = url.searchParams.get('classification'); - console.log('[Mock] 获取行业分类树形数据', { classification }); + console.log('[Mock] 获取行业分类树形数据(真实数据)', { classification }); try { - let data = industryTreeData; + let data = industryData; // 如果指定了分类体系,只返回该体系的数据 if (classification) { - data = industryTreeData.filter(item => item.value === classification); + data = industryData.filter(item => item.value === classification); } return HttpResponse.json({