// 位置追踪服务 - 处理用户签到后的位置追踪和状态管理 import { UserInfo, LocationData } from '../types'; import apiService from './apiService'; import userService from './userService'; import mapService from './mapService'; /** * 在线用户信息接口 */ export interface OnlineUserInfo { userId: number; name: string; avatarUrl: string; role: string; // 用户角色:'admin' | 'delivery_person' lastLocation: LocationData; lastUpdateTime: number; status: 'online' | 'offline' | 'timeout'; } /** * 位置追踪服务类 * 提供用户签到后的位置追踪、状态管理和位置分发功能 */ class LocationTrackingService { // 在线用户列表 private onlineUsers: Map; // 位置更新定时器 private locationUpdateTimer: number | null; // 状态检查定时器 private statusCheckTimer: number | null; // 位置更新回调集合 private locationUpdateCallbacks: Set<(locations: OnlineUserInfo[]) => void>; // 用户状态变化回调集合 private userStatusCallbacks: Set<(userId: number, status: 'online' | 'offline' | 'timeout') => void>; // 当前用户是否已签到 private isSignedIn: boolean; // 用户信息缓存 private userInfo: UserInfo | null; // 取消订阅回调函数 private unsubscribeCallback: (() => void) | null; /** * 构造函数 */ constructor() { this.onlineUsers = new Map(); this.locationUpdateTimer = null; this.statusCheckTimer = null; this.locationUpdateCallbacks = new Set(); this.userStatusCallbacks = new Set(); this.isSignedIn = false; this.userInfo = null; this.unsubscribeCallback = null; } /** * 用户签到后启动位置追踪 */ public async startTrackingAfterSignIn(): Promise { const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { throw new Error('用户未登录,无法启动位置追踪'); } this.isSignedIn = true; // 将当前用户添加到在线列表 await this.addUserToOnlineList(userInfo); // 签到消息已通过REST API处理,服务器会自动处理订阅逻辑 // 设置实时位置订阅(初始化WebSocket连接) await this.setupRealTimeSubscription(); // WebSocket连接成功后立即发送第一次位置信息 try { const location = await this.getCurrentLocation(); await this.updateUserLocation(location); console.log('WebSocket连接成功后立即发送第一次位置信息'); } catch (error) { console.error('立即发送位置信息失败:', error); } // 启动位置更新定时器(每30秒更新一次) this.startLocationUpdateTimer(); // 启动状态检查定时器(每60秒检查一次) this.startStatusCheckTimer(); console.log('位置追踪服务已启动'); } /** * 初始化位置追踪服务 */ public async initialize(): Promise { try { // 注册位置更新回调 apiService.onLocationUpdate((locationUpdate) => { this.handleLocationUpdate(locationUpdate); }); // 注册在线用户列表回调 apiService.onOnlineUserList((userList) => { this.handleOnlineUserList(userList); }); console.log('[LocationTrackingService] 位置追踪服务初始化完成'); } catch (error) { console.error('[LocationTrackingService] 初始化失败:', error); throw error; } } /** * 用户签退后停止位置追踪 */ public async stopTracking(): Promise { const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { return; } this.isSignedIn = false; // 签退消息已通过REST API处理,服务器会自动处理取消订阅逻辑 // 从在线列表中移除用户 this.removeUserFromOnlineList(userInfo.id); // 停止定时器 this.stopTimers(); // 关闭实时位置订阅(服务器会自动处理取消订阅) this.teardownRealTimeSubscription(); console.log('位置追踪服务已停止'); } /** * 手动更新用户位置 */ public async updateUserLocation(location: { longitude: number; latitude: number }): Promise { const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id || !this.isSignedIn) { console.warn('[位置追踪服务] 用户未登录或未签到,无法更新位置'); return; } console.log('[位置追踪服务] 前端发送给服务器的位置信息:'); console.log(`- 用户ID: ${userInfo.id}`); console.log(`- 经度: ${location.longitude}`); console.log(`- 纬度: ${location.latitude}`); console.log(`- 时间戳: ${Date.now()}`); const locationData: LocationData = { userId: userInfo.id, longitude: location.longitude, latitude: location.latitude, timestamp: Date.now() }; // 真实模式:通过WebSocket发送位置更新 console.log('[位置追踪服务] 真实模式:通过WebSocket发送位置更新'); await apiService.sendLocationUpdate(userInfo.id, location.latitude, location.longitude); // 同时更新本地缓存 this.updateLocalUserLocation(userInfo.id, locationData); // 通知所有回调 this.notifyLocationUpdateCallbacks(); console.log('[位置追踪服务] 位置更新完成,已通知回调'); } /** * 获取所有在线用户信息 */ public getOnlineUsers(): OnlineUserInfo[] { return Array.from(this.onlineUsers.values()).filter(user => user.status === 'online' ); } /** * 订阅位置更新 */ public subscribeToLocationUpdates(callback: (locations: OnlineUserInfo[]) => void): void { this.locationUpdateCallbacks.add(callback); } /** * 取消订阅位置更新 */ public unsubscribeFromLocationUpdates(callback: (locations: OnlineUserInfo[]) => void): void { this.locationUpdateCallbacks.delete(callback); } /** * 订阅用户状态变化 */ public subscribeToUserStatusChanges(callback: (userId: number, status: 'online' | 'offline' | 'timeout') => void): void { this.userStatusCallbacks.add(callback); } /** * 取消订阅用户状态变化 */ public unsubscribeFromUserStatusChanges(callback: (userId: number, status: 'online' | 'offline' | 'timeout') => void): void { this.userStatusCallbacks.delete(callback); } /** * 检查用户是否在线 */ public isUserOnline(userId: number): boolean { const userInfo = this.onlineUsers.get(userId); return userInfo ? userInfo.status === 'online' : false; } // ===== 私有方法 ===== /** * 添加用户到在线列表 */ private async addUserToOnlineList(userInfo: UserInfo): Promise { const currentLocation = await this.getCurrentLocation(); const onlineUser: OnlineUserInfo = { userId: userInfo.id, name: userInfo.name || '未知用户', avatarUrl: '/images/user-avatar.png', role: userInfo.role || 'delivery_person', // 用户角色 lastLocation: currentLocation, lastUpdateTime: Date.now(), status: 'online' }; this.onlineUsers.set(userInfo.id, onlineUser); console.log(`用户 ${userInfo.id} 已添加到在线列表`); } /** * 从在线列表中移除用户 */ private removeUserFromOnlineList(userId: number): void { if (this.onlineUsers.has(userId)) { this.onlineUsers.delete(userId); // 通知状态变化回调 this.notifyUserStatusChange(userId, 'offline'); console.log(`用户 ${userId} 已从在线列表移除`); } } /** * 更新本地用户位置 */ private updateLocalUserLocation(userId: number, location: LocationData): void { const userInfo = this.onlineUsers.get(userId); if (userInfo) { userInfo.lastLocation = location; userInfo.lastUpdateTime = Date.now(); userInfo.status = 'online'; this.onlineUsers.set(userId, userInfo); } } /** * 启动位置更新定时器 */ private startLocationUpdateTimer(): void { this.stopLocationUpdateTimer(); this.locationUpdateTimer = setInterval(async () => { if (this.isSignedIn) { try { const location = await this.getCurrentLocation(); await this.updateUserLocation(location); } catch (error) { console.error('自动位置更新失败:', error); } } }, 30000); // 每30秒更新一次 } /** * 启动状态检查定时器 */ private startStatusCheckTimer(): void { this.stopStatusCheckTimer(); this.statusCheckTimer = setInterval(() => { this.checkUserStatuses(); }, 60000); // 每60秒检查一次 } /** * 检查用户状态 */ private checkUserStatuses(): void { const now = Date.now(); const timeoutThreshold = 120000; // 2分钟无更新视为超时 this.onlineUsers.forEach((userInfo, userId) => { if (now - userInfo.lastUpdateTime > timeoutThreshold) { // 用户超时 userInfo.status = 'timeout'; this.onlineUsers.set(userId, userInfo); this.notifyUserStatusChange(userId, 'timeout'); console.log(`用户 ${userId} 因超时被标记为离线`); } }); } /** * 获取当前位置 */ private async getCurrentLocation(): Promise { const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { throw new Error('用户未登录'); } try { // 使用mapService获取真实位置,保持与项目中其他地方的一致性 const location = await mapService.getLocation(); console.log('[位置追踪服务] 获取真实位置成功:', location); return { userId: userInfo.id, longitude: location.longitude, latitude: location.latitude, timestamp: Date.now() }; } catch (error) { console.error('[位置追踪服务] 获取真实位置失败,使用默认位置:', error); // 定位失败时返回默认位置作为降级方案 return { userId: userInfo.id, longitude: 102.833722, latitude: 24.880095, timestamp: Date.now() }; } } /** * 取消订阅位置更新 */ private async unsubscribeFromLocations(): Promise { // 真实模式:取消WebSocket订阅 await this.teardownRealTimeSubscription(); } /** * 设置实时位置订阅 */ private async setupRealTimeSubscription(): Promise { // 获取用户信息 const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { console.warn('用户未登录,无法设置实时位置订阅'); return; } // 缓存用户信息 this.userInfo = userInfo; // 真实环境:初始化WebSocket连接(服务器会自动处理订阅逻辑) try { // 初始化WebSocket连接(如果尚未连接) await apiService.initLocationWebSocket(); // 等待WebSocket连接建立(最多等待5秒) await new Promise((resolve, reject) => { let attempts = 0; const maxAttempts = 50; // 5秒超时 const checkConnection = () => { if (apiService.isWebSocketConnected()) { resolve(); } else if (attempts < maxAttempts) { attempts++; setTimeout(checkConnection, 100); } else { reject(new Error('WebSocket连接建立超时')); } }; checkConnection(); }); // 服务器会自动处理订阅逻辑,无需发送订阅消息 console.log('WebSocket连接已建立,服务器将自动处理位置订阅逻辑'); // 设置位置更新回调 this.unsubscribeCallback = apiService.onLocationUpdate((locationUpdate) => { this.handleLocationUpdate(locationUpdate); }); } catch (error) { console.error('设置实时位置订阅失败:', error); } } /** * 关闭实时位置订阅 */ private teardownRealTimeSubscription(): void { // 真实环境:关闭WebSocket连接(服务器会自动处理取消订阅逻辑) try { // 服务器会自动处理取消订阅逻辑,无需发送取消订阅消息 console.log('关闭WebSocket连接,服务器将自动处理取消订阅逻辑'); // 移除位置更新回调 if (this.unsubscribeCallback) { this.unsubscribeCallback(); this.unsubscribeCallback = null; } // 关闭WebSocket连接 apiService.closeLocationWebSocket(); } catch (error) { console.error('关闭实时位置订阅失败:', error); } } /** * 停止所有定时器 */ private stopTimers(): void { this.stopLocationUpdateTimer(); this.stopStatusCheckTimer(); } /** * 停止位置更新定时器 */ private stopLocationUpdateTimer(): void { if (this.locationUpdateTimer) { clearInterval(this.locationUpdateTimer); this.locationUpdateTimer = null; } } /** * 停止状态检查定时器 */ private stopStatusCheckTimer(): void { if (this.statusCheckTimer) { clearInterval(this.statusCheckTimer); this.statusCheckTimer = null; } } /** * 通知位置更新回调 */ private notifyLocationUpdateCallbacks(): void { const onlineUsers = this.getOnlineUsers(); this.locationUpdateCallbacks.forEach(callback => { try { callback(onlineUsers); } catch (error) { console.error('位置更新回调执行失败:', error); } }); } /** * 通知用户状态变化回调 */ private notifyUserStatusChange(userId: number, status: 'online' | 'offline' | 'timeout'): void { this.userStatusCallbacks.forEach(callback => { try { callback(userId, status); } catch (error) { console.error('用户状态变化回调执行失败:', error); } }); } /** * 处理位置更新 */ private handleLocationUpdate(locationUpdate: any): void { console.log('收到位置更新:', locationUpdate); // 验证位置更新消息格式 if (!locationUpdate || typeof locationUpdate !== 'object') { console.warn('无效的位置更新消息格式'); return; } // 提取关键信息 const { deliveryPersonId, latitude, longitude, timestamp } = locationUpdate; if (!deliveryPersonId || latitude === undefined || longitude === undefined) { console.warn('位置更新消息缺少必要字段:', locationUpdate); return; } console.log(`处理员工 ${deliveryPersonId} 的位置更新: (${longitude}, ${latitude})`); // 更新本地缓存中的员工位置 this.updateLocalUserLocation(deliveryPersonId, { userId: deliveryPersonId, longitude: longitude, latitude: latitude, timestamp: timestamp || Date.now() }); // 通知所有位置更新回调(UI层会监听这些回调来更新地图标记点) this.notifyLocationUpdateCallbacks(); console.log(`员工 ${deliveryPersonId} 位置更新处理完成`); } /** * 处理在线用户列表消息 * @param userList 在线用户列表消息 */ private handleOnlineUserList(userList: any): void { try { console.log('[LocationTrackingService] 收到在线用户列表:', userList); if (!userList || !userList.users) { console.warn('[LocationTrackingService] 无效的在线用户列表消息'); return; } // 清空当前在线用户列表 this.onlineUsers.clear(); // 更新在线用户列表 userList.users.forEach((userInfo: any) => { if (userInfo && userInfo.userId) { this.onlineUsers.set(userInfo.userId, { userId: userInfo.userId, name: userInfo.userName || '未知用户', avatarUrl: '/images/user-avatar.png', role: userInfo.role || 'delivery_person', // 用户角色,默认为配送员 lastLocation: { userId: userInfo.userId, longitude: userInfo.longitude || 0, latitude: userInfo.latitude || 0, timestamp: userInfo.lastUpdateTime || Date.now() }, lastUpdateTime: userInfo.lastUpdateTime || Date.now(), status: 'online' }); } }); console.log('[LocationTrackingService] 在线用户列表已更新,当前在线用户数:', this.onlineUsers.size); // 通知位置模块更新地图标记点 this.notifyLocationUpdateCallbacks(); } catch (error) { console.error('[LocationTrackingService] 处理在线用户列表失败:', error); } } } /** * 位置追踪服务单例实例 * 导出供应用程序全局使用 */ export default new LocationTrackingService();