From b93e165590d32f54ff4c5eaab53138549901b3af Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 16 Jul 2026 01:09:56 +0800 Subject: [PATCH] fix(web): recover transient map SDK failures --- .../apps/web/src/integrations/amap.test.ts | 45 +++++++++++++++++- .../apps/web/src/integrations/amap.ts | 46 +++++++++++++------ 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/integrations/amap.test.ts b/vehicle-data-platform/apps/web/src/integrations/amap.test.ts index e14784aa..d2742b20 100644 --- a/vehicle-data-platform/apps/web/src/integrations/amap.test.ts +++ b/vehicle-data-platform/apps/web/src/integrations/amap.test.ts @@ -1,5 +1,18 @@ -import { expect, test } from 'vitest'; -import { buildAMapMarkerURL, gcj02ToWgs84, isValidAMapCoordinate, wgs84ToGcj02 } from './amap'; +import { afterEach, expect, test, vi } from 'vitest'; +import { + buildAMapMarkerURL, + gcj02ToWgs84, + isValidAMapCoordinate, + loadAMap, + type AMapLike, + wgs84ToGcj02 +} from './amap'; + +afterEach(() => { + delete window.__LINGNIU_APP_CONFIG__; + delete window.AMapLoader; + document.querySelector('script[src="https://webapi.amap.com/loader.js"]')?.remove(); +}); test('accepts coordinates inside the China vehicle service range', () => { expect(isValidAMapCoordinate(116.397128, 39.916527)).toBe(true); @@ -30,3 +43,31 @@ test('keeps coordinates outside China unchanged and converts AMap marker links a expect(position[0]).toBeCloseTo(116.40337, 5); expect(position[1]).toBeCloseTo(39.91793, 5); }); + +test('retries the SDK load after a transient AMap loader rejection', async () => { + const amap = {} as AMapLike; + const load = vi.fn() + .mockRejectedValueOnce(new Error('temporary network failure')) + .mockResolvedValueOnce(amap); + window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' }; + window.AMapLoader = { load }; + + await expect(loadAMap(['AMap.Geocoder'])).rejects.toThrow('temporary network failure'); + await expect(loadAMap(['AMap.Geocoder'])).resolves.toBe(amap); + expect(load).toHaveBeenCalledTimes(2); +}); + +test('removes a failed loader script so a later request can recover', async () => { + const amap = {} as AMapLike; + window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' }; + + const firstAttempt = loadAMap(['AMap.ToolBar']); + const failedScript = document.querySelector('script[src="https://webapi.amap.com/loader.js"]'); + expect(failedScript).not.toBeNull(); + failedScript?.dispatchEvent(new Event('error')); + await expect(firstAttempt).rejects.toThrow('高德地图 Loader 加载失败'); + expect(document.querySelector('script[src="https://webapi.amap.com/loader.js"]')).toBeNull(); + + window.AMapLoader = { load: vi.fn().mockResolvedValue(amap) }; + await expect(loadAMap(['AMap.ToolBar'])).resolves.toBe(amap); +}); diff --git a/vehicle-data-platform/apps/web/src/integrations/amap.ts b/vehicle-data-platform/apps/web/src/integrations/amap.ts index 4bf7cbb2..4115c3ef 100644 --- a/vehicle-data-platform/apps/web/src/integrations/amap.ts +++ b/vehicle-data-platform/apps/web/src/integrations/amap.ts @@ -104,16 +104,31 @@ function loadScript(src: string) { } const existing = document.querySelector(`script[src="${src}"]`); if (existing) { + if (existing.dataset.loadState === 'loaded') { + existing.remove(); + loadScript(src).then(resolve, reject); + return; + } existing.addEventListener('load', () => resolve(), { once: true }); - existing.addEventListener('error', () => reject(new Error('高德地图 Loader 加载失败')), { once: true }); + existing.addEventListener('error', () => { + existing.remove(); + reject(new Error('高德地图 Loader 加载失败')); + }, { once: true }); if (window.AMapLoader) resolve(); return; } const script = document.createElement('script'); script.src = src; script.async = true; - script.onload = () => resolve(); - script.onerror = () => reject(new Error('高德地图 Loader 加载失败')); + script.dataset.loadState = 'loading'; + script.onload = () => { + script.dataset.loadState = 'loaded'; + resolve(); + }; + script.onerror = () => { + script.remove(); + reject(new Error('高德地图 Loader 加载失败')); + }; document.head.appendChild(script); }); } @@ -221,16 +236,21 @@ export function loadAMap(plugins: AMapPlugin[] = ['AMap.Scale']) { } else if (config.securityJsCode) { window._AMapSecurityConfig = { securityJsCode: config.securityJsCode }; } - const promise = loadScript('https://webapi.amap.com/loader.js').then(() => { - if (!window.AMapLoader) { - throw new Error('高德地图 Loader 不可用'); - } - return window.AMapLoader.load({ - key: config.webJsKey, - version: '2.0', - plugins - }) as Promise; - }); + const promise = loadScript('https://webapi.amap.com/loader.js') + .then(() => { + if (!window.AMapLoader) { + throw new Error('高德地图 Loader 不可用'); + } + return window.AMapLoader.load({ + key: config.webJsKey, + version: '2.0', + plugins + }) as Promise; + }) + .catch((error: unknown) => { + amapLoaderPromises.delete(key); + throw error; + }); amapLoaderPromises.set(key, promise); return promise; }