添加管理员逻辑
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { WarehouseInfo, UserInfo, DeliveryPerson, Order, EmployeeInfo } from '../types';
|
||||
import locationTrackingService from './locationTrackingService';
|
||||
|
||||
|
||||
/**
|
||||
@@ -7,7 +8,7 @@ import { WarehouseInfo, UserInfo, DeliveryPerson, Order, EmployeeInfo } from '..
|
||||
*/
|
||||
|
||||
// API基础URL配置
|
||||
const IS_LOCAL_DEV = true; // true: 本地开发环境, false: 生产环境
|
||||
const IS_LOCAL_DEV = false; // true: 本地开发环境, false: 生产环境
|
||||
const API_BASE_URL = IS_LOCAL_DEV ? 'http://localhost:8080' : 'https://www.doubleyin.cn';
|
||||
|
||||
console.log(`当前API地址: ${API_BASE_URL} (${IS_LOCAL_DEV ? '本地开发环境' : '生产环境'})`);
|
||||
@@ -23,16 +24,7 @@ console.log(`当前API地址: ${API_BASE_URL} (${IS_LOCAL_DEV ? '本地开发环
|
||||
* 封装了所有与后端API交互的方法,并按照功能模块进行组织
|
||||
*/
|
||||
class ApiService {
|
||||
/**
|
||||
* 位置更新回调函数集合
|
||||
*/
|
||||
private locationUpdateCallbacks: Set<(location: any) => void> | null;
|
||||
|
||||
/**
|
||||
* 在线用户列表回调函数集合
|
||||
*/
|
||||
private onlineUserListCallbacks: Set<(userList: any) => void> | null;
|
||||
|
||||
|
||||
/**
|
||||
* 位置更新WebSocket连接
|
||||
*/
|
||||
@@ -42,8 +34,6 @@ class ApiService {
|
||||
* 构造函数
|
||||
*/
|
||||
constructor() {
|
||||
this.locationUpdateCallbacks = null;
|
||||
this.onlineUserListCallbacks = null;
|
||||
this.locationWebSocket = null;
|
||||
}
|
||||
|
||||
@@ -148,28 +138,27 @@ async ServerLogin(code: string) {
|
||||
|
||||
// 构建请求数据,必须包含位置数据
|
||||
const requestData: any = {
|
||||
userId,
|
||||
latitude: initialLocation.latitude,
|
||||
longitude: initialLocation.longitude,
|
||||
timestamp: initialLocation.timestamp
|
||||
};
|
||||
|
||||
// 服务器返回的是用户对象,需要转换为前端期望的格式
|
||||
// 服务器现在返回统一的DTO格式,直接使用
|
||||
const response = await this.request<any>('/user/signin', {
|
||||
method: 'POST',
|
||||
data: requestData,
|
||||
});
|
||||
|
||||
// 转换响应格式
|
||||
// 服务器返回格式:{ success: boolean, userInfo: UserInfoResponse }
|
||||
return {
|
||||
success: true,
|
||||
success: response.success,
|
||||
employeeInfo: {
|
||||
id: response.id,
|
||||
name: response.name,
|
||||
phone: response.phone,
|
||||
role: response.role || 'DELIVERY_PERSON'
|
||||
id: response.userInfo.id,
|
||||
name: response.userInfo.name,
|
||||
phone: response.userInfo.phone,
|
||||
role: response.userInfo.role || 'DELIVERY_PERSON'
|
||||
},
|
||||
message: '签到成功'
|
||||
message: response.message
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,22 +170,21 @@ async ServerLogin(code: string) {
|
||||
async userRegister(userInfo: { name: string; phone: string }): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> {
|
||||
console.log('API userRegister调用,参数userInfo:', userInfo);
|
||||
|
||||
// 服务器返回的是用户对象,需要转换为前端期望的格式
|
||||
// 服务器现在返回统一的DTO格式:{ success: boolean, userInfo: UserInfoResponse }
|
||||
const response = await this.request<any>('/user/register', {
|
||||
method: 'POST',
|
||||
data: userInfo,
|
||||
});
|
||||
|
||||
// 转换响应格式
|
||||
return {
|
||||
success: true,
|
||||
success: response.success,
|
||||
employeeInfo: {
|
||||
id: response.id,
|
||||
name: response.name || userInfo.name, // 如果服务器返回null,使用用户输入的值
|
||||
phone: response.phone || userInfo.phone, // 如果服务器返回null,使用用户输入的值
|
||||
role: response.role || 'DELIVERY_PERSON'
|
||||
id: response.userInfo.id,
|
||||
name: response.userInfo.name || userInfo.name, // 如果服务器返回null,使用用户输入的值
|
||||
phone: response.userInfo.phone || userInfo.phone, // 如果服务器返回null,使用用户输入的值
|
||||
role: response.userInfo.role || 'DELIVERY_PERSON'
|
||||
},
|
||||
message: '注册成功'
|
||||
message: response.message
|
||||
};
|
||||
}
|
||||
|
||||
@@ -217,10 +205,16 @@ async ServerLogin(code: string) {
|
||||
async userSignOut(userId: number): Promise<{ success: boolean; message?: string }> {
|
||||
console.log('API userSignOut调用,参数userId:', userId);
|
||||
|
||||
return await this.request('/user/signout', {
|
||||
// 服务器现在返回统一的DTO格式:{ success: boolean, message: string }
|
||||
const response = await this.request<any>('/user/signout', {
|
||||
method: 'POST',
|
||||
data: { userId },
|
||||
});
|
||||
|
||||
return {
|
||||
success: response.success,
|
||||
message: response.message
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,27 +565,6 @@ async ServerLogin(code: string) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 注册在线用户列表回调函数
|
||||
* @param callback 在线用户列表回调函数
|
||||
* @returns 取消订阅函数
|
||||
*/
|
||||
onOnlineUserList(callback: (userList: any) => void): () => void {
|
||||
// 添加回调到集合
|
||||
if (!this.onlineUserListCallbacks) {
|
||||
this.onlineUserListCallbacks = new Set();
|
||||
}
|
||||
this.onlineUserListCallbacks.add(callback);
|
||||
|
||||
// 返回取消订阅函数
|
||||
return () => {
|
||||
if (this.onlineUserListCallbacks) {
|
||||
this.onlineUserListCallbacks.delete(callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 私有属性和方法 =====
|
||||
|
||||
@@ -710,100 +683,9 @@ async ServerLogin(code: string) {
|
||||
console.warn('收到无效的WebSocket消息:', message);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
locationTrackingService.handleWebSocketMessage(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 触发在线用户列表回调
|
||||
* @param userList 在线用户列表信息
|
||||
*/
|
||||
private triggerOnlineUserListCallbacks(userList: any): void {
|
||||
console.log('🔔 [apiService] 开始触发在线用户列表回调,消息类型:', userList.type);
|
||||
console.log('📊 [apiService] 回调数据内容:', JSON.stringify(userList, null, 2));
|
||||
|
||||
if (this.onlineUserListCallbacks) {
|
||||
console.log(`📞 [apiService] 准备执行 ${this.onlineUserListCallbacks.size} 个回调函数`);
|
||||
let index = 0;
|
||||
this.onlineUserListCallbacks.forEach((callback) => {
|
||||
try {
|
||||
console.log(`🔄 [apiService] 执行第 ${index + 1} 个回调函数`);
|
||||
// 传递正确的数据格式:只传递users数组,而不是整个消息对象
|
||||
callback(userList.users);
|
||||
console.log(`✅ [apiService] 第 ${index + 1} 个回调函数执行成功`);
|
||||
index++;
|
||||
} catch (error) {
|
||||
console.error(`❌ [apiService] 第 ${index + 1} 个回调函数执行失败:`, error);
|
||||
index++;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ [apiService] 没有注册的在线用户列表回调函数');
|
||||
}
|
||||
console.log('🏁 [apiService] 在线用户列表回调触发完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送WebSocket消息
|
||||
* @param message 要发送的消息对象
|
||||
@@ -844,7 +726,7 @@ async ServerLogin(code: string) {
|
||||
* 取消订阅位置更新(服务器自动处理,无需发送消息)
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
async unsubscribeFromLocationUpdates(userId: number): Promise<void> {
|
||||
async unsubscribeFromLocationUpdates(_userId: number): Promise<void> {
|
||||
// 服务器会自动处理取消订阅逻辑,无需发送取消订阅消息
|
||||
console.log('位置取消订阅已初始化,服务器将自动处理取消订阅逻辑');
|
||||
}
|
||||
|
||||
@@ -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] 在线用户列表回调触发完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理在线用户列表
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 地图服务 - 处理地图相关的功能,包括定位、路线规划、地理编码等
|
||||
|
||||
import { AMapRegeoResponse, RoutePlanResult, SearchResult, LocationData } from '../types';
|
||||
import { AMapRegeoResponse, RoutePlanResult, SearchResult } from '../types';
|
||||
|
||||
/**
|
||||
* 地图服务类
|
||||
|
||||
@@ -70,7 +70,7 @@ class OrderService {
|
||||
*/
|
||||
async deleteOrder(orderId: number): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const result = await apiService.deleteOrder(orderId);
|
||||
await apiService.deleteOrder(orderId);
|
||||
return { success: true, message: '删除成功' };
|
||||
} catch (error) {
|
||||
console.error('删除订单失败:', error);
|
||||
|
||||
Reference in New Issue
Block a user