添加管理员逻辑

This commit is contained in:
2025-10-19 23:38:54 +08:00
parent 118ec38550
commit 5ee4e077fb
46 changed files with 5263 additions and 883 deletions

View File

@@ -1,5 +1,5 @@
// 位置追踪服务 - 处理用户签到后的位置追踪和状态管理
import { UserInfo, LocationData } from '../types';
import { LocationData } from '../types';
import apiService from './apiService';
import userService from './userService';
import mapService from './mapService';
@@ -22,29 +22,25 @@ export interface OnlineUserInfo {
* 提供用户签到后的位置追踪、状态管理和位置分发功能
*/
class LocationTrackingService {
// 在线用户列表
// 在线用户列表仅通过服务器WebSocket推送更新
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 userStatusCallback: ((userId: number, status: 'online' | 'offline' | 'timeout') => void) | null;
/**
* 位置更新回调函数类型
*/
private locationUpdateCallback: ((onlineUsers: OnlineUserInfo[]) => void) | null;
// 当前用户是否已签到
private isSignedIn: boolean;
// 用户信息缓存
private userInfo: UserInfo | null;
// 取消订阅回调函数
private unsubscribeCallback: (() => void) | null;
private unsubscribeCallback: (() => void) | null = null;
/**
* 构造函数
@@ -52,62 +48,27 @@ class LocationTrackingService {
constructor() {
this.onlineUsers = new Map();
this.locationUpdateTimer = null;
this.statusCheckTimer = null;
this.locationUpdateCallbacks = new Set();
this.userStatusCallbacks = new Set();
this.userStatusCallback = null;
this.locationUpdateCallback = null;
this.isSignedIn = false;
this.userInfo = null;
this.unsubscribeCallback = null;
}
/**
* 用户签到后启动位置追踪
* 启动位置追踪服务WebSocket连接
*/
public async startTrackingAfterSignIn(): Promise<void> {
public async startTracking(): Promise<void> {
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秒更新一次
// 启动位置更新定时器每30秒向服务器发送位置信息
this.startLocationUpdateTimer();
console.log('位置追踪服务已启动');
@@ -117,23 +78,11 @@ class LocationTrackingService {
* 初始化位置追踪服务
*/
public async initialize(): Promise<void> {
try {
// 注册在线用户列表回调
apiService.onOnlineUserList((userList) => {
this.handleOnlineUserList(userList);
});
console.log('[LocationTrackingService] 位置追踪服务初始化完成');
} catch (error) {
console.error('[LocationTrackingService] 初始化失败:', error);
throw error;
}
}
/**
* 用户签退后停止位置追踪
* 停止位置追踪服务WebSocket断开时调用
*/
public async stopTracking(): Promise<void> {
const userInfo = userService.getGlobalUserInfo();
@@ -143,26 +92,30 @@ class LocationTrackingService {
this.isSignedIn = false;
// 签退消息已通过REST API处理服务器会自动处理取消订阅逻辑
// 停止位置更新定时器
this.stopLocationUpdateTimer();
// 从在线列表中移除用户
this.removeUserFromOnlineList(userInfo.id);
// 关闭WebSocket连接
try {
await this.teardownRealTimeSubscription();
console.log('调试信息 - WebSocket连接已关闭');
} catch (error) {
console.warn('调试信息 - 关闭WebSocket连接失败:', error);
}
// 停止定时器
this.stopTimers();
// 清空在线用户列表WebSocket断开时清空所有数据
this.onlineUsers.clear();
// 通知所有位置更新回调,清空地图标记点
this.notifyLocationUpdateCallbacks();
// 关闭实时位置订阅(服务器会自动处理取消订阅)
this.teardownRealTimeSubscription();
console.log('位置追踪服务已停止');
console.log('位置追踪服务已停止,在线用户列表已清空');
}
/**
* 手动更新用户位置
* 向服务器发送位置更新(仅发送,不更新本地缓存)
*/
public async updateUserLocation(location: { longitude: number; latitude: number }): Promise<void> {
const userInfo = userService.getGlobalUserInfo();
@@ -171,28 +124,15 @@ class LocationTrackingService {
return;
}
console.log('[位置追踪服务] 前端发送给服务器位置信息:');
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发送位置更新');
// 通过WebSocket发送位置更新给服务器
await apiService.sendLocationUpdate(userInfo.id, location.latitude, location.longitude);
// 同时更新本地缓存
this.updateLocalUserLocation(userInfo.id, locationData);
console.log('[位置追踪服务] 位置更新完成');
console.log('[位置追踪服务] 位置信息已发送到服务器');
}
/**
@@ -204,35 +144,26 @@ class LocationTrackingService {
);
}
/**
* 订阅位置更新
*/
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);
}
/**
* 取消订阅位置更新
* 订阅用户状态变化(单一方法赋值)
* @param callback 用户状态变化回调函数
* @returns 取消订阅函数
*/
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 subscribeToUserStatusChanges(callback: (userId: number, status: 'online' | 'offline' | 'timeout') => void): () => void {
console.log('📝 [LocationTrackingService] 注册用户状态变化回调函数');
// 直接赋值,覆盖之前的回调
this.userStatusCallback = callback;
console.log('✅ [LocationTrackingService] 用户状态变化回调注册完成');
// 返回取消订阅函数
return () => {
if (this.userStatusCallback === callback) {
this.userStatusCallback = null;
console.log('✅ [LocationTrackingService] 用户状态变化回调已取消');
}
};
}
/**
@@ -245,59 +176,6 @@ class LocationTrackingService {
// ===== 私有方法 =====
/**
* 添加用户到在线列表
*/
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',
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);
}
}
/**
* 启动位置更新定时器
*/
@@ -316,35 +194,7 @@ class LocationTrackingService {
}, 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} 因超时被标记为离线`);
}
});
}
/**
* 获取当前位置
@@ -380,20 +230,6 @@ class LocationTrackingService {
}
}
/**
* 取消订阅位置更新
*/
private async unsubscribeFromLocations(): Promise<void> {
// 真实模式取消WebSocket订阅
await this.teardownRealTimeSubscription();
}
/**
* 设置实时位置订阅
*/
@@ -405,8 +241,7 @@ class LocationTrackingService {
return;
}
// 缓存用户信息
this.userInfo = userInfo;
// 缓存用户信息(已移除)
// 真实环境初始化WebSocket连接服务器会自动处理订阅逻辑
try {
@@ -464,14 +299,6 @@ class LocationTrackingService {
}
}
/**
* 停止所有定时器
*/
private stopTimers(): void {
this.stopLocationUpdateTimer();
this.stopStatusCheckTimer();
}
/**
* 停止位置更新定时器
*/
@@ -482,18 +309,6 @@ class LocationTrackingService {
}
}
/**
* 停止状态检查定时器
*/
private stopStatusCheckTimer(): void {
if (this.statusCheckTimer) {
clearInterval(this.statusCheckTimer);
this.statusCheckTimer = null;
}
}
/**
* 通知位置更新回调
*/
@@ -501,37 +316,160 @@ class LocationTrackingService {
const onlineUsers = this.getOnlineUsers();
console.log('🔔 [LocationTrackingService] 开始通知位置更新回调');
console.log('👥 [LocationTrackingService] 在线用户数量:', onlineUsers.length);
console.log('📞 [LocationTrackingService] 准备执行', this.locationUpdateCallbacks.size, '个回调函数');
this.locationUpdateCallbacks.forEach((callback, index) => {
// 触发位置更新回调
if (this.locationUpdateCallback) {
try {
console.log(`🔄 [LocationTrackingService] 执行${index + 1}位置更新回调`);
callback(onlineUsers);
console.log(`✅ [LocationTrackingService] ${index + 1}位置更新回调执行成功`);
console.log('🔄 [LocationTrackingService] 执行位置更新回调');
this.locationUpdateCallback(onlineUsers);
console.log('✅ [LocationTrackingService] 位置更新回调执行成功');
} catch (error) {
console.error(`❌ [LocationTrackingService] ${index + 1}位置更新回调执行失败:`, error);
console.error('❌ [LocationTrackingService] 位置更新回调执行失败:', error);
}
});
} else {
console.log('⚠️ [LocationTrackingService] 没有注册位置更新回调');
}
console.log('🏁 [LocationTrackingService] 位置更新回调通知完成');
}
/**
* 手动触发位置更新(公共方法,可从外部调用)
* 用于手动刷新地图上的用户标记
*/
public triggerLocationUpdate(): void {
console.log('🔔 [LocationTrackingService] 手动触发位置更新');
this.notifyLocationUpdateCallbacks();
}
/**
* 订阅位置更新用于locationModule注册回调
* @param callback 位置更新回调函数
* @returns 取消订阅函数
*/
public subscribeToLocationUpdates(callback: (onlineUsers: OnlineUserInfo[]) => void): () => void {
console.log('📝 [LocationTrackingService] 注册位置更新回调函数');
// 直接赋值,覆盖之前的回调
this.locationUpdateCallback = callback;
console.log('✅ [LocationTrackingService] 位置更新回调注册完成');
// 返回取消订阅函数
return () => {
if (this.locationUpdateCallback === callback) {
this.locationUpdateCallback = null;
console.log('✅ [LocationTrackingService] 位置更新回调已取消');
}
};
}
/**
* 通知用户状态变化回调
*/
private notifyUserStatusChange(userId: number, status: 'online' | 'offline' | 'timeout'): void {
this.userStatusCallbacks.forEach(callback => {
console.log(`🔔 [LocationTrackingService] 开始通知用户状态变化回调: 用户 ${userId} 状态变为 ${status}`);
if (this.userStatusCallback) {
try {
callback(userId, status);
console.log('🔄 [LocationTrackingService] 执行用户状态变化回调');
this.userStatusCallback(userId, status);
console.log('✅ [LocationTrackingService] 用户状态变化回调执行成功');
} catch (error) {
console.error('用户状态变化回调执行失败:', error);
console.error('❌ [LocationTrackingService] 用户状态变化回调执行失败:', error);
}
});
} else {
console.log('⚠️ [LocationTrackingService] 没有注册用户状态变化回调');
}
console.log('🏁 [LocationTrackingService] 用户状态变化回调通知完成');
}
/**
* 处理WebSocket消息
* @param message 服务器下发的消息
*/
public handleWebSocketMessage(message: any){
console.log(`📡 收到WebSocket消息类型: ${message.type}`, message);
switch (message.type) {
case 'onlineUserList':
// 处理在线用户列表消息(服务器发送当前在线用户列表)
console.log('👥 处理在线用户列表,用户数量:', message.users ? message.users.length : 0);
if (message.users && Array.isArray(message.users)) {
this.triggerOnlineUserListCallbacks({
type: 'onlineUserList',
users: message.users.map((user: any) => ({
userId: user.userId,
name: user.name,
role: user.role,
userStatus: user.userStatus,
lastUpdateTime: user.lastUpdateTime,
latitude: user.locationData?.latitude || user.latitude,
longitude: user.locationData?.longitude || user.longitude,
timestamp: user.locationData?.timestamp || user.timestamp
}))
});
}else {
console.warn('❌ onlineUserList消息格式错误users字段不存在或不是数组');
}
break;
case 'userLocationList':
// 处理用户位置列表消息服务器每30秒发送所有在线用户的位置列表
console.log('👥 处理用户位置列表,用户数量:', message.users ? message.users.length : 0);
// 确保用户列表存在且是数组
if (message.users && Array.isArray(message.users)) {
// 转换用户数据格式,确保与位置追踪服务兼容
const formattedUsers = message.users.map((user: any) => {
// 支持多种数据格式locationData字段或直接字段
const locationData = user.locationData || user;
return {
userId: user.userId || locationData.userId,
name: user.name || user.userName || `用户${user.userId || locationData.userId}`,
role: user.role || 'employee',
userStatus: user.userStatus !== false, // 转换为布尔值
lastUpdateTime: user.lastUpdateTime || locationData.timestamp || Date.now(),
latitude: locationData.latitude,
longitude: locationData.longitude,
timestamp: locationData.timestamp || user.timestamp || Date.now()
};
});
console.log('📊 转换后的用户位置数据:', formattedUsers);
this.triggerOnlineUserListCallbacks({
type: 'userLocationList',
users: formattedUsers
});
} else {
console.warn('❌ userLocationList消息格式错误users字段不存在或不是数组');
}
break;
default:
console.warn('收到未知类型的WebSocket消息:', message);
break;
}
}
/**
* 触发在线用户列表回调函数
* @param userList 在线用户列表数据
*/
private triggerOnlineUserListCallbacks(userList: any): void {
console.log('🔔 [apiService] 开始触发在线用户列表回调,消息类型:', userList.type);
console.log('📊 [apiService] 回调数据内容:', JSON.stringify(userList, null, 2));
try {
console.log('🔄 [apiService] 执行在线用户列表回调函数');
// 传递正确的数据格式只传递users数组而不是整个消息对象
this.handleOnlineUserList(userList.users);
console.log('✅ [apiService] 在线用户列表回调函数执行成功');
} catch (error) {
console.error('❌ [apiService] 在线用户列表回调函数执行失败:', error);
}
console.log('🏁 [apiService] 在线用户列表回调触发完成');
}
/**
* 处理在线用户列表