diff --git a/apps/miniprogram/src/hooks/useLongPolling.ts b/apps/miniprogram/src/hooks/useLongPolling.ts index 435a3bb..ea998f7 100644 --- a/apps/miniprogram/src/hooks/useLongPolling.ts +++ b/apps/miniprogram/src/hooks/useLongPolling.ts @@ -10,7 +10,7 @@ interface LongPollOptions { enabled: boolean; /** 成功轮询间隔 ms,默认 3000 */ intervalMs?: number; - /** 连续失败上限,默认 50 */ + /** 连续失败上限,默认 10 */ maxFailures?: number; } @@ -27,7 +27,7 @@ export function useLongPolling({ onData, enabled, intervalMs = 3000, - maxFailures = 50, + maxFailures = 10, }: LongPollOptions) { const generation = useRef(0); const mountedRef = useRef(true); diff --git a/apps/miniprogram/src/services/consultation.ts b/apps/miniprogram/src/services/consultation.ts index 515a228..1c101ef 100644 --- a/apps/miniprogram/src/services/consultation.ts +++ b/apps/miniprogram/src/services/consultation.ts @@ -1,4 +1,4 @@ -import { api, requestWithTimeout } from './request'; +import { api, requestUnlimited } from './request'; export interface ConsultationSession { id: string; @@ -72,5 +72,5 @@ export async function pollMessages(sessionId: string, afterId?: string) { params.set('timeout', '25'); const query = params.toString(); const path = `/health/consultation-sessions/${sessionId}/messages/poll${query ? '?' + query : ''}`; - return requestWithTimeout('GET', path, undefined, 30000); + return requestUnlimited('GET', path, undefined, 30000); } diff --git a/apps/miniprogram/src/services/doctor/consultation.ts b/apps/miniprogram/src/services/doctor/consultation.ts index 6896adb..c43f250 100644 --- a/apps/miniprogram/src/services/doctor/consultation.ts +++ b/apps/miniprogram/src/services/doctor/consultation.ts @@ -1,4 +1,4 @@ -import { api, requestWithTimeout } from '../request'; +import { api, requestUnlimited } from '../request'; // ── Consultation (doctor view) ───────────────────── @@ -71,7 +71,7 @@ export async function pollMessages(sessionId: string, afterId?: string) { params.set('timeout', '25'); const query = params.toString(); const path = `/health/consultation-sessions/${sessionId}/messages/poll${query ? '?' + query : ''}`; - return requestWithTimeout('GET', path, undefined, 30000); + return requestUnlimited('GET', path, undefined, 30000); } export interface ConsultationStats { diff --git a/apps/miniprogram/src/services/request.ts b/apps/miniprogram/src/services/request.ts index b3c4b17..19c6ab0 100644 --- a/apps/miniprogram/src/services/request.ts +++ b/apps/miniprogram/src/services/request.ts @@ -56,7 +56,7 @@ class ConcurrencyLimiter { } } -const limiter = new ConcurrencyLimiter(8); +const limiter = new ConcurrencyLimiter(12); // --- Response cache + deduplication --- @@ -221,13 +221,24 @@ async function doRefresh(): Promise { return false; } +// --- reLaunch 去重 --- + +let reLaunchPromise: Promise | null = null; + +function safeReLaunch(url: string): void { + if (reLaunchPromise) return; + reLaunchPromise = Taro.reLaunch({ url }).then(() => {}, () => {}).then(() => { + setTimeout(() => { reLaunchPromise = null; }, 2000); + }); +} + // --- Core request --- -async function request(method: string, path: string, data?: unknown, timeout?: number, signal?: AbortSignal): Promise { +async function request(method: string, path: string, data?: unknown, timeout?: number, signal?: AbortSignal, bypassLimiter = false): Promise { let retryCount401 = 0; for (;;) { if (signal?.aborted) throw new Error('请求已取消'); - await limiter.acquire(); + if (!bypassLimiter) await limiter.acquire(); try { const headers = await getHeaders(); const url = `${BASE_URL}${path}`; @@ -262,7 +273,7 @@ async function request(method: string, path: string, data?: unknown, timeout? const pages = Taro.getCurrentPages(); const currentPath = pages[pages.length - 1]?.path || ''; if (!currentPath.includes('pages/login')) { - Taro.reLaunch({ url: '/pages/login/index' }); + safeReLaunch('/pages/login/index'); } } throw new Error('登录已过期'); @@ -287,7 +298,7 @@ async function request(method: string, path: string, data?: unknown, timeout? } return body.data as T; } finally { - limiter.release(); + if (!bypassLimiter) limiter.release(); } } } @@ -349,6 +360,11 @@ export async function requestWithTimeout(method: string, path: string, data?: return request(method, path, data, timeout); } +/** 绕过并发限制的请求,用于长轮询等长时间 hang 的请求 */ +export async function requestUnlimited(method: string, path: string, data?: unknown, timeout?: number): Promise { + return request(method, path, data, timeout, undefined, true); +} + /** 重置所有模块级状态,用于测试隔离 */ export function resetForTesting(): void { limiter.reset();