// 位置追踪服务 - 处理用户签到后的位置追踪和状态管理 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('用户未登录,无法启动位置追踪'); } // 先获取当前位置数据 let initialLocation: { latitude: number; longitude: number; timestamp: number } ; const location = await this.getCurrentLocation(); initialLocation = { latitude: location.latitude, longitude: location.longitude, timestamp: Math.floor(Date.now() / 1000) } console.log('获取到初始位置数据:', initialLocation); // 调用签到接口,传递位置数据 try { await userService.signIn(initialLocation); console.log('签到成功,位置数据已发送到服务器'); } catch (error) { console.error('签到失败:', error); throw error; } this.isSignedIn = true; // 将当前用户添加到在线列表 await this.addUserToOnlineList(userInfo); // 设置实时位置订阅(初始化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(); console.log('位置追踪服务已启动'); } /** * 初始化位置追踪服务 */ public async initialize(): Promise { try { // 注册在线用户列表回调 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}`); const timestamp = Math.floor(Date.now() / 1000); console.log(`- 时间戳: ${timestamp}`); const locationData: LocationData = { userId: userInfo.id, longitude: location.longitude, latitude: location.latitude, timestamp: timestamp }; // 真实模式:通过WebSocket发送位置更新 console.log('[位置追踪服务] 真实模式:通过WebSocket发送位置更新'); await apiService.sendLocationUpdate(userInfo.id, location.latitude, location.longitude); // 同时更新本地缓存 this.updateLocalUserLocation(userInfo.id, locationData); console.log('[位置追踪服务] 位置更新完成'); } /** * 获取所有在线用户信息 */ public getOnlineUsers(): OnlineUserInfo[] { return Array.from(this.onlineUsers.values()).filter(user => user.status === 'online' ); } /** * 订阅位置更新 */ public subscribeToLocationUpdates(callback: (locations: OnlineUserInfo[]) => void): void { console.log('📝 [LocationTrackingService] 注册位置更新回调函数'); console.log('📍 [LocationTrackingService] 当前已注册回调数量:', this.locationUpdateCallbacks.size); this.locationUpdateCallbacks.add(callback); console.log('✅ [LocationTrackingService] 位置更新回调注册完成,当前回调数量:', this.locationUpdateCallbacks.size); } /** * 取消订阅位置更新 */ 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: Math.floor(Date.now() / 1000), 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} 因超时被标记为离线`); } }); } /** * 获取当前位置 */ public 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连接已建立,服务器将自动处理位置订阅逻辑'); } 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(); console.log('🔔 [LocationTrackingService] 开始通知位置更新回调'); console.log('👥 [LocationTrackingService] 在线用户数量:', onlineUsers.length); console.log('📞 [LocationTrackingService] 准备执行', this.locationUpdateCallbacks.size, '个回调函数'); this.locationUpdateCallbacks.forEach((callback, index) => { try { console.log(`🔄 [LocationTrackingService] 执行第 ${index + 1} 个位置更新回调`); callback(onlineUsers); console.log(`✅ [LocationTrackingService] 第 ${index + 1} 个位置更新回调执行成功`); } catch (error) { console.error(`❌ [LocationTrackingService] 第 ${index + 1} 个位置更新回调执行失败:`, error); } }); console.log('🏁 [LocationTrackingService] 位置更新回调通知完成'); } /** * 通知用户状态变化回调 */ private notifyUserStatusChange(userId: number, status: 'online' | 'offline' | 'timeout'): void { this.userStatusCallbacks.forEach(callback => { try { callback(userId, status); } catch (error) { console.error('用户状态变化回调执行失败:', error); } }); } /** * 处理在线用户列表 * @param userList 服务器下发的在线用户列表 */ private handleOnlineUserList(userList: any[]): void { try { // 验证数据格式 if (!Array.isArray(userList)) { console.warn('[LocationTrackingService] 无效的在线用户列表数据格式'); return; } console.log(`[LocationTrackingService] 开始处理在线用户列表,用户数量: ${userList.length}`); // 获取当前本地用户列表的userId集合 const currentUserIds = new Set(this.onlineUsers.keys()); // 处理服务器下发的用户列表 const newUserIds = new Set(); userList.forEach(user => { if (user && user.userId && user.latitude !== undefined && user.longitude !== undefined) { newUserIds.add(user.userId); // 提取位置数据(支持多种数据格式) const latitude = user.latitude; const longitude = user.longitude; const timestamp = user.timestamp || user.lastUpdateTime || Date.now(); // 更新或添加用户信息 this.onlineUsers.set(user.userId, { userId: user.userId, name: user.name || user.userName || `用户${user.userId}`, avatarUrl: '/images/user-avatar.png', role: user.role || 'employee', lastLocation: { userId: user.userId, longitude: longitude, latitude: latitude, timestamp: timestamp }, lastUpdateTime: timestamp, status: user.userStatus === false ? 'offline' : 'online' }); console.log(`[LocationTrackingService] 更新用户 ${user.userId} 位置: (${latitude}, ${longitude})`); // 如果用户是新上线的,触发用户状态回调 if (!currentUserIds.has(user.userId)) { this.notifyUserStatusChange(user.userId, 'online'); console.log(`[LocationTrackingService] 新用户上线: ${user.userId}`); } } else { console.warn(`[LocationTrackingService] 跳过无效用户数据:`, user); } }); // 删除本地列表中不在服务器列表中的用户 let removedCount = 0; currentUserIds.forEach(userId => { if (!newUserIds.has(userId)) { // 删除离线用户 this.onlineUsers.delete(userId); removedCount++; // 触发用户状态回调(离线) this.notifyUserStatusChange(userId, 'offline'); console.log(`[LocationTrackingService] 用户离线: ${userId}`); } }); console.log(`[LocationTrackingService] 在线用户列表更新完成: 新增 ${newUserIds.size - (currentUserIds.size - removedCount)} 个用户,删除 ${removedCount} 个用户`); // 通知位置模块更新地图标记点(无论是否有变化都通知,确保地图标记点同步) this.notifyLocationUpdateCallbacks(); console.log('[LocationTrackingService] 已通知位置模块更新地图标记点'); } catch (error) { console.error('[LocationTrackingService] 处理在线用户列表失败:', error); } } } /** * 位置追踪服务单例实例 * 导出供应用程序全局使用 */ export default new LocationTrackingService();