83 lines
2.6 KiB
TypeScript
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 };
|
|
}
|