From 118ec385500f70206130bf94a0eb4a0dc01b190c Mon Sep 17 00:00:00 2001 From: Doubleyin <953994191@qq.com> Date: Sun, 19 Oct 2025 13:40:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=9C=A8=E7=BA=BF=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 +++- miniprogram/app.ts | 6 + .../pages/index/modules/locationModule.ts | 30 ++- .../pages/index/modules/loginModule.ts | 20 +- miniprogram/services/apiService.ts | 151 +++++++++----- .../services/locationTrackingService.ts | 192 ++++++++++-------- miniprogram/services/userService.ts | 5 +- 7 files changed, 283 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index 1ce5b5a..d9bc23f 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,12 @@ - **员工信息验证**: 注册时验证员工信息是否存在 - **角色管理**: 支持管理员和配送员两种角色分配 -### 📍 位置追踪功能 +### 位置追踪功能 - **实时位置更新**: 通过WebSocket实现货运人员实时位置追踪 - **位置订阅机制**: 支持订阅特定货运人员的位置更新 - **在线状态管理**: 实时显示货运人员在线/离线状态 - **位置历史记录**: 记录货运人员位置历史轨迹 +- **位置列表更新机制**: 系统通过WebSocket被动接收服务器推送的位置列表更新,不主动请求刷新 ### 🔧 系统管理功能 - **API配置管理**: 可以配置API服务器地址等参数 @@ -230,14 +231,35 @@ #### WebSocket实时位置消息 -1. **位置更新消息** +1. **在线用户列表消息** ```json { - "type": "updateLocation", // 消息类型 - "deliveryPersonId": number, // 货运人员ID - "latitude": number, // 纬度 - "longitude": number, // 经度 - "timestamp": number // 时间戳 + "type": "onlineUserList", // 消息类型 + "users": [ // 在线用户列表 + { + "id": number, // 用户ID + "name": "string", // 用户名(或userName字段) + "latitude": number, // 纬度 + "longitude": number, // 经度 + "timestamp": number // 时间戳(或lastUpdateTime字段) + } + ] + } + ``` + +2. **用户位置列表消息** + ```json + { + "type": "userLocationList", // 消息类型 + "users": [ // 用户位置列表 + { + "id": number, // 用户ID + "name": "string", // 用户名(或userName字段) + "latitude": number, // 纬度 + "longitude": number, // 经度 + "timestamp": number // 时间戳(或lastUpdateTime字段) + } + ] } ``` diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 94edd80..e7a1e1d 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -1,5 +1,6 @@ // app.ts import userService from "./services/userService"; +import locationTrackingService from "./services/locationTrackingService"; import { showToast } from "./utils/helpers"; // 定义应用类 @@ -26,6 +27,11 @@ class MyApp { console.log("=== 应用启动 ==="); console.log("调试信息 - 开始执行onLaunch"); try { + // 初始化位置追踪服务 + console.log("调试信息 - 初始化位置追踪服务"); + await locationTrackingService.initialize(); + console.log("调试信息 - 位置追踪服务初始化完成"); + // 初始化日志存储 const logs = wx.getStorageSync("logs") || []; logs.unshift(Date.now()); diff --git a/miniprogram/pages/index/modules/locationModule.ts b/miniprogram/pages/index/modules/locationModule.ts index 6f072b8..41cf950 100644 --- a/miniprogram/pages/index/modules/locationModule.ts +++ b/miniprogram/pages/index/modules/locationModule.ts @@ -142,30 +142,36 @@ export class LocationModule { * 处理位置更新回调 */ private handleLocationUpdates(locationData: any): void { - console.log('收到位置更新回调:', locationData); + console.log('🔔 [LocationModule] 收到位置更新回调'); + console.log('📊 [LocationModule] 回调数据类型:', locationData.type || '未知类型'); + console.log('📋 [LocationModule] 回调数据内容:', JSON.stringify(locationData, null, 2)); if (locationData.type === 'onlineUserList' && locationData.users) { // 处理在线用户列表更新 - console.log('处理在线用户列表,用户数量:', locationData.users.length); + console.log('👥 [LocationModule] 处理在线用户列表,用户数量:', locationData.users.length); this.updateEmployeeMarkers(locationData.users); } else if (Array.isArray(locationData)) { // 处理位置更新数组(旧格式) - console.log('处理位置更新数组,用户数量:', locationData.length); + console.log('📋 [LocationModule] 处理位置更新数组,用户数量:', locationData.length); this.updateEmployeeMarkers(locationData); } else if (locationData.userId && locationData.latitude && locationData.longitude) { // 处理单个用户位置更新 - console.log('处理单个用户位置更新:', locationData.userId); + console.log('👤 [LocationModule] 处理单个用户位置更新:', locationData.userId); this.updateSingleEmployeeMarker(locationData); } else { - console.warn('未知的位置数据格式:', locationData); + console.warn('❌ [LocationModule] 未知的位置数据格式:', locationData); } + + console.log('🏁 [LocationModule] 位置更新回调处理完成'); } /** * 更新员工标记点 */ private updateEmployeeMarkers(onlineUsers: any[]): void { - console.log('开始更新员工标记点,在线用户数量:', onlineUsers.length); + console.log('📍 [LocationModule] 开始更新员工标记点'); + console.log('👥 [LocationModule] 传入用户数量:', onlineUsers.length); + console.log('📋 [LocationModule] 用户数据示例:', JSON.stringify(onlineUsers[0], null, 2)); // 获取地图模块来更新标记点 const mapModule = this.dataModule.getMapModule(); @@ -175,7 +181,9 @@ export class LocationModule { } // 为每个在线用户创建标记点 - const employeeMarkers = onlineUsers.map(user => { + const employeeMarkers = onlineUsers.map((user, index) => { + console.log(`🔍 [LocationModule] 处理第 ${index + 1} 个用户:`, user.userId || '未知ID'); + // 获取用户角色信息 const userRole = user.role || this.getUserRole(user.userId); @@ -184,6 +192,9 @@ export class LocationModule { const latitude = user.latitude || (user.lastLocation && user.lastLocation.latitude) || 0; const lastUpdateTime = user.lastUpdateTime || Date.now(); + console.log(`📡 [LocationModule] 用户 ${index + 1} 位置: ${latitude}, ${longitude}`); + console.log(`👤 [LocationModule] 用户 ${index + 1} 信息: ${user.userName || '未知用户'} (${userRole})`); + const employeeMarker = { id: 10000 + user.userId, // 避免ID冲突 type: 'employee', @@ -204,10 +215,13 @@ export class LocationModule { return employeeMarker; }); + console.log('📍 [LocationModule] 生成标记点数量:', employeeMarkers.length); + console.log('📋 [LocationModule] 标记点数据示例:', JSON.stringify(employeeMarkers[0], null, 2)); + // 调用地图模块更新员工标记点 mapModule.updateEmployeeMarkers(employeeMarkers); - console.log(`成功更新了 ${employeeMarkers.length} 个员工标记点`); + console.log('✅ [LocationModule] 员工标记点更新完成'); } /** diff --git a/miniprogram/pages/index/modules/loginModule.ts b/miniprogram/pages/index/modules/loginModule.ts index b69fce0..4cbc267 100644 --- a/miniprogram/pages/index/modules/loginModule.ts +++ b/miniprogram/pages/index/modules/loginModule.ts @@ -193,8 +193,24 @@ export class LoginModule { title: '签到中...', }); - // 调用实际的签到接口 - const signInResult = await userService.signIn(); + // 先获取地图位置和当前时间 + let initialLocation: { latitude: number; longitude: number; timestamp: number }; + try { + const location = await locationTrackingService.getCurrentLocation(); + // 使用秒级时间戳(除以1000取整) + initialLocation = { + latitude: location.latitude, + longitude: location.longitude, + timestamp: Math.floor(Date.now() / 1000) + }; + console.log('获取到签到位置数据:', initialLocation); + } catch (error) { + console.error('获取位置失败:', error); + throw new Error('获取位置失败,请检查位置权限设置'); + } + + // 调用实际的签到接口,传递位置数据 + const signInResult = await userService.signIn(initialLocation); wx.hideLoading(); diff --git a/miniprogram/services/apiService.ts b/miniprogram/services/apiService.ts index ce8e59c..efab06f 100644 --- a/miniprogram/services/apiService.ts +++ b/miniprogram/services/apiService.ts @@ -7,7 +7,7 @@ import { WarehouseInfo, UserInfo, DeliveryPerson, Order, EmployeeInfo } from '.. */ // API基础URL配置 -const IS_LOCAL_DEV = false; // true: 本地开发环境, false: 生产环境 +const IS_LOCAL_DEV = true; // 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 ? '本地开发环境' : '生产环境'})`); @@ -140,15 +140,24 @@ async ServerLogin(code: string) { /** * 签到接口 * @param userId 用户ID + * @param initialLocation 初始位置数据(必须) * @returns 签到结果和员工信息 */ - async userSignIn(userId: number): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> { - console.log('API userSignIn调用,参数userId:', userId); + async userSignIn(userId: number, initialLocation: { latitude: number; longitude: number; timestamp: number }): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> { + console.log('API userSignIn调用,参数userId:', userId, 'initialLocation:', initialLocation); + + // 构建请求数据,必须包含位置数据 + const requestData: any = { + userId, + latitude: initialLocation.latitude, + longitude: initialLocation.longitude, + timestamp: initialLocation.timestamp + }; // 服务器返回的是用户对象,需要转换为前端期望的格式 const response = await this.request('/user/signin', { method: 'POST', - data: { userId }, + data: requestData, }); // 转换响应格式 @@ -562,29 +571,7 @@ async ServerLogin(code: string) { } } - /** - * 注册位置更新回调函数 - * @param callback 位置更新回调函数 - * @returns 取消订阅函数 - */ - onLocationUpdate(callback: (location: any) => void): () => void { - // 添加回调到集合 - if (!this.locationUpdateCallbacks) { - this.locationUpdateCallbacks = new Set(); - } - this.locationUpdateCallbacks.add(callback); - - // 返回取消订阅函数 - return () => { - if (this.locationUpdateCallbacks) { - this.locationUpdateCallbacks.delete(callback); - // 如果没有回调了,可以关闭WebSocket连接 - if (this.locationUpdateCallbacks.size === 0 && this.locationWebSocket) { - this.closeLocationWebSocket(); - } - } - }; - } + /** * 注册在线用户列表回调函数 @@ -625,8 +612,18 @@ async ServerLogin(code: string) { const serverUrl = API_BASE_URL.replace('http', 'ws'); const wsUrl = `${serverUrl}/ws/location`; + // 获取当前用户ID(从全局应用数据中获取) + const app = getApp(); + const userInfo = app.globalData.userInfo; + const userId = userInfo?.id; + + // 构建带用户ID参数的WebSocket URL + const finalWsUrl = userId ? `${wsUrl}?userId=${userId}` : wsUrl; + + console.log(`WebSocket连接URL: ${finalWsUrl}`); + this.locationWebSocket = wx.connectSocket({ - url: wsUrl, + url: finalWsUrl, header: { 'content-type': 'application/json' } @@ -714,18 +711,61 @@ async ServerLogin(code: string) { return; } + console.log(`📡 收到WebSocket消息类型: ${message.type}`, message); + switch (message.type) { - case 'updateLocation': - // 处理位置更新消息 - this.triggerLocationUpdateCallbacks(message); - break; case 'onlineUserList': - // 处理在线用户列表消息 - this.triggerOnlineUserListCallbacks(message); + // 处理在线用户列表消息(服务器发送当前在线用户列表) + 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 'subscribed': - // 处理订阅成功响应 - console.log('WebSocket订阅成功:', message); + 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); @@ -733,41 +773,40 @@ async ServerLogin(code: string) { } } - /** - * 触发位置更新回调 - * @param locationUpdate 位置更新信息 - */ - private triggerLocationUpdateCallbacks(locationUpdate: any): void { - if (this.locationUpdateCallbacks) { - this.locationUpdateCallbacks.forEach(callback => { - try { - callback(locationUpdate); - } catch (error) { - console.error('位置更新回调执行失败:', error); - } - }); - } - } + /** * 触发在线用户列表回调 * @param userList 在线用户列表信息 */ private triggerOnlineUserListCallbacks(userList: any): void { + console.log('🔔 [apiService] 开始触发在线用户列表回调,消息类型:', userList.type); + console.log('📊 [apiService] 回调数据内容:', JSON.stringify(userList, null, 2)); + if (this.onlineUserListCallbacks) { - this.onlineUserListCallbacks.forEach(callback => { + console.log(`📞 [apiService] 准备执行 ${this.onlineUserListCallbacks.size} 个回调函数`); + let index = 0; + this.onlineUserListCallbacks.forEach((callback) => { try { - callback(userList); + console.log(`🔄 [apiService] 执行第 ${index + 1} 个回调函数`); + // 传递正确的数据格式:只传递users数组,而不是整个消息对象 + callback(userList.users); + console.log(`✅ [apiService] 第 ${index + 1} 个回调函数执行成功`); + index++; } catch (error) { - console.error('在线用户列表回调执行失败:', error); + console.error(`❌ [apiService] 第 ${index + 1} 个回调函数执行失败:`, error); + index++; } }); + } else { + console.warn('⚠️ [apiService] 没有注册的在线用户列表回调函数'); } + console.log('🏁 [apiService] 在线用户列表回调触发完成'); } /** * 发送WebSocket消息 - * @param message 要发送的消息 + * @param message 要发送的消息对象 */ private sendWebSocketMessage(message: any): void { if (!this.locationWebSocket) { diff --git a/miniprogram/services/locationTrackingService.ts b/miniprogram/services/locationTrackingService.ts index 2fad5e6..9425291 100644 --- a/miniprogram/services/locationTrackingService.ts +++ b/miniprogram/services/locationTrackingService.ts @@ -69,13 +69,32 @@ class LocationTrackingService { 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); - // 签到消息已通过REST API处理,服务器会自动处理订阅逻辑 - // 设置实时位置订阅(初始化WebSocket连接) await this.setupRealTimeSubscription(); @@ -91,9 +110,6 @@ class LocationTrackingService { // 启动位置更新定时器(每30秒更新一次) this.startLocationUpdateTimer(); - // 启动状态检查定时器(每60秒检查一次) - this.startStatusCheckTimer(); - console.log('位置追踪服务已启动'); } @@ -102,11 +118,6 @@ class LocationTrackingService { */ public async initialize(): Promise { try { - // 注册位置更新回调 - apiService.onLocationUpdate((locationUpdate) => { - this.handleLocationUpdate(locationUpdate); - }); - // 注册在线用户列表回调 apiService.onOnlineUserList((userList) => { this.handleOnlineUserList(userList); @@ -119,6 +130,8 @@ class LocationTrackingService { } } + + /** * 用户签退后停止位置追踪 */ @@ -138,11 +151,15 @@ class LocationTrackingService { // 停止定时器 this.stopTimers(); + + // 关闭实时位置订阅(服务器会自动处理取消订阅) this.teardownRealTimeSubscription(); console.log('位置追踪服务已停止'); } + + /** * 手动更新用户位置 @@ -158,13 +175,14 @@ class LocationTrackingService { console.log(`- 用户ID: ${userInfo.id}`); console.log(`- 经度: ${location.longitude}`); console.log(`- 纬度: ${location.latitude}`); - console.log(`- 时间戳: ${Date.now()}`); + const timestamp = Math.floor(Date.now() / 1000); + console.log(`- 时间戳: ${timestamp}`); const locationData: LocationData = { userId: userInfo.id, longitude: location.longitude, latitude: location.latitude, - timestamp: Date.now() + timestamp: timestamp }; // 真实模式:通过WebSocket发送位置更新 @@ -174,9 +192,7 @@ class LocationTrackingService { // 同时更新本地缓存 this.updateLocalUserLocation(userInfo.id, locationData); - // 通知所有回调 - this.notifyLocationUpdateCallbacks(); - console.log('[位置追踪服务] 位置更新完成,已通知回调'); + console.log('[位置追踪服务] 位置更新完成'); } /** @@ -192,7 +208,10 @@ 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); } /** @@ -238,7 +257,7 @@ class LocationTrackingService { avatarUrl: '/images/user-avatar.png', role: userInfo.role || 'delivery_person', // 用户角色 lastLocation: currentLocation, - lastUpdateTime: Date.now(), + lastUpdateTime: Math.floor(Date.now() / 1000), status: 'online' }; @@ -330,7 +349,7 @@ class LocationTrackingService { /** * 获取当前位置 */ - private async getCurrentLocation(): Promise { + public async getCurrentLocation(): Promise { const userInfo = userService.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { throw new Error('用户未登录'); @@ -415,10 +434,7 @@ class LocationTrackingService { // 服务器会自动处理订阅逻辑,无需发送订阅消息 console.log('WebSocket连接已建立,服务器将自动处理位置订阅逻辑'); - // 设置位置更新回调 - this.unsubscribeCallback = apiService.onLocationUpdate((locationUpdate) => { - this.handleLocationUpdate(locationUpdate); - }); + } catch (error) { console.error('设置实时位置订阅失败:', error); @@ -476,18 +492,28 @@ class LocationTrackingService { } } + + /** * 通知位置更新回调 */ private notifyLocationUpdateCallbacks(): void { const onlineUsers = this.getOnlineUsers(); - this.locationUpdateCallbacks.forEach(callback => { + 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('位置更新回调执行失败:', error); + console.error(`❌ [LocationTrackingService] 第 ${index + 1} 个位置更新回调执行失败:`, error); } }); + + console.log('🏁 [LocationTrackingService] 位置更新回调通知完成'); } /** @@ -505,82 +531,84 @@ class LocationTrackingService { - /** - * 处理位置更新 - */ - private handleLocationUpdate(locationUpdate: any): void { - console.log('收到位置更新:', locationUpdate); - - // 验证位置更新消息格式 - if (!locationUpdate || typeof locationUpdate !== 'object') { - console.warn('无效的位置更新消息格式'); - return; - } - - // 提取关键信息 - const { deliveryPersonId, latitude, longitude, timestamp } = locationUpdate; - - if (!deliveryPersonId || latitude === undefined || longitude === undefined) { - console.warn('位置更新消息缺少必要字段:', locationUpdate); - return; - } - - console.log(`处理员工 ${deliveryPersonId} 的位置更新: (${longitude}, ${latitude})`); - - // 更新本地缓存中的员工位置 - this.updateLocalUserLocation(deliveryPersonId, { - userId: deliveryPersonId, - longitude: longitude, - latitude: latitude, - timestamp: timestamp || Date.now() - }); - - // 通知所有位置更新回调(UI层会监听这些回调来更新地图标记点) - this.notifyLocationUpdateCallbacks(); - - console.log(`员工 ${deliveryPersonId} 位置更新处理完成`); - } + /** - * 处理在线用户列表消息 - * @param userList 在线用户列表消息 + * 处理在线用户列表 + * @param userList 服务器下发的在线用户列表 */ - private handleOnlineUserList(userList: any): void { + private handleOnlineUserList(userList: any[]): void { try { - console.log('[LocationTrackingService] 收到在线用户列表:', userList); - - if (!userList || !userList.users) { - console.warn('[LocationTrackingService] 无效的在线用户列表消息'); + // 验证数据格式 + if (!Array.isArray(userList)) { + console.warn('[LocationTrackingService] 无效的在线用户列表数据格式'); return; } - // 清空当前在线用户列表 - this.onlineUsers.clear(); + console.log(`[LocationTrackingService] 开始处理在线用户列表,用户数量: ${userList.length}`); - // 更新在线用户列表 - userList.users.forEach((userInfo: any) => { - if (userInfo && userInfo.userId) { - this.onlineUsers.set(userInfo.userId, { - userId: userInfo.userId, - name: userInfo.userName || '未知用户', + // 获取当前本地用户列表的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: userInfo.role || 'delivery_person', // 用户角色,默认为配送员 + role: user.role || 'employee', lastLocation: { - userId: userInfo.userId, - longitude: userInfo.longitude || 0, - latitude: userInfo.latitude || 0, - timestamp: userInfo.lastUpdateTime || Date.now() + userId: user.userId, + longitude: longitude, + latitude: latitude, + timestamp: timestamp }, - lastUpdateTime: userInfo.lastUpdateTime || Date.now(), - status: 'online' + 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); } }); - console.log('[LocationTrackingService] 在线用户列表已更新,当前在线用户数:', this.onlineUsers.size); + // 删除本地列表中不在服务器列表中的用户 + 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); diff --git a/miniprogram/services/userService.ts b/miniprogram/services/userService.ts index b2ae98c..a3d287d 100644 --- a/miniprogram/services/userService.ts +++ b/miniprogram/services/userService.ts @@ -255,9 +255,10 @@ class UserService { /** * 用户签到 + * @param initialLocation 初始位置数据(必须) * @returns 签到结果和更新后的用户信息 */ - async signIn(): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> { + async signIn(initialLocation: { latitude: number; longitude: number; timestamp: number }): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> { const userInfo = this.getGlobalUserInfo(); if (!userInfo || !userInfo.id) { throw new Error('用户未登录,无法签到'); @@ -269,7 +270,7 @@ class UserService { throw new Error('用户认证信息缺失,请重新登录'); } - return apiService.userSignIn(userInfo.id); + return apiService.userSignIn(userInfo.id, initialLocation); } /**