- DeepAnalysis: 4 个 fetch → axios - DynamicTracking: 3 个 fetch → axios (NewsPanel, ForecastPanel) - MarketDataView/services: 4 个 fetch → axios - CompanyOverview/hooks: 9 个 fetch → axios (6 个文件) - StockQuoteCard/hooks: 1 个 fetch → axios - ValueChainNodeCard: 1 个 fetch → axios 清理: - 删除未使用的 useCompanyOverviewData.ts - 移除所有 getApiBase/API_BASE_URL 引用 总计: 22 个 fetch 调用迁移, 复用项目已有的 axios 拦截器配置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
/**
|
|
* useStockQuoteData - 股票行情数据获取 Hook
|
|
*
|
|
* 合并获取行情数据和基本信息,供 StockQuoteCard 内部使用
|
|
*/
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { stockService } from '@services/eventService';
|
|
import { logger } from '@utils/logger';
|
|
import axios from '@utils/axiosConfig';
|
|
import type { StockQuoteCardData } from '../types';
|
|
import type { BasicInfo } from '../../CompanyOverview/types';
|
|
|
|
/**
|
|
* 将 API 响应数据转换为 StockQuoteCard 所需格式
|
|
*/
|
|
const transformQuoteData = (apiData: any, stockCode: string): StockQuoteCardData | null => {
|
|
if (!apiData) return null;
|
|
|
|
return {
|
|
// 基础信息
|
|
name: apiData.name || apiData.stock_name || '未知',
|
|
code: apiData.code || apiData.stock_code || stockCode,
|
|
indexTags: apiData.index_tags || apiData.indexTags || [],
|
|
industry: apiData.industry || apiData.sw_industry_l2 || '',
|
|
industryL1: apiData.industry_l1 || apiData.sw_industry_l1 || '',
|
|
|
|
// 价格信息
|
|
currentPrice: apiData.current_price || apiData.currentPrice || apiData.close || 0,
|
|
changePercent: apiData.change_percent || apiData.changePercent || apiData.pct_chg || 0,
|
|
todayOpen: apiData.today_open || apiData.todayOpen || apiData.open || 0,
|
|
yesterdayClose: apiData.yesterday_close || apiData.yesterdayClose || apiData.pre_close || 0,
|
|
todayHigh: apiData.today_high || apiData.todayHigh || apiData.high || 0,
|
|
todayLow: apiData.today_low || apiData.todayLow || apiData.low || 0,
|
|
|
|
// 关键指标
|
|
pe: apiData.pe || apiData.pe_ttm || 0,
|
|
eps: apiData.eps || apiData.basic_eps || undefined,
|
|
pb: apiData.pb || apiData.pb_mrq || 0,
|
|
marketCap: apiData.market_cap || apiData.marketCap || apiData.circ_mv || '0',
|
|
week52Low: apiData.week52_low || apiData.week52Low || 0,
|
|
week52High: apiData.week52_high || apiData.week52High || 0,
|
|
|
|
// 主力动态
|
|
mainNetInflow: apiData.main_net_inflow || apiData.mainNetInflow || 0,
|
|
institutionHolding: apiData.institution_holding || apiData.institutionHolding || 0,
|
|
buyRatio: apiData.buy_ratio || apiData.buyRatio || 50,
|
|
sellRatio: apiData.sell_ratio || apiData.sellRatio || 50,
|
|
|
|
// 更新时间
|
|
updateTime: apiData.update_time || apiData.updateTime || new Date().toLocaleString(),
|
|
};
|
|
};
|
|
|
|
interface UseStockQuoteDataResult {
|
|
quoteData: StockQuoteCardData | null;
|
|
basicInfo: BasicInfo | null;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
refetch: () => void;
|
|
}
|
|
|
|
/**
|
|
* 股票行情数据获取 Hook
|
|
* 合并获取行情数据和基本信息
|
|
*
|
|
* @param stockCode - 股票代码
|
|
*/
|
|
export const useStockQuoteData = (stockCode?: string): UseStockQuoteDataResult => {
|
|
const [quoteData, setQuoteData] = useState<StockQuoteCardData | null>(null);
|
|
const [basicInfo, setBasicInfo] = useState<BasicInfo | null>(null);
|
|
const [quoteLoading, setQuoteLoading] = useState(false);
|
|
const [basicLoading, setBasicLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// 获取行情数据
|
|
const fetchQuote = useCallback(async () => {
|
|
if (!stockCode) {
|
|
setQuoteData(null);
|
|
return;
|
|
}
|
|
|
|
setQuoteLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
logger.debug('useStockQuoteData', '获取股票行情', { stockCode });
|
|
const quotes = await stockService.getQuotes([stockCode]);
|
|
|
|
// API 返回格式: { [stockCode]: quoteData }
|
|
const quoteResult = quotes?.[stockCode] || quotes;
|
|
const transformedData = transformQuoteData(quoteResult, stockCode);
|
|
|
|
logger.debug('useStockQuoteData', '行情数据转换完成', { stockCode, hasData: !!transformedData });
|
|
setQuoteData(transformedData);
|
|
} catch (err) {
|
|
logger.error('useStockQuoteData', '获取行情失败', err);
|
|
setError('获取行情数据失败');
|
|
setQuoteData(null);
|
|
} finally {
|
|
setQuoteLoading(false);
|
|
}
|
|
}, [stockCode]);
|
|
|
|
// 获取基本信息
|
|
const fetchBasicInfo = useCallback(async () => {
|
|
if (!stockCode) {
|
|
setBasicInfo(null);
|
|
return;
|
|
}
|
|
|
|
setBasicLoading(true);
|
|
|
|
try {
|
|
const { data: result } = await axios.get(`/api/stock/${stockCode}/basic-info`);
|
|
|
|
if (result.success) {
|
|
setBasicInfo(result.data);
|
|
}
|
|
} catch (err) {
|
|
logger.error('useStockQuoteData', '获取基本信息失败', err);
|
|
// 基本信息获取失败不影响主流程,只记录日志
|
|
} finally {
|
|
setBasicLoading(false);
|
|
}
|
|
}, [stockCode]);
|
|
|
|
// stockCode 变化时重新获取数据
|
|
useEffect(() => {
|
|
fetchQuote();
|
|
fetchBasicInfo();
|
|
}, [fetchQuote, fetchBasicInfo]);
|
|
|
|
// 手动刷新
|
|
const refetch = useCallback(() => {
|
|
fetchQuote();
|
|
fetchBasicInfo();
|
|
}, [fetchQuote, fetchBasicInfo]);
|
|
|
|
return {
|
|
quoteData,
|
|
basicInfo,
|
|
isLoading: quoteLoading || basicLoading,
|
|
error,
|
|
refetch,
|
|
};
|
|
};
|
|
|
|
export default useStockQuoteData;
|