/** * 用户同步页轮询逻辑(纯函数/可注入依赖,vitest 用 fake timers 覆盖) * * - waitSyncTerminal:手动补拉 202 受理后等待批次离开 running(间隔轮询 + * 超时上限;终态即 resolve,避免页面无限转圈) * - createAutoRefresh:页面打开期间的定时状态刷新(10s);立即执行一次 + * 间隔触发 + 重入保护(慢响应不堆叠)+ 幂等 start/stop(组件 * onUnmounted 清理,防路由离开后仍在请求) */ export interface WaitSyncOptions { /** 轮询间隔(默认 1.5s:批次通常秒级完成) */ intervalMs?: number /** 总超时(默认 45s:超过视为异常,交调用方展示错误) */ timeoutMs?: number } const DEFAULT_INTERVAL_MS = 1500 const DEFAULT_TIMEOUT_MS = 45_000 function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)) } /** * 轮询同步状态直到批次离开 running(首次查询即终态则立刻 resolve)。 * 超时 reject(Error.message = 'timeout');fetchStatus 的网络错误向上抛。 */ export function waitSyncTerminal( fetchStatus: () => Promise, options: WaitSyncOptions = {} ): Promise { const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS const deadline = Date.now() + timeoutMs return (async function poll(): Promise { const status = await fetchStatus() if (status.state !== 'running') return status if (Date.now() >= deadline) { throw new Error('timeout') } await delay(intervalMs) return poll() })() } export interface AutoRefreshHandle { /** 开始(立即执行一次 + 间隔触发;重复调用幂等) */ start: () => void /** 停止并清理定时器(重复调用幂等) */ stop: () => void /** 是否在刷新周期内 */ isRunning: () => boolean } /** * 周期刷新:start 立即触发一次;执行中的回调未完成时不堆叠下一次。 * 返回的 handle 由组件持有,onUnmounted 时 stop()。 */ export function createAutoRefresh( fn: () => void | Promise, intervalMs = 10_000 ): AutoRefreshHandle { let timer: ReturnType | null = null let inFlight = false async function run(): Promise { if (inFlight) return inFlight = true try { await fn() } finally { inFlight = false } } return { start() { if (timer !== null) return void run() timer = setInterval(() => void run(), intervalMs) }, stop() { if (timer === null) return clearInterval(timer) timer = null }, isRunning() { return timer !== null } } }