文件
OmniCard/mpuniapp/common/offline-queue.uts

195 行
4.6 KiB
Plaintext

/**
* OmniCard AI (CRMmp) - 移动端弱网离线视觉指示器与断网重放队列
* 严格遵循 AGENTS.md 规范与 uts 强类型规范,一套代码兼顾 App 与 Web/H5
*/
export interface OfflineAction {
id?: string;
type: string;
url: string;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
data?: any;
header?: Record<string, any>;
timestamp?: number;
retryCount?: number;
}
export interface OfflineQueueStatus {
isOnline: boolean;
networkType: string;
pendingCount: number;
lastSyncTime?: number;
}
export interface ReplayResult {
total: number;
success: number;
failed: number;
}
const STORAGE_KEY = 'omni_offline_queue';
const DEFAULT_MAX_RETRIES = 3;
let onlineState = true;
let currentNetworkType = 'wifi';
let lastSyncTimestamp: number | undefined = undefined;
/**
* 获取当前网络在线状态
*/
export function isOnline(): boolean {
return onlineState;
}
/**
* 手动或通过原生事件更新网络状态
*/
export function setNetworkStatus(online: boolean, networkType = 'unknown'): void {
onlineState = online;
currentNetworkType = networkType;
}
/**
* 获取离线队列所有条目
*/
export function getOfflineQueue(): OfflineAction[] {
try {
const raw = uni.getStorageSync(STORAGE_KEY);
if (!raw) return [];
const list = JSON.parse(String(raw));
return Array.isArray(list) ? list : [];
} catch (e) {
return [];
}
}
/**
* 清空离线队列
*/
export function clearOfflineQueue(): void {
try {
uni.removeStorageSync(STORAGE_KEY);
} catch (e) {}
}
/**
* 暂存一个离线操作到本地队列 (FIFO)
*/
export function enqueueOfflineAction(action: OfflineAction): number {
const queue = getOfflineQueue();
const item: OfflineAction = {
...action,
id: action.id || `offline_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
timestamp: action.timestamp || Date.now(),
retryCount: action.retryCount || 0,
};
queue.push(item);
try {
uni.setStorageSync(STORAGE_KEY, JSON.stringify(queue));
} catch (e) {}
return queue.length;
}
/**
* 网络恢复时自动重放离线队列
*/
export async function replayOfflineQueue(
executor: (action: OfflineAction) => Promise<boolean>,
maxRetries = DEFAULT_MAX_RETRIES
): Promise<ReplayResult> {
const queue = getOfflineQueue();
if (queue.length === 0) {
return { total: 0, success: 0, failed: 0 };
}
const remaining: OfflineAction[] = [];
let successCount = 0;
let failedCount = 0;
for (const item of queue) {
let ok = false;
try {
ok = await executor(item);
} catch (e) {
ok = false;
}
if (ok) {
successCount++;
} else {
failedCount++;
const nextRetries = (item.retryCount || 0) + 1;
if (nextRetries < maxRetries) {
remaining.push({
...item,
retryCount: nextRetries,
});
}
}
}
try {
if (remaining.length > 0) {
uni.setStorageSync(STORAGE_KEY, JSON.stringify(remaining));
} else {
uni.removeStorageSync(STORAGE_KEY);
}
} catch (e) {}
lastSyncTimestamp = Date.now();
return {
total: queue.length,
success: successCount,
failed: failedCount,
};
}
/**
* 获取离线队列综合状态
*/
export function getOfflineQueueStatus(): OfflineQueueStatus {
const queue = getOfflineQueue();
return {
isOnline: onlineState,
networkType: currentNetworkType,
pendingCount: queue.length,
lastSyncTime: lastSyncTimestamp,
};
}
/**
* 格式化多语言横幅提示文案
*/
export function formatOfflineBannerText(lang: string = 'en', pendingCount = 0): string {
const normalized = (lang || 'en').toLowerCase().split('-')[0];
switch (normalized) {
case 'zh':
return pendingCount > 0
? `网络已断开 (${pendingCount} 条暂存待同步)`
: '网络已断开,正在尝试重连...';
case 'ar':
return pendingCount > 0
? `غير متصل بالشبكة (${pendingCount} في الانتظار)`
: 'غير متصل بالشبكة، جاري إعادة الاتصال...';
case 'es':
return pendingCount > 0
? `Sin conexión (${pendingCount} pendientes)`
: 'Sin conexión, reintentando...';
case 'fr':
return pendingCount > 0
? `Hors ligne (${pendingCount} en attente)`
: 'Hors ligne, reconnexion en cours...';
case 'ja':
return pendingCount > 0
? `オフライン (${pendingCount} 件の未送信保留)`
: 'オフラインです。再接続中...';
case 'en':
default:
return pendingCount > 0
? `Offline mode (${pendingCount} pending)`
: 'Offline, reconnecting...';
}
}