stock-portfolio_byQwen3.6/src/actions/market.ts

83 lines
2.6 KiB
TypeScript

'use server';
import { ProxyAgent, setGlobalDispatcher } from 'undici';
import { db } from '@/db';
import { assets } from '@/db/schema';
import { eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
function getTencentSymbol(asset: { symbol: string; exchange: string | null }): string {
const cleanSymbol = asset.symbol.trim().toUpperCase().replace(/[^0-9A-Z]/g, '');
switch (asset.exchange) {
case 'SSE': return 'sh' + cleanSymbol;
case 'SZSE': return 'sz' + cleanSymbol;
case 'HKEX': return 'hk' + cleanSymbol;
case 'US':
default: return 's_us' + cleanSymbol;
}
}
export async function syncAllMarketPrices() {
const allAssets = await db
.select()
.from(assets);
const proxyUrl = process.env.HTTPS_PROXY;
if (proxyUrl) {
const proxyAgent = new ProxyAgent(proxyUrl);
setGlobalDispatcher(proxyAgent);
}
let successCount = 0;
for (const asset of allAssets) {
try {
if (asset.type === 'STOCK') {
const tCode = getTencentSymbol(asset);
const response = await fetch(`https://qt.gtimg.cn/q=${tCode}`, { cache: 'no-store' });
const arrayBuffer = await response.arrayBuffer();
const decoder = new TextDecoder('gbk');
const text = decoder.decode(arrayBuffer);
const match = text.match(/="([^"]+)"/);
if (match && match[1]) {
const dataArr = match[1].split('~');
const latestPrice = dataArr[3];
if (latestPrice && !isNaN(Number(latestPrice)) && Number(latestPrice) > 0) {
const stockName = dataArr[1] || null;
await db.update(assets)
.set({ latestPrice: latestPrice, name: stockName })
.where(eq(assets.id, asset.id));
successCount++;
}
}
} else if (asset.type === 'CRYPTO') {
const cryptoSymbol = asset.symbol.trim().toUpperCase() + 'USDT';
const response = await fetch(`https://api.binance.com/api/v3/ticker/price?symbol=${cryptoSymbol}`, { cache: 'no-store' });
if (response.ok) {
const data = await response.json();
if (data.price) {
await db.update(assets)
.set({
latestPrice: data.price,
name: asset.symbol.toUpperCase(),
})
.where(eq(assets.id, asset.id));
successCount++;
}
}
}
} catch (error) {
console.warn(`[行情引擎] 同步 ${asset.symbol} 失败,保持原价:`, error);
}
}
revalidatePath('/dashboard');
revalidatePath('/dashboard/assets');
return { success: true, count: successCount };
}