移除深圳宝安机场跑道数据采集,深圳市场结算使用流浮山 HKO 数据

This commit is contained in:
2569718930@qq.com
2026-05-20 19:38:07 +08:00
parent 0c24a3ed6d
commit 8ba9567f64
3 changed files with 108 additions and 43 deletions
+107 -40
View File
@@ -241,6 +241,8 @@ const TELEGRAM_TOPICS_GROUP_URL = TELEGRAM_GROUP_URL;
const SUBSCRIPTION_HELP_HREF = "/subscription-help"; const SUBSCRIPTION_HELP_HREF = "/subscription-help";
const PAYMENT_RECOVERY_STORAGE_KEY = "polyweather:lastPaymentRecovery"; const PAYMENT_RECOVERY_STORAGE_KEY = "polyweather:lastPaymentRecovery";
const PAYMENT_RECOVERY_TTL_MS = 6 * 60 * 60 * 1000; const PAYMENT_RECOVERY_TTL_MS = 6 * 60 * 60 * 1000;
const WALLET_REQUEST_TIMEOUT_MS = 60_000;
const WALLET_TRANSACTION_REQUEST_TIMEOUT_MS = 120_000;
let walletConnectProviderCache: EvmProvider | null = null; let walletConnectProviderCache: EvmProvider | null = null;
let walletConnectProviderChainId: number | null = null; let walletConnectProviderChainId: number | null = null;
@@ -574,6 +576,31 @@ function buildBalanceOfCalldata(owner: string) {
return `0x70a08231${toPaddedAddress(owner)}`; return `0x70a08231${toPaddedAddress(owner)}`;
} }
async function requestWalletWithTimeout<T>(
provider: EvmProvider,
args: { method: string; params?: unknown[] },
actionLabel = "钱包操作",
timeoutMs = WALLET_REQUEST_TIMEOUT_MS,
): Promise<T> {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
try {
return (await Promise.race([
provider.request(args),
new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
reject(
new Error(
`${actionLabel}长时间无响应,请确认钱包弹窗是否被拦截;如使用 Binance Web3 Wallet,请回到钱包确认或重新连接后再试。`,
),
);
}, timeoutMs);
}),
])) as T;
} finally {
if (timeoutId) clearTimeout(timeoutId);
}
}
function formatTokenUnits(amount: bigint, decimals: number) { function formatTokenUnits(amount: bigint, decimals: number) {
const safeDecimals = const safeDecimals =
Number.isFinite(decimals) && decimals >= 0 ? Math.floor(decimals) : 6; Number.isFinite(decimals) && decimals >= 0 ? Math.floor(decimals) : 6;
@@ -1835,17 +1862,23 @@ export function AccountCenter() {
const waitForReceipt = async ( const waitForReceipt = async (
txHash: string, txHash: string,
provider?: EvmProvider,
timeoutMs = 120000, timeoutMs = 120000,
pollMs = 3000, pollMs = 3000,
) => { ) => {
const eth = getEvmProvider(); const eth = provider || getEvmProvider();
if (!eth) throw new Error("No EVM wallet provider found"); if (!eth) throw new Error("No EVM wallet provider found");
const started = Date.now(); const started = Date.now();
while (Date.now() - started < timeoutMs) { while (Date.now() - started < timeoutMs) {
const receipt = (await eth.request({ const receipt = await requestWalletWithTimeout<{ status?: string } | null>(
method: "eth_getTransactionReceipt", eth,
params: [txHash], {
})) as { status?: string } | null; method: "eth_getTransactionReceipt",
params: [txHash],
},
"查询授权交易确认",
15_000,
);
if (receipt && receipt.status) { if (receipt && receipt.status) {
if (receipt.status === "0x1") return receipt; if (receipt.status === "0x1") return receipt;
throw new Error(`transaction reverted: ${txHash}`); throw new Error(`transaction reverted: ${txHash}`);
@@ -1938,16 +1971,24 @@ export function AccountCenter() {
targetChainId: number, targetChainId: number,
): Promise<void> => { ): Promise<void> => {
const currentChainIdHex = String( const currentChainIdHex = String(
(await eth.request({ method: "eth_chainId" })) || "", (await requestWalletWithTimeout<string>(
eth,
{ method: "eth_chainId" },
"读取钱包网络",
)) || "",
); );
const targetChainHex = `0x${targetChainId.toString(16)}`; const targetChainHex = `0x${targetChainId.toString(16)}`;
if (currentChainIdHex.toLowerCase() === targetChainHex.toLowerCase()) if (currentChainIdHex.toLowerCase() === targetChainHex.toLowerCase())
return; return;
try { try {
await eth.request({ await requestWalletWithTimeout(
method: "wallet_switchEthereumChain", eth,
params: [{ chainId: targetChainHex }], {
}); method: "wallet_switchEthereumChain",
params: [{ chainId: targetChainHex }],
},
"切换钱包网络",
);
} catch (err: any) { } catch (err: any) {
const code = Number(err?.code); const code = Number(err?.code);
const msg = String(err?.message || "").toLowerCase(); const msg = String(err?.message || "").toLowerCase();
@@ -1955,9 +1996,11 @@ export function AccountCenter() {
// If the error code indicates the chain is not added (4902), or it's Polygon (137) // If the error code indicates the chain is not added (4902), or it's Polygon (137)
if (code === 4902 || targetChainId === 137) { if (code === 4902 || targetChainId === 137) {
try { try {
await eth.request({ await requestWalletWithTimeout(
method: "wallet_addEthereumChain", eth,
params: [ {
method: "wallet_addEthereumChain",
params: [
{ {
chainId: "0x89", chainId: "0x89",
chainName: "Polygon Mainnet", chainName: "Polygon Mainnet",
@@ -1965,8 +2008,10 @@ export function AccountCenter() {
rpcUrls: ["https://polygon-rpc.com"], rpcUrls: ["https://polygon-rpc.com"],
blockExplorerUrls: ["https://polygonscan.com"], blockExplorerUrls: ["https://polygonscan.com"],
}, },
], ],
}); },
"添加 Polygon 网络",
);
return; return;
} catch (addErr: any) { } catch (addErr: any) {
err = addErr; err = addErr;
@@ -2010,9 +2055,11 @@ export function AccountCenter() {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
}; };
const accounts = (await eth.request({ const accounts = await requestWalletWithTimeout<string[]>(
method: "eth_requestAccounts", eth,
})) as string[]; { method: "eth_requestAccounts" },
"连接绑定钱包",
);
const address = String(accounts?.[0] || "").toLowerCase(); const address = String(accounts?.[0] || "").toLowerCase();
if (!address) if (!address)
throw new Error(isEn ? "Wallet account is empty." : "钱包账户为空"); throw new Error(isEn ? "Wallet account is empty." : "钱包账户为空");
@@ -2209,9 +2256,11 @@ export function AccountCenter() {
selectedInjectedProviderKey, selectedInjectedProviderKey,
); );
const eth = providerSelection.provider; const eth = providerSelection.provider;
const activeAccounts = (await eth.request({ const activeAccounts = await requestWalletWithTimeout<string[]>(
method: "eth_requestAccounts", eth,
})) as string[]; { method: "eth_requestAccounts" },
"连接付款钱包",
);
const activeAddress = String(activeAccounts?.[0] || "").toLowerCase(); const activeAddress = String(activeAccounts?.[0] || "").toLowerCase();
if (!activeAddress) if (!activeAddress)
throw new Error(isEn ? "Wallet account is empty." : "钱包账户为空"); throw new Error(isEn ? "Wallet account is empty." : "钱包账户为空");
@@ -2330,16 +2379,20 @@ export function AccountCenter() {
6, 6,
); );
const balanceHex = (await eth.request({ const balanceHex = await requestWalletWithTimeout<string>(
method: "eth_call", eth,
params: [ {
method: "eth_call",
params: [
{ {
to: tokenAddress, to: tokenAddress,
data: buildBalanceOfCalldata(payingWallet), data: buildBalanceOfCalldata(payingWallet),
}, },
"latest", "latest",
], ],
})) as string; },
`读取 ${tokenSymbol} 余额`,
);
const tokenBalance = BigInt(String(balanceHex || "0x0")); const tokenBalance = BigInt(String(balanceHex || "0x0"));
if (tokenBalance < amountUnits) { if (tokenBalance < amountUnits) {
const need = formatTokenUnits(amountUnits, tokenDecimals); const need = formatTokenUnits(amountUnits, tokenDecimals);
@@ -2349,16 +2402,20 @@ export function AccountCenter() {
); );
} }
const allowanceHex = (await eth.request({ const allowanceHex = await requestWalletWithTimeout<string>(
method: "eth_call", eth,
params: [ {
method: "eth_call",
params: [
{ {
to: tokenAddress, to: tokenAddress,
data: buildAllowanceCalldata(payingWallet, txPayload.to), data: buildAllowanceCalldata(payingWallet, txPayload.to),
}, },
"latest", "latest",
], ],
})) as string; },
`读取 ${tokenSymbol} 授权额度`,
);
const allowance = BigInt(String(allowanceHex || "0x0")); const allowance = BigInt(String(allowanceHex || "0x0"));
if (allowance < amountUnits) { if (allowance < amountUnits) {
@@ -2368,11 +2425,16 @@ export function AccountCenter() {
to: tokenAddress, to: tokenAddress,
data: buildApproveCalldata(txPayload.to, amountUnits), data: buildApproveCalldata(txPayload.to, amountUnits),
}; };
const approveHash = (await eth.request({ const approveHash = await requestWalletWithTimeout<string>(
method: "eth_sendTransaction", eth,
params: [approveParams], {
})) as string; method: "eth_sendTransaction",
await waitForReceipt(String(approveHash || "")); params: [approveParams],
},
`发起 ${tokenSymbol} 授权`,
WALLET_TRANSACTION_REQUEST_TIMEOUT_MS,
);
await waitForReceipt(String(approveHash || ""), eth);
approvedInThisRun = true; approvedInThisRun = true;
setPaymentInfo(`${tokenSymbol} 授权成功,正在发起支付...`); setPaymentInfo(`${tokenSymbol} 授权成功,正在发起支付...`);
} else { } else {
@@ -2388,10 +2450,15 @@ export function AccountCenter() {
payParams.value = txPayload.value; payParams.value = txPayload.value;
} }
const txHash = (await eth.request({ const txHash = await requestWalletWithTimeout<string>(
method: "eth_sendTransaction", eth,
params: [payParams], {
})) as string; method: "eth_sendTransaction",
params: [payParams],
},
"发起支付交易",
WALLET_TRANSACTION_REQUEST_TIMEOUT_MS,
);
const txHashNorm = String(txHash || "").toLowerCase(); const txHashNorm = String(txHash || "").toLowerCase();
setLastTxHash(txHashNorm); setLastTxHash(txHashNorm);
setLastPaymentStartedAt(Date.now()); setLastPaymentStartedAt(Date.now());
-1
View File
@@ -27,7 +27,6 @@ AMSC_AWOS_AIRPORTS: Dict[str, Dict[str, str]] = {
"shanghai": {"icao": "ZSPD", "label": "Shanghai Pudong"}, "shanghai": {"icao": "ZSPD", "label": "Shanghai Pudong"},
"beijing": {"icao": "ZBAA", "label": "Beijing Capital"}, "beijing": {"icao": "ZBAA", "label": "Beijing Capital"},
"guangzhou": {"icao": "ZGGG", "label": "Guangzhou Baiyun"}, "guangzhou": {"icao": "ZGGG", "label": "Guangzhou Baiyun"},
"shenzhen": {"icao": "ZGSZ", "label": "Shenzhen Bao'an"},
"chengdu": {"icao": "ZUUU", "label": "Chengdu Shuangliu"}, "chengdu": {"icao": "ZUUU", "label": "Chengdu Shuangliu"},
"chongqing": {"icao": "ZUCK", "label": "Chongqing Jiangbei"}, "chongqing": {"icao": "ZUCK", "label": "Chongqing Jiangbei"},
"wuhan": {"icao": "ZHHH", "label": "Wuhan Tianhe"}, "wuhan": {"icao": "ZHHH", "label": "Wuhan Tianhe"},
+1 -2
View File
@@ -560,7 +560,7 @@ SETTLEMENT_RUNWAY_PAIRS: Dict[str, Set[Tuple[str, str]]] = {
# All cities with active runway observation data (AMSC AWOS / AMOS). # All cities with active runway observation data (AMSC AWOS / AMOS).
RUNWAY_OBSERVATION_CITIES = { RUNWAY_OBSERVATION_CITIES = {
"shanghai", "beijing", "guangzhou", "shenzhen", "shanghai", "beijing", "guangzhou",
"chengdu", "chongqing", "wuhan", "qingdao", "chengdu", "chongqing", "wuhan", "qingdao",
"seoul", "busan", "seoul", "busan",
} }
@@ -574,7 +574,6 @@ WIND_REGIME: Dict[str, Dict[str, Tuple[int, int]]] = {
"qingdao": {"sea_breeze": (90, 180), "warm_advection": (200, 300)}, "qingdao": {"sea_breeze": (90, 180), "warm_advection": (200, 300)},
"beijing": {"sea_breeze": (120, 200), "warm_advection": (220, 320)}, "beijing": {"sea_breeze": (120, 200), "warm_advection": (220, 320)},
"guangzhou": {"sea_breeze": (120, 200), "warm_advection": (200, 300)}, "guangzhou": {"sea_breeze": (120, 200), "warm_advection": (200, 300)},
"shenzhen": {"sea_breeze": (120, 200), "warm_advection": (200, 300)},
"chengdu": {"sea_breeze": (0, 0), "warm_advection": (0, 0)}, "chengdu": {"sea_breeze": (0, 0), "warm_advection": (0, 0)},
"chongqing": {"sea_breeze": (0, 0), "warm_advection": (0, 0)}, "chongqing": {"sea_breeze": (0, 0), "warm_advection": (0, 0)},
"wuhan": {"sea_breeze": (0, 0), "warm_advection": (0, 0)}, "wuhan": {"sea_breeze": (0, 0), "warm_advection": (0, 0)},