first commit

This commit is contained in:
2025-10-16 21:32:16 +08:00
commit c446df73b5
229 changed files with 499497 additions and 0 deletions

View File

@@ -0,0 +1,564 @@
// 位置追踪服务 - 处理用户签到后的位置追踪和状态管理
import { UserInfo, LocationData } from '../types';
import apiService from './apiService';
import { isMockMode } from './apiService';
import userService from './userService';
/**
* 在线用户信息接口
*/
export interface OnlineUserInfo {
userId: number;
name: string;
avatarUrl: string;
lastLocation: LocationData;
lastUpdateTime: number;
status: 'online' | 'offline' | 'timeout';
}
/**
* 位置追踪服务类
* 提供用户签到后的位置追踪、状态管理和位置分发功能
*/
class LocationTrackingService {
// 在线用户列表
private onlineUsers: Map<number, OnlineUserInfo>;
// 位置更新定时器
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<void> {
const userInfo = userService.getGlobalUserInfo();
if (!userInfo || !userInfo.id) {
throw new Error('用户未登录,无法启动位置追踪');
}
this.isSignedIn = true;
// 将当前用户添加到在线列表
await this.addUserToOnlineList(userInfo);
// 启动位置更新定时器每30秒更新一次
this.startLocationUpdateTimer();
// 启动状态检查定时器每60秒检查一次
this.startStatusCheckTimer();
// 订阅其他用户的位置更新
await this.subscribeToOtherUsersLocations();
console.log('位置追踪服务已启动');
}
/**
* 用户签退后停止位置追踪
*/
public async stopTracking(): Promise<void> {
const userInfo = userService.getGlobalUserInfo();
if (!userInfo || !userInfo.id) {
return;
}
this.isSignedIn = false;
// 从在线列表中移除用户
this.removeUserFromOnlineList(userInfo.id);
// 停止定时器
this.stopTimers();
// 取消订阅
await this.unsubscribeFromLocations();
console.log('位置追踪服务已停止');
}
/**
* 手动更新用户位置
*/
public async updateUserLocation(location: { longitude: number; latitude: number }): Promise<void> {
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()
};
if (isMockMode) {
// 模拟模式:更新本地在线用户列表
console.log('[位置追踪服务] 模拟模式:更新本地位置');
this.updateLocalUserLocation(userInfo.id, locationData);
} else {
// 真实模式调用API更新位置
console.log('[位置追踪服务] 真实模式调用API更新位置');
await apiService.updateDeliveryPersonLocation(userInfo.id, location);
// 同时更新本地缓存
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<void> {
const currentLocation = await this.getCurrentLocation();
const onlineUser: OnlineUserInfo = {
userId: userInfo.id,
name: userInfo.name || '未知用户',
avatarUrl: '/images/user-avatar.png',
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<LocationData> {
const userInfo = userService.getGlobalUserInfo();
if (!userInfo || !userInfo.id) {
throw new Error('用户未登录');
}
// 这里应该调用地图服务获取当前位置
// 暂时返回一个默认位置
return {
userId: userInfo.id,
longitude: 102.833722,
latitude: 24.880095,
timestamp: Date.now()
};
}
/**
* 订阅其他用户的位置更新
*/
private async subscribeToOtherUsersLocations(): Promise<void> {
if (isMockMode) {
// 模拟模式:定时生成其他用户的位置更新
this.startMockOtherUsersUpdates();
} else {
// 真实模式通过WebSocket订阅
await this.setupRealTimeSubscription();
}
}
/**
* 取消订阅位置更新
*/
private async unsubscribeFromLocations(): Promise<void> {
if (!isMockMode) {
// 真实模式取消WebSocket订阅
await this.teardownRealTimeSubscription();
}
}
/**
* 启动模拟的其他用户位置更新
*/
private startMockOtherUsersUpdates(): void {
// 模拟其他用户的位置更新每45秒一次
setInterval(() => {
if (this.isSignedIn) {
this.generateMockOtherUsersLocations();
this.notifyLocationUpdateCallbacks();
}
}, 45000);
}
/**
* 生成模拟的其他用户位置
*/
private generateMockOtherUsersLocations(): void {
const currentUserInfo = userService.getGlobalUserInfo();
if (!currentUserInfo) return;
// 模拟2个其他在线用户
const mockUserIds = [101, 102]; // 模拟用户ID
mockUserIds.forEach(userId => {
if (userId !== currentUserInfo.id) {
const existingUser = this.onlineUsers.get(userId);
if (existingUser) {
// 更新现有用户位置
existingUser.lastLocation.longitude += (Math.random() - 0.5) * 0.001;
existingUser.lastLocation.latitude += (Math.random() - 0.5) * 0.001;
existingUser.lastUpdateTime = Date.now();
existingUser.status = 'online';
this.onlineUsers.set(userId, existingUser);
} else {
// 添加新用户
const newUser: OnlineUserInfo = {
userId,
name: `用户${userId}`,
avatarUrl: '/images/user-avatar.png',
lastLocation: {
userId,
longitude: 102.833722 + (Math.random() - 0.5) * 0.01,
latitude: 24.880095 + (Math.random() - 0.5) * 0.01,
timestamp: Date.now()
},
lastUpdateTime: Date.now(),
status: 'online'
};
this.onlineUsers.set(userId, newUser);
this.notifyUserStatusChange(userId, 'online');
}
}
});
}
/**
* 设置实时位置订阅
*/
private setupRealTimeSubscription(): void {
// 获取用户信息
const userInfo = userService.getGlobalUserInfo();
if (!userInfo || !userInfo.id) {
console.warn('用户未登录,无法设置实时位置订阅');
return;
}
// 缓存用户信息
this.userInfo = userInfo;
if (isMockMode) {
console.log('[MOCK] 设置实时位置订阅');
this.startMockLocationGeneration();
return;
}
// 真实环境初始化WebSocket连接并订阅位置更新
try {
// 使用新的API接口
apiService.initLocationWebSocket();
// 订阅当前用户的位置更新
apiService.subscribeToLocationUpdates(this.userInfo.id)
.then(() => {
console.log('成功订阅位置更新');
})
.catch((error) => {
console.error('订阅位置更新失败:', error);
});
// 设置位置更新回调
this.unsubscribeCallback = apiService.onLocationUpdate((locationUpdate) => {
this.handleLocationUpdate(locationUpdate);
});
} catch (error) {
console.error('设置实时位置订阅失败:', error);
}
}
/**
* 关闭实时位置订阅
*/
private teardownRealTimeSubscription(): void {
if (isMockMode) {
console.log('[MOCK] 关闭实时位置订阅');
this.stopMockLocationGeneration();
return;
}
// 真实环境取消订阅并关闭WebSocket连接
try {
if (this.userInfo) {
// 使用新的API接口取消订阅
apiService.unsubscribeFromLocationUpdates(this.userInfo.id)
.then(() => {
console.log('成功取消订阅位置更新');
})
.catch((error) => {
console.error('取消订阅位置更新失败:', error);
});
}
// 移除位置更新回调
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 startMockLocationGeneration(): void {
console.log('[MOCK] 启动模拟位置生成');
// 这里可以添加模拟位置生成的逻辑
}
/**
* 停止模拟位置生成
*/
private stopMockLocationGeneration(): void {
console.log('[MOCK] 停止模拟位置生成');
// 这里可以添加停止模拟位置生成的逻辑
}
/**
* 处理位置更新
*/
private handleLocationUpdate(locationUpdate: any): void {
console.log('收到位置更新:', locationUpdate);
// 这里可以添加处理位置更新的逻辑
}
}
/**
* 位置追踪服务单例实例
* 导出供应用程序全局使用
*/
export default new LocationTrackingService();