diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b240878e..eeb68960 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,11 @@ "Bash(npm run build)", "Bash(chmod +x /Users/qiye/Desktop/jzqy/vf_react/scripts/*.sh)", "Bash(node scripts/parseIndustryCSV.js)", - "Bash(cat:*)" + "Bash(cat:*)", + "Bash(npm cache clean --force)", + "Bash(npm install)", + "Bash(npm run start:mock)", + "Bash(npm install fsevents@latest --save-optional --force)" ], "deny": [], "ask": [] diff --git a/src/mocks/data/kline.js b/src/mocks/data/kline.js new file mode 100644 index 00000000..ced4aab1 --- /dev/null +++ b/src/mocks/data/kline.js @@ -0,0 +1,124 @@ +// src/mocks/data/kline.js +// K线数据生成函数 + +/** + * 生成分时数据 (timeline) + * 用于展示当日分钟级别的价格走势 + */ +export const generateTimelineData = (indexCode) => { + const data = []; + const basePrice = getBasePrice(indexCode); + const today = new Date(); + + // 生成早盘数据 (09:30 - 11:30) + const morningStart = new Date(today.setHours(9, 30, 0, 0)); + const morningEnd = new Date(today.setHours(11, 30, 0, 0)); + generateTimeRange(data, morningStart, morningEnd, basePrice, 'morning'); + + // 生成午盘数据 (13:00 - 15:00) + const afternoonStart = new Date(today.setHours(13, 0, 0, 0)); + const afternoonEnd = new Date(today.setHours(15, 0, 0, 0)); + generateTimeRange(data, afternoonStart, afternoonEnd, basePrice, 'afternoon'); + + return data; +}; + +/** + * 生成日线数据 (daily) + * 用于获取历史收盘价等数据 + */ +export const generateDailyData = (indexCode, days = 30) => { + const data = []; + const basePrice = getBasePrice(indexCode); + const today = new Date(); + + for (let i = days - 1; i >= 0; i--) { + const date = new Date(today); + date.setDate(date.getDate() - i); + + // 跳过周末 + const dayOfWeek = date.getDay(); + if (dayOfWeek === 0 || dayOfWeek === 6) continue; + + const open = basePrice * (1 + (Math.random() * 0.04 - 0.02)); + const close = open * (1 + (Math.random() * 0.03 - 0.015)); + const high = Math.max(open, close) * (1 + Math.random() * 0.015); + const low = Math.min(open, close) * (1 - Math.random() * 0.015); + const volume = Math.floor(Math.random() * 50000000000 + 10000000000); + + data.push({ + date: formatDate(date), + time: formatDate(date), + open: parseFloat(open.toFixed(2)), + close: parseFloat(close.toFixed(2)), + high: parseFloat(high.toFixed(2)), + low: parseFloat(low.toFixed(2)), + volume: volume, + prev_close: i === 0 ? parseFloat((basePrice * 0.99).toFixed(2)) : data[data.length - 1]?.close + }); + } + + return data; +}; + +/** + * 生成时间范围内的数据 + */ +function generateTimeRange(data, startTime, endTime, basePrice, session) { + const current = new Date(startTime); + let price = basePrice; + + // 波动趋势(早盘和午盘可能有不同的走势) + const trend = session === 'morning' ? Math.random() * 0.02 - 0.01 : Math.random() * 0.015 - 0.005; + + while (current <= endTime) { + // 添加随机波动 + const volatility = (Math.random() - 0.5) * 0.005; + price = price * (1 + trend / 120 + volatility); // 每分钟微小变化 + + const volume = Math.floor(Math.random() * 500000000 + 100000000); + + data.push({ + time: formatTime(current), + price: parseFloat(price.toFixed(2)), + close: parseFloat(price.toFixed(2)), + volume: volume, + prev_close: basePrice + }); + + // 增加1分钟 + current.setMinutes(current.getMinutes() + 1); + } +} + +/** + * 获取不同指数的基准价格 + */ +function getBasePrice(indexCode) { + const basePrices = { + '000001.SH': 3200, // 上证指数 + '399001.SZ': 10500, // 深证成指 + '399006.SZ': 2100 // 创业板指 + }; + + return basePrices[indexCode] || 3000; +} + +/** + * 格式化时间为 HH:mm + */ +function formatTime(date) { + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${hours}:${minutes}`; +} + +/** + * 格式化日期为 YYYY-MM-DD + */ +function formatDate(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} diff --git a/src/mocks/handlers/stock.js b/src/mocks/handlers/stock.js index e41bdd59..5491e2e0 100644 --- a/src/mocks/handlers/stock.js +++ b/src/mocks/handlers/stock.js @@ -2,6 +2,7 @@ // 股票相关的 Mock Handlers import { http, HttpResponse } from 'msw'; +import { generateTimelineData, generateDailyData } from '../data/kline'; // 模拟延迟 const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); @@ -140,4 +141,87 @@ export const stockHandlers = [ ); } }), + + // 获取指数K线数据 + http.get('/api/index/:indexCode/kline', async ({ params, request }) => { + await delay(300); + + const { indexCode } = params; + const url = new URL(request.url); + const type = url.searchParams.get('type') || 'timeline'; + const eventTime = url.searchParams.get('event_time'); + + console.log('[Mock Stock] 获取指数K线数据:', { indexCode, type, eventTime }); + + try { + let data; + + if (type === 'timeline') { + data = generateTimelineData(indexCode); + } else if (type === 'daily') { + data = generateDailyData(indexCode, 30); + } else { + return HttpResponse.json( + { error: '不支持的类型' }, + { status: 400 } + ); + } + + return HttpResponse.json({ + success: true, + data: data, + index_code: indexCode, + type: type, + message: '获取成功' + }); + } catch (error) { + console.error('[Mock Stock] 获取K线数据失败:', error); + return HttpResponse.json( + { error: '获取K线数据失败' }, + { status: 500 } + ); + } + }), + + // 获取股票K线数据 + http.get('/api/stock/:stockCode/kline', async ({ params, request }) => { + await delay(300); + + const { stockCode } = params; + const url = new URL(request.url); + const type = url.searchParams.get('type') || 'timeline'; + const eventTime = url.searchParams.get('event_time'); + + console.log('[Mock Stock] 获取股票K线数据:', { stockCode, type, eventTime }); + + try { + let data; + + if (type === 'timeline') { + // 股票使用指数的数据生成逻辑,但价格基数不同 + data = generateTimelineData('000001.SH'); // 可以根据股票代码调整 + } else if (type === 'daily') { + data = generateDailyData('000001.SH', 30); + } else { + return HttpResponse.json( + { error: '不支持的类型' }, + { status: 400 } + ); + } + + return HttpResponse.json({ + success: true, + data: data, + stock_code: stockCode, + type: type, + message: '获取成功' + }); + } catch (error) { + console.error('[Mock Stock] 获取股票K线数据失败:', error); + return HttpResponse.json( + { error: '获取K线数据失败' }, + { status: 500 } + ); + } + }), ];