340 lines
16 KiB
JavaScript
340 lines
16 KiB
JavaScript
// src/mocks/handlers/market.js
|
||
// 市场行情相关的 Mock Handlers
|
||
|
||
import { http, HttpResponse } from 'msw';
|
||
import { generateMarketData } from '../data/market';
|
||
|
||
// 模拟延迟
|
||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||
|
||
export const marketHandlers = [
|
||
// 1. 成交数据
|
||
http.get('/api/market/trade/:stockCode', async ({ params, request }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.tradeData);
|
||
}),
|
||
|
||
// 2. 资金流向
|
||
http.get('/api/market/funding/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.fundingData);
|
||
}),
|
||
|
||
// 3. 大单统计
|
||
http.get('/api/market/bigdeal/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.bigDealData);
|
||
}),
|
||
|
||
// 4. 异动分析
|
||
http.get('/api/market/unusual/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.unusualData);
|
||
}),
|
||
|
||
// 5. 股权质押
|
||
http.get('/api/market/pledge/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.pledgeData);
|
||
}),
|
||
|
||
// 6. 市场摘要
|
||
http.get('/api/market/summary/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.summaryData);
|
||
}),
|
||
|
||
// 7. 涨停分析
|
||
http.get('/api/market/rise-analysis/:stockCode', async ({ params }) => {
|
||
await delay(200);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.riseAnalysisData);
|
||
}),
|
||
|
||
// 8. 最新分时数据
|
||
http.get('/api/stock/:stockCode/latest-minute', async ({ params }) => {
|
||
await delay(300);
|
||
const { stockCode } = params;
|
||
const data = generateMarketData(stockCode);
|
||
return HttpResponse.json(data.latestMinuteData);
|
||
}),
|
||
|
||
// 9. 热门概念数据(个股中心页面使用)
|
||
http.get('/api/concepts/daily-top', async ({ request }) => {
|
||
await delay(300);
|
||
const url = new URL(request.url);
|
||
const limit = parseInt(url.searchParams.get('limit') || '6');
|
||
const date = url.searchParams.get('date');
|
||
|
||
// 获取当前日期或指定日期
|
||
const tradeDate = date || new Date().toISOString().split('T')[0];
|
||
|
||
// 热门概念列表
|
||
const conceptPool = [
|
||
{ name: '人工智能', desc: '人工智能是"技术突破+政策扶持"双轮驱动的硬科技主题。随着大模型技术的突破,AI应用场景不断拓展。' },
|
||
{ name: '新能源汽车', desc: '新能源汽车行业景气度持续向好,渗透率不断提升。政策支持力度大,产业链上下游企业均受益。' },
|
||
{ name: '半导体', desc: '国产半导体替代加速,自主可控需求强烈。政策和资金支持力度大,行业迎来黄金发展期。' },
|
||
{ name: '光伏', desc: '光伏装机量快速增长,成本持续下降,行业景气度维持高位。双碳目标下前景广阔。' },
|
||
{ name: '锂电池', desc: '锂电池技术进步,成本优势扩大,下游应用领域持续扩张。新能源汽车和储能需求旺盛。' },
|
||
{ name: '储能', desc: '储能市场爆发式增长,政策支持力度大,应用场景不断拓展。未来市场空间巨大。' },
|
||
{ name: '算力', desc: 'AI大模型推动算力需求爆发,数据中心、服务器、芯片等产业链受益明显。' },
|
||
{ name: '机器人', desc: '人形机器人产业化加速,特斯拉、小米等巨头入局,产业链迎来发展机遇。' },
|
||
];
|
||
|
||
// 股票池(扩展到足够多的股票)
|
||
const stockPool = [
|
||
{ stock_code: '600519', stock_name: '贵州茅台' },
|
||
{ stock_code: '300750', stock_name: '宁德时代' },
|
||
{ stock_code: '601318', stock_name: '中国平安' },
|
||
{ stock_code: '002594', stock_name: '比亚迪' },
|
||
{ stock_code: '601012', stock_name: '隆基绿能' },
|
||
{ stock_code: '300274', stock_name: '阳光电源' },
|
||
{ stock_code: '688981', stock_name: '中芯国际' },
|
||
{ stock_code: '000725', stock_name: '京东方A' },
|
||
{ stock_code: '600036', stock_name: '招商银行' },
|
||
{ stock_code: '000858', stock_name: '五粮液' },
|
||
{ stock_code: '601166', stock_name: '兴业银行' },
|
||
{ stock_code: '600276', stock_name: '恒瑞医药' },
|
||
{ stock_code: '000333', stock_name: '美的集团' },
|
||
{ stock_code: '600887', stock_name: '伊利股份' },
|
||
{ stock_code: '002415', stock_name: '海康威视' },
|
||
{ stock_code: '601888', stock_name: '中国中免' },
|
||
{ stock_code: '300059', stock_name: '东方财富' },
|
||
{ stock_code: '002475', stock_name: '立讯精密' },
|
||
{ stock_code: '600900', stock_name: '长江电力' },
|
||
{ stock_code: '601398', stock_name: '工商银行' },
|
||
{ stock_code: '600030', stock_name: '中信证券' },
|
||
{ stock_code: '000568', stock_name: '泸州老窖' },
|
||
{ stock_code: '002352', stock_name: '顺丰控股' },
|
||
{ stock_code: '600809', stock_name: '山西汾酒' },
|
||
{ stock_code: '300015', stock_name: '爱尔眼科' },
|
||
{ stock_code: '002142', stock_name: '宁波银行' },
|
||
{ stock_code: '601899', stock_name: '紫金矿业' },
|
||
{ stock_code: '600309', stock_name: '万华化学' },
|
||
{ stock_code: '002304', stock_name: '洋河股份' },
|
||
{ stock_code: '600585', stock_name: '海螺水泥' },
|
||
{ stock_code: '601288', stock_name: '农业银行' },
|
||
{ stock_code: '600050', stock_name: '中国联通' },
|
||
{ stock_code: '000001', stock_name: '平安银行' },
|
||
{ stock_code: '601668', stock_name: '中国建筑' },
|
||
{ stock_code: '600028', stock_name: '中国石化' },
|
||
{ stock_code: '601857', stock_name: '中国石油' },
|
||
{ stock_code: '600000', stock_name: '浦发银行' },
|
||
{ stock_code: '601328', stock_name: '交通银行' },
|
||
{ stock_code: '000002', stock_name: '万科A' },
|
||
{ stock_code: '600104', stock_name: '上汽集团' },
|
||
{ stock_code: '601601', stock_name: '中国太保' },
|
||
{ stock_code: '600016', stock_name: '民生银行' },
|
||
{ stock_code: '601628', stock_name: '中国人寿' },
|
||
{ stock_code: '600031', stock_name: '三一重工' },
|
||
{ stock_code: '002230', stock_name: '科大讯飞' },
|
||
{ stock_code: '300124', stock_name: '汇川技术' },
|
||
{ stock_code: '002049', stock_name: '紫光国微' },
|
||
{ stock_code: '688012', stock_name: '中微公司' },
|
||
{ stock_code: '688008', stock_name: '澜起科技' },
|
||
{ stock_code: '603501', stock_name: '韦尔股份' },
|
||
];
|
||
|
||
// 生成历史触发时间
|
||
const generateHappenedTimes = (seed) => {
|
||
const times = [];
|
||
const count = 3 + (seed % 3); // 3-5个时间点
|
||
for (let k = 0; k < count; k++) {
|
||
const daysAgo = 30 + (seed * 7 + k * 11) % 330;
|
||
const d = new Date();
|
||
d.setDate(d.getDate() - daysAgo);
|
||
times.push(d.toISOString().split('T')[0]);
|
||
}
|
||
return times.sort().reverse();
|
||
};
|
||
|
||
const matchTypes = ['hybrid_knn', 'keyword', 'semantic'];
|
||
|
||
// 生成概念数据
|
||
const concepts = [];
|
||
for (let i = 0; i < Math.min(limit, conceptPool.length); i++) {
|
||
const concept = conceptPool[i];
|
||
const changePercent = parseFloat((Math.random() * 8 - 1).toFixed(2)); // -1% ~ 7%
|
||
const stockCount = Math.floor(Math.random() * 20) + 15; // 15-35只股票
|
||
|
||
// 生成与 stockCount 一致的股票列表(包含完整字段)
|
||
const relatedStocks = [];
|
||
for (let j = 0; j < stockCount; j++) {
|
||
const idx = (i * 7 + j) % stockPool.length;
|
||
const stock = stockPool[idx];
|
||
relatedStocks.push({
|
||
stock_code: stock.stock_code,
|
||
stock_name: stock.stock_name,
|
||
reason: `作为行业龙头企业,${stock.stock_name}在该领域具有核心竞争优势,市场份额领先。`,
|
||
change_pct: parseFloat((Math.random() * 15 - 5).toFixed(2)) // -5% ~ +10%
|
||
});
|
||
}
|
||
|
||
concepts.push({
|
||
concept_id: `CONCEPT_${1001 + i}`,
|
||
concept: concept.name, // 原始字段名
|
||
concept_name: concept.name, // 兼容字段名
|
||
description: concept.desc,
|
||
stock_count: stockCount,
|
||
score: parseFloat((Math.random() * 5 + 3).toFixed(2)), // 3-8 分数
|
||
match_type: matchTypes[i % 3],
|
||
price_info: {
|
||
avg_change_pct: changePercent,
|
||
avg_price: parseFloat((Math.random() * 100 + 10).toFixed(2)),
|
||
total_market_cap: parseFloat((Math.random() * 1000 + 100).toFixed(2))
|
||
},
|
||
change_percent: changePercent, // 兼容字段
|
||
happened_times: generateHappenedTimes(i),
|
||
stocks: relatedStocks,
|
||
hot_score: Math.floor(Math.random() * 100)
|
||
});
|
||
}
|
||
|
||
// 按涨跌幅降序排序
|
||
concepts.sort((a, b) => b.change_percent - a.change_percent);
|
||
|
||
console.log('[Mock Market] 获取热门概念:', { limit, date: tradeDate, count: concepts.length });
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: concepts,
|
||
trade_date: tradeDate
|
||
});
|
||
}),
|
||
|
||
// 10. 市值热力图数据(个股中心页面使用)
|
||
http.get('/api/market/heatmap', async ({ request }) => {
|
||
await delay(400);
|
||
const url = new URL(request.url);
|
||
const limit = parseInt(url.searchParams.get('limit') || '500');
|
||
const date = url.searchParams.get('date');
|
||
|
||
const tradeDate = date || new Date().toISOString().split('T')[0];
|
||
|
||
// 行业列表
|
||
const industries = ['食品饮料', '银行', '医药生物', '电子', '计算机', '汽车', '电力设备', '机械设备', '化工', '房地产', '有色金属', '钢铁'];
|
||
const provinces = ['北京', '上海', '广东', '浙江', '江苏', '山东', '四川', '湖北', '福建', '安徽'];
|
||
|
||
// 常见股票数据
|
||
const majorStocks = [
|
||
{ code: '600519', name: '贵州茅台', cap: 1850, industry: '食品饮料', province: '贵州' },
|
||
{ code: '601318', name: '中国平安', cap: 920, industry: '保险', province: '广东' },
|
||
{ code: '600036', name: '招商银行', cap: 850, industry: '银行', province: '广东' },
|
||
{ code: '300750', name: '宁德时代', cap: 780, industry: '电力设备', province: '福建' },
|
||
{ code: '601166', name: '兴业银行', cap: 420, industry: '银行', province: '福建' },
|
||
{ code: '000858', name: '五粮液', cap: 580, industry: '食品饮料', province: '四川' },
|
||
{ code: '002594', name: '比亚迪', cap: 650, industry: '汽车', province: '广东' },
|
||
{ code: '601012', name: '隆基绿能', cap: 320, industry: '电力设备', province: '陕西' },
|
||
{ code: '688981', name: '中芯国际', cap: 280, industry: '电子', province: '上海' },
|
||
{ code: '600900', name: '长江电力', cap: 520, industry: '公用事业', province: '湖北' },
|
||
];
|
||
|
||
// 生成热力图数据
|
||
const heatmapData = [];
|
||
let risingCount = 0;
|
||
let fallingCount = 0;
|
||
|
||
// 先添加主要股票
|
||
majorStocks.forEach(stock => {
|
||
const changePercent = parseFloat((Math.random() * 12 - 4).toFixed(2)); // -4% ~ 8%
|
||
const amount = parseFloat((Math.random() * 100 + 10).toFixed(2)); // 10-110亿
|
||
|
||
if (changePercent > 0) risingCount++;
|
||
else if (changePercent < 0) fallingCount++;
|
||
|
||
heatmapData.push({
|
||
stock_code: stock.code,
|
||
stock_name: stock.name,
|
||
market_cap: stock.cap,
|
||
change_percent: changePercent,
|
||
amount: amount,
|
||
industry: stock.industry,
|
||
province: stock.province
|
||
});
|
||
});
|
||
|
||
// 生成更多随机股票数据
|
||
for (let i = majorStocks.length; i < Math.min(limit, 200); i++) {
|
||
const changePercent = parseFloat((Math.random() * 14 - 5).toFixed(2)); // -5% ~ 9%
|
||
const marketCap = parseFloat((Math.random() * 500 + 20).toFixed(2)); // 20-520亿
|
||
const amount = parseFloat((Math.random() * 50 + 1).toFixed(2)); // 1-51亿
|
||
|
||
if (changePercent > 0) risingCount++;
|
||
else if (changePercent < 0) fallingCount++;
|
||
|
||
heatmapData.push({
|
||
stock_code: `${600000 + i}`,
|
||
stock_name: `股票${i}`,
|
||
market_cap: marketCap,
|
||
change_percent: changePercent,
|
||
amount: amount,
|
||
industry: industries[Math.floor(Math.random() * industries.length)],
|
||
province: provinces[Math.floor(Math.random() * provinces.length)]
|
||
});
|
||
}
|
||
|
||
console.log('[Mock Market] 获取热力图数据:', { limit, date: tradeDate, count: heatmapData.length });
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
data: heatmapData,
|
||
trade_date: tradeDate,
|
||
statistics: {
|
||
rising_count: risingCount,
|
||
falling_count: fallingCount
|
||
}
|
||
});
|
||
}),
|
||
|
||
// 11. 市场统计数据(个股中心页面使用)
|
||
http.get('/api/market/statistics', async ({ request }) => {
|
||
await delay(200);
|
||
const url = new URL(request.url);
|
||
const date = url.searchParams.get('date');
|
||
|
||
const tradeDate = date || new Date().toISOString().split('T')[0];
|
||
|
||
// 生成最近30个交易日
|
||
const availableDates = [];
|
||
const currentDate = new Date(tradeDate);
|
||
for (let i = 0; i < 30; i++) {
|
||
const d = new Date(currentDate);
|
||
d.setDate(d.getDate() - i);
|
||
// 跳过周末
|
||
if (d.getDay() !== 0 && d.getDay() !== 6) {
|
||
availableDates.push(d.toISOString().split('T')[0]);
|
||
}
|
||
}
|
||
|
||
console.log('[Mock Market] 获取市场统计数据:', { date: tradeDate });
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
summary: {
|
||
total_market_cap: parseFloat((Math.random() * 5000 + 80000).toFixed(2)), // 80000-85000亿
|
||
total_amount: parseFloat((Math.random() * 3000 + 8000).toFixed(2)), // 8000-11000亿
|
||
avg_pe: parseFloat((Math.random() * 5 + 12).toFixed(2)), // 12-17
|
||
avg_pb: parseFloat((Math.random() * 0.5 + 1.3).toFixed(2)), // 1.3-1.8
|
||
rising_stocks: Math.floor(Math.random() * 1500 + 1500), // 1500-3000
|
||
falling_stocks: Math.floor(Math.random() * 1500 + 1000), // 1000-2500
|
||
unchanged_stocks: Math.floor(Math.random() * 200 + 100) // 100-300
|
||
},
|
||
trade_date: tradeDate,
|
||
available_dates: availableDates.slice(0, 20) // 返回最近20个交易日
|
||
});
|
||
}),
|
||
];
|