update pay ui
This commit is contained in:
@@ -116,56 +116,74 @@ const extractSZSEPrices = (stockData: SZSEStockData) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理深交所股票行情消息(新 API 格式:type='stock')
|
* 处理深交所批量行情消息(新 API 格式,与 SSE 一致)
|
||||||
|
* 格式:{ type: 'stock', data: { '000001': {...}, '000002': {...} }, timestamp: '...' }
|
||||||
*/
|
*/
|
||||||
const handleSZSEStockMessage = (
|
const handleSZSEBatchMessage = (
|
||||||
data: SZSEStockData,
|
msg: SZSERealtimeMessage,
|
||||||
timestamp: string,
|
|
||||||
subscribedCodes: Set<string>,
|
subscribedCodes: Set<string>,
|
||||||
prevQuotes: QuotesMap
|
prevQuotes: QuotesMap
|
||||||
): QuotesMap | null => {
|
): QuotesMap | null => {
|
||||||
const rawCode = data.security_id;
|
const { data, timestamp } = msg;
|
||||||
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
|
||||||
|
|
||||||
if (!subscribedCodes.has(rawCode) && !subscribedCodes.has(fullCode)) {
|
// 新 API 格式:data 是 { code: quote, ... } 的字典
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(data);
|
|
||||||
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(data);
|
|
||||||
|
|
||||||
const updated: QuotesMap = { ...prevQuotes };
|
const updated: QuotesMap = { ...prevQuotes };
|
||||||
updated[fullCode] = {
|
let hasUpdate = false;
|
||||||
code: fullCode,
|
|
||||||
name: prevQuotes[fullCode]?.name || '',
|
|
||||||
price: data.last_px,
|
|
||||||
prevClose,
|
|
||||||
open: data.open_px,
|
|
||||||
high: data.high_px,
|
|
||||||
low: data.low_px,
|
|
||||||
volume,
|
|
||||||
amount,
|
|
||||||
numTrades: data.num_trades,
|
|
||||||
upperLimit,
|
|
||||||
lowerLimit,
|
|
||||||
change: data.last_px - prevClose,
|
|
||||||
changePct: calcChangePct(data.last_px, prevClose),
|
|
||||||
bidPrices,
|
|
||||||
bidVolumes,
|
|
||||||
askPrices,
|
|
||||||
askVolumes,
|
|
||||||
tradingPhase,
|
|
||||||
updateTime: data.update_time || timestamp,
|
|
||||||
exchange: 'SZSE',
|
|
||||||
} as QuoteData;
|
|
||||||
|
|
||||||
return updated;
|
// 遍历所有股票数据
|
||||||
|
Object.entries(data).forEach(([code, quote]) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const stockData = quote as any;
|
||||||
|
if (!stockData || typeof stockData !== 'object') return;
|
||||||
|
|
||||||
|
const rawCode = stockData.security_id || code;
|
||||||
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
|
// 只处理已订阅的代码
|
||||||
|
if (!subscribedCodes.has(code) && !subscribedCodes.has(rawCode) && !subscribedCodes.has(fullCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasUpdate = true;
|
||||||
|
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(stockData);
|
||||||
|
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(stockData);
|
||||||
|
|
||||||
|
updated[fullCode] = {
|
||||||
|
code: fullCode,
|
||||||
|
name: prevQuotes[fullCode]?.name || '',
|
||||||
|
price: stockData.last_px,
|
||||||
|
prevClose,
|
||||||
|
open: stockData.open_px,
|
||||||
|
high: stockData.high_px,
|
||||||
|
low: stockData.low_px,
|
||||||
|
volume,
|
||||||
|
amount,
|
||||||
|
numTrades: stockData.num_trades,
|
||||||
|
upperLimit,
|
||||||
|
lowerLimit,
|
||||||
|
change: stockData.last_px - prevClose,
|
||||||
|
changePct: calcChangePct(stockData.last_px, prevClose),
|
||||||
|
bidPrices,
|
||||||
|
bidVolumes,
|
||||||
|
askPrices,
|
||||||
|
askVolumes,
|
||||||
|
tradingPhase,
|
||||||
|
updateTime: stockData.update_time || timestamp,
|
||||||
|
exchange: 'SZSE',
|
||||||
|
} as QuoteData;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasUpdate ? updated : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理深交所实时消息 (兼容新旧 API)
|
* 处理深交所实时消息 (兼容新旧 API)
|
||||||
* 新 API: type='stock'/'bond'/'fund', data 直接是行情对象
|
* 新 API (批量模式): type='stock'/'bond'/'fund', data = { code: quote, ... }
|
||||||
* 旧 API: type='realtime', category='stock'/'bond'/etc
|
* 旧 API (单条模式): type='realtime', category='stock', data = { security_id, ... }
|
||||||
*/
|
*/
|
||||||
const handleSZSERealtimeMessage = (
|
const handleSZSERealtimeMessage = (
|
||||||
msg: SZSERealtimeMessage,
|
msg: SZSERealtimeMessage,
|
||||||
@@ -173,7 +191,22 @@ const handleSZSERealtimeMessage = (
|
|||||||
prevQuotes: QuotesMap
|
prevQuotes: QuotesMap
|
||||||
): QuotesMap | null => {
|
): QuotesMap | null => {
|
||||||
const { type, category, data, timestamp } = msg;
|
const { type, category, data, timestamp } = msg;
|
||||||
const rawCode = data.security_id;
|
|
||||||
|
// 新 API 批量格式检测:data 是字典 { code: quote, ... },没有 security_id 在顶层
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const anyData = data as any;
|
||||||
|
const isBatchFormat = anyData && typeof anyData === 'object' && !anyData.security_id;
|
||||||
|
|
||||||
|
if (isBatchFormat && (type === 'stock' || type === 'bond' || type === 'fund')) {
|
||||||
|
return handleSZSEBatchMessage(msg, subscribedCodes, prevQuotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧 API 单条格式:data 直接是行情对象 { security_id, last_px, ... }
|
||||||
|
const rawCode = anyData?.security_id;
|
||||||
|
if (!rawCode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
if (!subscribedCodes.has(rawCode) && !subscribedCodes.has(fullCode)) {
|
if (!subscribedCodes.has(rawCode) && !subscribedCodes.has(fullCode)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user