地址路径修改
This commit is contained in:
482
dist/services/locationTrackingService.js
vendored
Normal file
482
dist/services/locationTrackingService.js
vendored
Normal file
@@ -0,0 +1,482 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const apiService_1 = __importDefault(require("./apiService"));
|
||||
const userService_1 = __importDefault(require("./userService"));
|
||||
const mapService_1 = __importDefault(require("./mapService"));
|
||||
/**
|
||||
* 位置追踪服务类
|
||||
* 提供用户签到后的位置追踪、状态管理和位置分发功能
|
||||
*/
|
||||
class LocationTrackingService {
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
constructor() {
|
||||
// 取消订阅回调函数
|
||||
this.unsubscribeCallback = null;
|
||||
this.onlineUsers = new Map();
|
||||
this.locationUpdateTimer = null;
|
||||
this.userStatusCallback = null;
|
||||
this.locationUpdateCallback = null;
|
||||
this.isSignedIn = false;
|
||||
this.unsubscribeCallback = null;
|
||||
}
|
||||
/**
|
||||
* 启动位置追踪服务(WebSocket连接)
|
||||
*/
|
||||
async startTracking() {
|
||||
const userInfo = userService_1.default.getGlobalUserInfo();
|
||||
if (!userInfo || !userInfo.id) {
|
||||
throw new Error('用户未登录,无法启动位置追踪');
|
||||
}
|
||||
this.isSignedIn = true;
|
||||
// 设置实时位置订阅(初始化WebSocket连接)
|
||||
await this.setupRealTimeSubscription();
|
||||
// 启动位置更新定时器(每30秒向服务器发送位置信息)
|
||||
this.startLocationUpdateTimer();
|
||||
console.log('位置追踪服务已启动');
|
||||
}
|
||||
/**
|
||||
* 初始化位置追踪服务
|
||||
*/
|
||||
async initialize() {
|
||||
}
|
||||
/**
|
||||
* 停止位置追踪服务(WebSocket断开时调用)
|
||||
*/
|
||||
async stopTracking() {
|
||||
const userInfo = userService_1.default.getGlobalUserInfo();
|
||||
if (!userInfo || !userInfo.id) {
|
||||
return;
|
||||
}
|
||||
this.isSignedIn = false;
|
||||
// 停止位置更新定时器
|
||||
this.stopLocationUpdateTimer();
|
||||
// 关闭WebSocket连接
|
||||
try {
|
||||
await this.teardownRealTimeSubscription();
|
||||
console.log('调试信息 - WebSocket连接已关闭');
|
||||
}
|
||||
catch (error) {
|
||||
console.warn('调试信息 - 关闭WebSocket连接失败:', error);
|
||||
}
|
||||
// 清空在线用户列表(WebSocket断开时清空所有数据)
|
||||
this.onlineUsers.clear();
|
||||
// 通知所有位置更新回调,清空地图标记点
|
||||
this.notifyLocationUpdateCallbacks();
|
||||
console.log('位置追踪服务已停止,在线用户列表已清空');
|
||||
}
|
||||
/**
|
||||
* 向服务器发送位置更新(仅发送,不更新本地缓存)
|
||||
*/
|
||||
async updateUserLocation(location) {
|
||||
const userInfo = userService_1.default.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}`);
|
||||
// 通过WebSocket发送位置更新给服务器
|
||||
await apiService_1.default.sendLocationUpdate(userInfo.id, location.latitude, location.longitude);
|
||||
console.log('[位置追踪服务] 位置信息已发送到服务器');
|
||||
}
|
||||
/**
|
||||
* 获取所有在线用户信息
|
||||
*/
|
||||
getOnlineUsers() {
|
||||
return Array.from(this.onlineUsers.values()).filter(user => user.status === 'online');
|
||||
}
|
||||
/**
|
||||
* 订阅用户状态变化(单一方法赋值)
|
||||
* @param callback 用户状态变化回调函数
|
||||
* @returns 取消订阅函数
|
||||
*/
|
||||
subscribeToUserStatusChanges(callback) {
|
||||
console.log('📝 [LocationTrackingService] 注册用户状态变化回调函数');
|
||||
// 直接赋值,覆盖之前的回调
|
||||
this.userStatusCallback = callback;
|
||||
console.log('✅ [LocationTrackingService] 用户状态变化回调注册完成');
|
||||
// 返回取消订阅函数
|
||||
return () => {
|
||||
if (this.userStatusCallback === callback) {
|
||||
this.userStatusCallback = null;
|
||||
console.log('✅ [LocationTrackingService] 用户状态变化回调已取消');
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 检查用户是否在线
|
||||
*/
|
||||
isUserOnline(userId) {
|
||||
const userInfo = this.onlineUsers.get(userId);
|
||||
return userInfo ? userInfo.status === 'online' : false;
|
||||
}
|
||||
// ===== 私有方法 =====
|
||||
/**
|
||||
* 启动位置更新定时器
|
||||
*/
|
||||
startLocationUpdateTimer() {
|
||||
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秒更新一次
|
||||
}
|
||||
/**
|
||||
* 获取当前位置
|
||||
*/
|
||||
async getCurrentLocation() {
|
||||
const userInfo = userService_1.default.getGlobalUserInfo();
|
||||
if (!userInfo || !userInfo.id) {
|
||||
throw new Error('用户未登录');
|
||||
}
|
||||
try {
|
||||
// 使用mapService获取真实位置,保持与项目中其他地方的一致性
|
||||
const location = await mapService_1.default.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()
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置实时位置订阅
|
||||
*/
|
||||
async setupRealTimeSubscription() {
|
||||
// 获取用户信息
|
||||
const userInfo = userService_1.default.getGlobalUserInfo();
|
||||
if (!userInfo || !userInfo.id) {
|
||||
console.warn('用户未登录,无法设置实时位置订阅');
|
||||
return;
|
||||
}
|
||||
// 缓存用户信息(已移除)
|
||||
// 真实环境:初始化WebSocket连接(服务器会自动处理订阅逻辑)
|
||||
try {
|
||||
// 初始化WebSocket连接(如果尚未连接)
|
||||
await apiService_1.default.initLocationWebSocket();
|
||||
// 等待WebSocket连接建立(最多等待5秒)
|
||||
await new Promise((resolve, reject) => {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 50; // 5秒超时
|
||||
const checkConnection = () => {
|
||||
if (apiService_1.default.isWebSocketConnected()) {
|
||||
resolve();
|
||||
}
|
||||
else if (attempts < maxAttempts) {
|
||||
attempts++;
|
||||
setTimeout(checkConnection, 100);
|
||||
}
|
||||
else {
|
||||
reject(new Error('WebSocket连接建立超时'));
|
||||
}
|
||||
};
|
||||
checkConnection();
|
||||
});
|
||||
// 服务器会自动处理订阅逻辑,无需发送订阅消息
|
||||
console.log('WebSocket连接已建立,服务器将自动处理位置订阅逻辑');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('设置实时位置订阅失败:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 关闭实时位置订阅
|
||||
*/
|
||||
teardownRealTimeSubscription() {
|
||||
// 真实环境:关闭WebSocket连接(服务器会自动处理取消订阅逻辑)
|
||||
try {
|
||||
// 服务器会自动处理取消订阅逻辑,无需发送取消订阅消息
|
||||
console.log('关闭WebSocket连接,服务器将自动处理取消订阅逻辑');
|
||||
// 移除位置更新回调
|
||||
if (this.unsubscribeCallback) {
|
||||
this.unsubscribeCallback();
|
||||
this.unsubscribeCallback = null;
|
||||
}
|
||||
// 关闭WebSocket连接
|
||||
apiService_1.default.closeLocationWebSocket();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('关闭实时位置订阅失败:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 停止位置更新定时器
|
||||
*/
|
||||
stopLocationUpdateTimer() {
|
||||
if (this.locationUpdateTimer) {
|
||||
clearInterval(this.locationUpdateTimer);
|
||||
this.locationUpdateTimer = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 通知位置更新回调
|
||||
*/
|
||||
notifyLocationUpdateCallbacks() {
|
||||
const onlineUsers = this.getOnlineUsers();
|
||||
console.log('🔔 [LocationTrackingService] 开始通知位置更新回调');
|
||||
console.log('👥 [LocationTrackingService] 在线用户数量:', onlineUsers.length);
|
||||
// 触发位置更新回调
|
||||
if (this.locationUpdateCallback) {
|
||||
try {
|
||||
console.log('🔄 [LocationTrackingService] 执行位置更新回调');
|
||||
this.locationUpdateCallback(onlineUsers);
|
||||
console.log('✅ [LocationTrackingService] 位置更新回调执行成功');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('❌ [LocationTrackingService] 位置更新回调执行失败:', error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('⚠️ [LocationTrackingService] 没有注册位置更新回调');
|
||||
}
|
||||
console.log('🏁 [LocationTrackingService] 位置更新回调通知完成');
|
||||
}
|
||||
/**
|
||||
* 手动触发位置更新(公共方法,可从外部调用)
|
||||
* 用于手动刷新地图上的用户标记
|
||||
*/
|
||||
triggerLocationUpdate() {
|
||||
console.log('🔔 [LocationTrackingService] 手动触发位置更新');
|
||||
this.notifyLocationUpdateCallbacks();
|
||||
}
|
||||
/**
|
||||
* 订阅位置更新(用于locationModule注册回调)
|
||||
* @param callback 位置更新回调函数
|
||||
* @returns 取消订阅函数
|
||||
*/
|
||||
subscribeToLocationUpdates(callback) {
|
||||
console.log('📝 [LocationTrackingService] 注册位置更新回调函数');
|
||||
// 直接赋值,覆盖之前的回调
|
||||
this.locationUpdateCallback = callback;
|
||||
console.log('✅ [LocationTrackingService] 位置更新回调注册完成');
|
||||
// 返回取消订阅函数
|
||||
return () => {
|
||||
if (this.locationUpdateCallback === callback) {
|
||||
this.locationUpdateCallback = null;
|
||||
console.log('✅ [LocationTrackingService] 位置更新回调已取消');
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 通知用户状态变化回调
|
||||
*/
|
||||
notifyUserStatusChange(userId, status) {
|
||||
console.log(`🔔 [LocationTrackingService] 开始通知用户状态变化回调: 用户 ${userId} 状态变为 ${status}`);
|
||||
if (this.userStatusCallback) {
|
||||
try {
|
||||
console.log('🔄 [LocationTrackingService] 执行用户状态变化回调');
|
||||
this.userStatusCallback(userId, status);
|
||||
console.log('✅ [LocationTrackingService] 用户状态变化回调执行成功');
|
||||
}
|
||||
catch (error) {
|
||||
console.error('❌ [LocationTrackingService] 用户状态变化回调执行失败:', error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('⚠️ [LocationTrackingService] 没有注册用户状态变化回调');
|
||||
}
|
||||
console.log('🏁 [LocationTrackingService] 用户状态变化回调通知完成');
|
||||
}
|
||||
/**
|
||||
* 处理WebSocket消息
|
||||
* @param message 服务器下发的消息
|
||||
*/
|
||||
handleWebSocketMessage(message) {
|
||||
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) => ({
|
||||
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) => {
|
||||
// 支持多种数据格式:locationData字段或直接字段
|
||||
const locationData = user.locationData || user;
|
||||
// 验证必需字段是否存在
|
||||
if (!user.userId && !locationData.userId) {
|
||||
console.error('❌ 用户数据缺少userId字段:', user);
|
||||
return null;
|
||||
}
|
||||
if (locationData.latitude === undefined || locationData.longitude === undefined) {
|
||||
console.error('❌ 用户数据缺少位置信息:', user);
|
||||
return null;
|
||||
}
|
||||
// 对于位置更新消息,允许缺少name和role字段,从本地缓存中获取
|
||||
const existingUser = this.onlineUsers.get(user.userId || locationData.userId);
|
||||
const formattedUser = {
|
||||
userId: user.userId || locationData.userId,
|
||||
name: user.name || user.userName || (existingUser ? existingUser.name : `用户${user.userId || locationData.userId}`),
|
||||
role: user.role || (existingUser ? existingUser.role : 'DRIVER'),
|
||||
userStatus: user.userStatus !== false, // 转换为布尔值
|
||||
lastUpdateTime: user.lastUpdateTime || locationData.timestamp || Date.now(),
|
||||
latitude: locationData.latitude,
|
||||
longitude: locationData.longitude,
|
||||
timestamp: locationData.timestamp || user.timestamp || Date.now()
|
||||
};
|
||||
// 验证必需字段
|
||||
if (!formattedUser.userId) {
|
||||
console.error('❌ 用户数据缺少userId字段:', formattedUser);
|
||||
return null;
|
||||
}
|
||||
return formattedUser;
|
||||
}).filter((user) => user !== null); // 过滤掉无效数据
|
||||
console.log('📊 转换后的用户位置数据:', formattedUsers);
|
||||
this.triggerOnlineUserListCallbacks({
|
||||
type: 'userLocationList',
|
||||
users: formattedUsers
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.warn('❌ userLocationList消息格式错误,users字段不存在或不是数组');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn('收到未知类型的WebSocket消息:', message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 触发在线用户列表回调函数
|
||||
* @param userList 在线用户列表数据
|
||||
*/
|
||||
triggerOnlineUserListCallbacks(userList) {
|
||||
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] 在线用户列表回调触发完成');
|
||||
}
|
||||
/**
|
||||
* 处理在线用户列表
|
||||
* @param userList 服务器下发的在线用户列表
|
||||
*/
|
||||
handleOnlineUserList(userList) {
|
||||
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();
|
||||
// 对于位置更新消息,允许缺少name和role字段,从本地缓存中获取
|
||||
const existingUser = this.onlineUsers.get(user.userId);
|
||||
// 更新或添加用户信息
|
||||
this.onlineUsers.set(user.userId, {
|
||||
userId: user.userId,
|
||||
name: user.name || user.userName || (existingUser ? existingUser.name : `用户${user.userId}`),
|
||||
avatarUrl: '/images/user-avatar.png',
|
||||
role: user.role || (existingUser ? existingUser.role : 'DRIVER'),
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 位置追踪服务单例实例
|
||||
* 导出供应用程序全局使用
|
||||
*/
|
||||
exports.default = new LocationTrackingService();
|
||||
Reference in New Issue
Block a user