Some checks failed
Frontend CI / build-android-apk (push) Has been cancelled
Frontend CI / build-and-push-web (push) Has been cancelled
Frontend CI / ota-android (push) Has been cancelled
Frontend CI / ota-android (pull_request) Has been skipped
Frontend CI / build-and-push-web (pull_request) Has been cancelled
Frontend CI / build-android-apk (pull_request) Has been cancelled
Replace WebSocket-based real-time communication with Server-Sent Events (SSE) across the messaging infrastructure. This includes: - Create new SSEClient datasource to manage SSE connections - Create new SSEMessageHandler to process SSE events - Update ProcessMessageUseCase to use SSEClient instead of WebSocketClient - Update MessageManager and MessageStateManager to work with SSE handlers - Rename connection state variables from `isWebSocketConnected` to `isSSEConnected` - Update type definitions in dto.ts (WSEventType → SSEEventType, etc.) - Delete obsolete WebSocketClient and WebSocketMessageHandler files - Update comments and documentation to reflect SSE terminology This refactoring aligns with the backend's SSE implementation for better compatibility with React Native's networking capabilities.
301 lines
7.9 KiB
TypeScript
301 lines
7.9 KiB
TypeScript
/**
|
||
* 后台保活服务
|
||
* 使用 expo-background-fetch 和 expo-task-manager 实现 App 后台保活
|
||
*
|
||
* 功能:
|
||
* - 定期后台任务保持 App 在内存中
|
||
* - 配合 SSE 服务保持长连接
|
||
* - 收到消息时触发震动提示
|
||
*/
|
||
|
||
import { AppState, AppStateStatus, Platform } from 'react-native';
|
||
import * as BackgroundFetch from 'expo-background-fetch';
|
||
import * as TaskManager from 'expo-task-manager';
|
||
import * as Haptics from 'expo-haptics';
|
||
import { sseService } from './sseService';
|
||
|
||
// 后台任务名称
|
||
const BACKGROUND_FETCH_TASK = 'background-fetch-keepalive';
|
||
const REALTIME_KEEPALIVE_TASK = 'realtime-keepalive';
|
||
|
||
// 后台任务间隔(Android 最小 15 分钟,iOS 最小 15 分钟)
|
||
const BACKGROUND_INTERVAL = 15; // 15 分钟
|
||
|
||
// 震动配置
|
||
interface VibrationConfig {
|
||
enabled: boolean; // 是否启用震动
|
||
onChatMessage: boolean; // 私聊消息震动
|
||
onGroupMessage: boolean; // 群聊消息震动
|
||
onNotification: boolean; // 系统通知震动
|
||
style: Haptics.ImpactFeedbackStyle; // 震动样式
|
||
}
|
||
|
||
// 默认震动配置
|
||
const defaultVibrationConfig: VibrationConfig = {
|
||
enabled: true,
|
||
onChatMessage: true,
|
||
onGroupMessage: true,
|
||
onNotification: true,
|
||
style: Haptics.ImpactFeedbackStyle.Light,
|
||
};
|
||
|
||
// 后台服务状态
|
||
let isInitialized = false;
|
||
let vibrationConfig: VibrationConfig = { ...defaultVibrationConfig };
|
||
let appStateSubscription: any = null;
|
||
|
||
// 定义后台任务
|
||
TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
|
||
try {
|
||
// 检查 SSE 连接状态
|
||
if (!sseService.isConnected()) {
|
||
await sseService.connect();
|
||
}
|
||
|
||
// 返回收到新数据
|
||
return BackgroundFetch.BackgroundFetchResult.NewData;
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 后台任务执行失败:', error);
|
||
return BackgroundFetch.BackgroundFetchResult.Failed;
|
||
}
|
||
});
|
||
|
||
// SSE 保活任务
|
||
TaskManager.defineTask(REALTIME_KEEPALIVE_TASK, async () => {
|
||
try {
|
||
if (!sseService.isConnected()) {
|
||
await sseService.connect();
|
||
}
|
||
return BackgroundFetch.BackgroundFetchResult.NewData;
|
||
} catch (error) {
|
||
console.error('[BackgroundService] SSE 保活失败:', error);
|
||
return BackgroundFetch.BackgroundFetchResult.Failed;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 触发震动反馈
|
||
* @param type 震动类型
|
||
*/
|
||
export async function triggerVibration(
|
||
type: 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error' = 'light'
|
||
): Promise<void> {
|
||
if (!vibrationConfig.enabled) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
switch (type) {
|
||
case 'light':
|
||
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||
break;
|
||
case 'medium':
|
||
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||
break;
|
||
case 'heavy':
|
||
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
|
||
break;
|
||
case 'success':
|
||
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||
break;
|
||
case 'warning':
|
||
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
|
||
break;
|
||
case 'error':
|
||
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 震动反馈失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 收到消息时触发震动
|
||
* @param messageType 消息类型
|
||
*/
|
||
export async function vibrateOnMessage(messageType: 'chat' | 'group_message' | 'notification'): Promise<void> {
|
||
if (!vibrationConfig.enabled) {
|
||
return;
|
||
}
|
||
|
||
switch (messageType) {
|
||
case 'chat':
|
||
if (vibrationConfig.onChatMessage) {
|
||
await triggerVibration('light');
|
||
}
|
||
break;
|
||
case 'group_message':
|
||
if (vibrationConfig.onGroupMessage) {
|
||
await triggerVibration('light');
|
||
}
|
||
break;
|
||
case 'notification':
|
||
if (vibrationConfig.onNotification) {
|
||
await triggerVibration('medium');
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 配置震动设置
|
||
*/
|
||
export function setVibrationConfig(config: Partial<VibrationConfig>): void {
|
||
vibrationConfig = { ...vibrationConfig, ...config };
|
||
}
|
||
|
||
/**
|
||
* 获取当前震动配置
|
||
*/
|
||
export function getVibrationConfig(): VibrationConfig {
|
||
return { ...vibrationConfig };
|
||
}
|
||
|
||
/**
|
||
* 启用/禁用震动
|
||
*/
|
||
export function setVibrationEnabled(enabled: boolean): void {
|
||
vibrationConfig.enabled = enabled;
|
||
}
|
||
|
||
/**
|
||
* 注册后台任务
|
||
*/
|
||
async function registerBackgroundTasks(): Promise<void> {
|
||
try {
|
||
// 检查是否已注册
|
||
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_FETCH_TASK);
|
||
if (!isRegistered) {
|
||
await BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
|
||
minimumInterval: BACKGROUND_INTERVAL * 60, // 转换为秒
|
||
stopOnTerminate: false, // App 终止后继续运行
|
||
startOnBoot: true, // 设备启动后自动运行
|
||
});
|
||
}
|
||
|
||
// 注册 SSE 保活任务
|
||
const isSseKeepaliveRegistered = await TaskManager.isTaskRegisteredAsync(REALTIME_KEEPALIVE_TASK);
|
||
if (!isSseKeepaliveRegistered) {
|
||
await BackgroundFetch.registerTaskAsync(REALTIME_KEEPALIVE_TASK, {
|
||
minimumInterval: 60, // 1 分钟检查一次
|
||
stopOnTerminate: false,
|
||
startOnBoot: true,
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 注册后台任务失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消后台任务
|
||
*/
|
||
async function unregisterBackgroundTasks(): Promise<void> {
|
||
try {
|
||
await BackgroundFetch.unregisterTaskAsync(BACKGROUND_FETCH_TASK);
|
||
await BackgroundFetch.unregisterTaskAsync(REALTIME_KEEPALIVE_TASK);
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 取消后台任务失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置 App 状态监听
|
||
*/
|
||
function setupAppStateListener(): void {
|
||
if (appStateSubscription) {
|
||
return;
|
||
}
|
||
|
||
appStateSubscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {
|
||
void nextAppState;
|
||
if (nextAppState === 'active') {
|
||
// App 回到前台,确保连接
|
||
if (!sseService.isConnected()) {
|
||
sseService.connect();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 初始化后台保活服务
|
||
*/
|
||
export async function initBackgroundService(): Promise<boolean> {
|
||
if (isInitialized) {
|
||
return true;
|
||
}
|
||
|
||
try {
|
||
// 检查后台任务状态
|
||
const status = await BackgroundFetch.getStatusAsync();
|
||
if (status !== BackgroundFetch.BackgroundFetchStatus.Available) {
|
||
console.warn('[BackgroundService] 后台任务不可用,状态:', status);
|
||
// 即使后台任务不可用,也继续初始化其他功能
|
||
}
|
||
|
||
// 注册后台任务
|
||
await registerBackgroundTasks();
|
||
|
||
// 设置 App 状态监听
|
||
setupAppStateListener();
|
||
|
||
isInitialized = true;
|
||
return true;
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 初始化失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 停止后台保活服务
|
||
*/
|
||
export async function stopBackgroundService(): Promise<void> {
|
||
try {
|
||
await unregisterBackgroundTasks();
|
||
|
||
if (appStateSubscription) {
|
||
appStateSubscription.remove();
|
||
appStateSubscription = null;
|
||
}
|
||
|
||
isInitialized = false;
|
||
} catch (error) {
|
||
console.error('[BackgroundService] 停止服务失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查后台服务状态
|
||
*/
|
||
export async function checkBackgroundStatus(): Promise<{
|
||
isAvailable: boolean;
|
||
isInitialized: boolean;
|
||
registeredTasks: string[];
|
||
}> {
|
||
const status = await BackgroundFetch.getStatusAsync();
|
||
const registeredTasks = await TaskManager.getRegisteredTasksAsync();
|
||
|
||
return {
|
||
isAvailable: status === BackgroundFetch.BackgroundFetchStatus.Available,
|
||
isInitialized,
|
||
registeredTasks: registeredTasks.map(task => task.taskName),
|
||
};
|
||
}
|
||
|
||
// 导出服务实例
|
||
export const backgroundService = {
|
||
init: initBackgroundService,
|
||
stop: stopBackgroundService,
|
||
vibrate: triggerVibration,
|
||
vibrateOnMessage,
|
||
setVibrationConfig,
|
||
getVibrationConfig,
|
||
setVibrationEnabled,
|
||
checkStatus: checkBackgroundStatus,
|
||
};
|
||
|
||
export default backgroundService;
|