| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- // app.js
- const api = require('./api/index.js'); // 注意路径
- App({
- globalData: {
- socketTask: null,
- isConnected: false,
- reconnectTimer: null,
- heartbeatTimer: null,
- currentWorkorderId: '',
- userInfo: wx.getStorageSync('user') || {}
- },
-
- onLaunch() {
- // wx.setStorageSync('user', { userId: 'driver_1001', operationRole: 4 });
- this.initWebSocketService();
- wx.onAppShow(() => this.handleAppShow());
- wx.onAppHide(() => this.handleAppHide());
- this.locationService = {
- isTracking: false,
- currentWorkorderId: '', // 存储当前跟踪的工单ID
- isLocationUpdateStarted: false // 标记是否已启动定位服务
- };
- },
-
- initWebSocketService() {
- if (!this.wsService) {
- this.wsService = {
- connect: (url) => {
- const that = this;
- if (that.globalData.socketTask) {
- that.wsService.close();
- }
-
- console.log('正在连接WebSocket:', url);
- const socketTask = wx.connectSocket({ url, header: { 'content-type': 'application/json' } });
-
- socketTask.onOpen(() => {
- console.log('WebSocket 连接成功');
- that.globalData.isConnected = true;
- that.globalData.socketTask = socketTask;
- that.clearReconnectTimer();
- // that.startHeartbeat();
- if (that.globalData.currentWorkorderId) {
- that.wsService.send({ type: 'subscribe', workorderId: that.globalData.currentWorkorderId });
- }
- wx.$emit('socketOpen');
- });
-
- socketTask.onMessage((res) => {
- try {
- console.log(res);
- const message = JSON.parse(res.data);
- console.log('收到消息:', message);
- wx.$emit('wsMessage', message);
- } catch (err) { console.error('消息解析失败', err); }
- });
-
- socketTask.onClose((res) => {
- console.log('WebSocket 连接关闭, 状态码:', res.code);
- that.globalData.isConnected = false;
- that.globalData.socketTask = null;
- that.stopHeartbeat();
- wx.$emit('socketClose');
- if (res.code !== 1000) { that.startReconnect(); }
- });
-
- socketTask.onError((err) => {
- console.error('WebSocket 连接错误:', err);
- that.globalData.isConnected = false;
- that.globalData.socketTask = null;
- that.stopHeartbeat();
- wx.$emit('socketError', err);
- });
- },
-
- send: (data) => {
- const that = this;
- if (that.globalData.isConnected && that.globalData.socketTask) {
- try {
- that.globalData.socketTask.send({
- data: JSON.stringify(data),
- fail: (err) => {
- console.error('消息发送失败:', err);
- wx.showToast({ title: '消息发送失败', icon: 'none' });
- }
- });
- } catch (e) { console.error('消息序列化失败:', e); }
- } else {
- console.warn('WebSocket 未连接');
- wx.showToast({ title: '网络连接中...', icon: 'none' });
- }
- },
-
- close: (code = 1000, reason = 'normal close') => {
- const that = this;
- if (that.globalData.socketTask) {
- console.log(`关闭WebSocket连接: ${code} - ${reason}`);
- that.globalData.socketTask.close({ code, reason });
- }
- }
- };
- }
- },
-
- handleAppShow() {
- const that = this;
- console.log(!that.globalData.isConnected);
- console.log(!that.globalData.reconnectTimer);
- if (!that.globalData.isConnected && !that.globalData.reconnectTimer) {
- const { operationId, operationRole } = that.globalData.userInfo;
- console.log(operationRole);
- // 修复:startReconnect 中用了 operationId,这里统一字段
- if (!operationId) {
- console.error('用户operationId不存在,无法连接WebSocket');
- return;
- }
- const wsUrl = `wss://esos-iot.bjdexn.cn:9443/communication/update/${operationId}`;
- that.wsService.connect(wsUrl);
- }
- },
-
- handleAppHide() {
- // 可以选择在后台关闭连接
- // this.wsService.close(1001, 'app enter background');
- },
-
- startHeartbeat() {
- const that = this;
- that.stopHeartbeat();
- that.heartbeatTimer = setInterval(() => {
- if (that.globalData.isConnected) {
- that.wsService.send({ type: 'heartbeat', timestamp: Date.now() });
- }
- }, 5000);
- },
-
- stopHeartbeat() {
- if (this.heartbeatTimer) {
- clearInterval(this.heartbeatTimer);
- this.heartbeatTimer = null;
- }
- },
-
- startReconnect() {
- const that = this;
- that.clearReconnectTimer();
- let delay = 1000;
- that.globalData.reconnectTimer = setInterval(() => {
- if (!that.globalData.isConnected) {
- console.log(`尝试重连 in ${delay}ms...`);
- const { operationId, operationRole } = that.globalData.userInfo; // 修复:用 operationId 而非 userId
- if (operationId) {
- const wsUrl = `wss://esos-iot.bjdexn.cn:9443/communication/update/${operationId}`;
- that.wsService.connect(wsUrl);
- }
- delay = Math.min(delay * 2, 8000);
- } else {
- that.clearReconnectTimer();
- }
- }, delay);
- },
-
- clearReconnectTimer() {
- if (this.globalData.reconnectTimer) {
- clearInterval(this.globalData.reconnectTimer);
- this.globalData.reconnectTimer = null;
- }
- },
-
- /**
- * 司机端开始后台定位并上传(单工单)
- * @param {string} workorderId - 当前工单ID
- */
- startDriverLocationUpload(workorderId) {
- const service = this.locationService;
-
- // 入参校验
- if (!workorderId) {
- console.error('工单ID不能为空');
- wx.showToast({ title: '工单ID异常', icon: 'none' });
- return;
- }
-
- // 如果已经在为当前订单上传,则忽略
- if (service.isTracking && service.currentWorkorderId === workorderId) {
- console.log(`已经在为订单 ${workorderId} 上传位置`);
- return;
- }
-
- // 如果已经在为其他订单上传,先停止
- if (service.isTracking && service.currentWorkorderId !== workorderId) {
- console.log(`当前正在为订单 ${service.currentWorkorderId} 上传,切换到 ${workorderId},先停止旧定位`);
- this.stopDriverLocationUpload();
- }
-
- // 权限校验
- wx.getSetting({
- success: (res) => {
- if (!res.authSetting['scope.userLocationBackground']) {
- wx.authorize({
- scope: 'scope.userLocationBackground',
- success: () => {
- this._doStartLocationUpload(workorderId, service);
- },
- fail: () => {
- wx.showModal({
- title: '权限不足',
- content: '需要开启后台定位权限才能实时上传位置,请前往设置页开启',
- confirmText: '去设置',
- success: (modalRes) => {
- if (modalRes.confirm) {
- wx.openSetting({
- success: (settingRes) => {
- if (settingRes.authSetting['scope.userLocationBackground']) {
- this._doStartLocationUpload(workorderId, service);
- }
- }
- });
- }
- }
- });
- }
- });
- } else {
- this._doStartLocationUpload(workorderId, service);
- }
- }
- });
- },
-
- /**
- * 私有方法:执行真正的定位启动逻辑(核心修复:兼容低版本,改用 wx.onLocationChange)
- */
- //
- _doStartLocationUpload(workorderId, service) {
- console.log(`开始为订单 ${workorderId} 启动后台定位上传...`);
- service.currentWorkorderId = workorderId;
-
- // 修复1:先移除旧的定位监听(防止重复监听)
- this._removeLocationListener();
-
- // 修复2:启动后台定位(兼容低版本,用 startLocationUpdate 兜底)
- const startLocationApi = wx.startLocationUpdateBackground || wx.startLocationUpdate;
- startLocationApi({
- type: 'gcj02',
- success: (res) => {
- console.log('定位服务已启动', res);
- service.isLocationUpdateStarted = true;
- // 存储上一次的定位信息
- let lastLocation = null;
- // 距离阈值(单位:米,根据需求调整,5米)
- const DISTANCE_THRESHOLD = 5;
- // 修复3:改用低版本支持的 wx.onLocationChange(替代 wx.watchPosition)
- wx.onLocationChange((location) => {
- if (!lastLocation) {
- lastLocation = location;
- console.log('位置变化,准备上传:', location);
- this._uploadLocation(location, workorderId);
- return;
- }
- // 计算当前定位与上一次的直线距离(Haversine公式)
- const distance = this.calculateDistance(
- lastLocation.latitude,
- lastLocation.longitude,
- location.latitude,
- location.longitude
- );
- console.log(distance);
- // 只有距离超过阈值,才认为是“有效变化”
- if (distance >= DISTANCE_THRESHOLD) {
- lastLocation = location; // 更新上一次定位
- this._uploadLocation(location, workorderId);
- }
-
- });
- // 标记定位中状态
- service.isTracking = true;
- console.log('位置监听已注册,开始实时上传');
- },
- fail: (err) => {
- let errMsg = '启动定位失败';
- if (err.errMsg.includes('auth deny')) errMsg = '定位权限被拒绝';
- else if (err.errMsg.includes('system')) errMsg = '系统不支持定位';
-
- console.error(errMsg, err);
- wx.showToast({ title: errMsg, icon: 'none', duration: 2000 });
- }
- });
- },
-
- // Haversine公式:计算两点经纬度之间的直线距离(单位:米)
- calculateDistance(lat1, lng1, lat2, lng2) {
- const R = 6371000; // 地球半径(米)
- const radLat1 = (lat1 * Math.PI) / 180;
- const radLat2 = (lat2 * Math.PI) / 180;
- const deltaLat = radLat2 - radLat1;
- const deltaLng = (lng2 - lng1) * Math.PI / 180;
-
- const a =
- Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
- Math.cos(radLat1) * Math.cos(radLat2) *
- Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
-
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return R * c; // 距离(米)
- },
- /**
- * 司机端停止定位上传(核心修复:对应移除 wx.onLocationChange 监听)
- */
- stopDriverLocationUpload() {
- const service = this.locationService;
-
- if (!service.isTracking) {
- return;
- }
-
- console.log(`停止为订单 ${service.currentWorkorderId} 上传位置`);
-
- // 1. 移除位置监听(wx.onLocationChange 对应 wx.offLocationChange)
- this._removeLocationListener();
-
- // 2. 停止定位服务(兼容低版本,用 stopLocationUpdate 兜底)
- this._stopLocationService();
-
- // 3. 重置状态
- service.isTracking = false;
- service.currentWorkorderId = '';
- service.isLocationUpdateStarted = false;
- },
-
- /**
- * 私有方法:移除位置监听(兼容低版本)
- */
- _removeLocationListener() {
- if (wx.offLocationChange) {
- wx.offLocationChange();
- console.log('位置监听已移除');
- } else {
- console.warn('当前基础库不支持 wx.offLocationChange,可能存在重复监听风险');
- }
- },
-
- /**
- * 私有方法:统一停止定位服务(兼容低版本)
- */
- _stopLocationService() {
- const stopLocationApi = wx.stopLocationUpdateBackground || wx.stopLocationUpdate;
- if (stopLocationApi && this.locationService.isLocationUpdateStarted) {
- stopLocationApi({
- success: (res) => {
- console.log('定位服务已停止', res);
- },
- fail: (err) => {
- console.error('停止定位服务失败:', err);
- }
- });
- }
- },
-
- /**
- * 私有方法:上传位置到服务器(无改动)
- */
- _uploadLocation(location, workorderId, retryCount = 0) {
- const maxRetry = 2;
- const isLatValid = location.latitude >= 3.86 && location.latitude <= 53.55;
- const isLngValid = location.longitude >= 73.66 && location.longitude <= 135.05;
-
- if (!isLatValid || !isLngValid) {
- console.warn(`无效位置信息(订单: ${workorderId}),跳过上传`, location);
- return;
- }
-
- const data = {
- workorderId: workorderId,
- latitude: location.latitude,
- longitude: location.longitude,
- createTime: Date.now(),
- accuracy: location.accuracy
- };
-
- api.request(`/sysworkorder/insercoordinateredis`, 'post', data, { isPublic: false })
- .then((response) => {
- if (response.code !== 200) {
- throw new Error(`响应码异常: ${response.code}`);
- }
- console.log(`位置上传成功 (订单: ${workorderId})`, data);
- })
- .catch((err) => {
- console.error(`位置上传失败(第${retryCount+1}次)(订单: ${workorderId})`, err);
- if (retryCount < maxRetry) {
- setTimeout(() => {
- this._uploadLocation(location, workorderId, retryCount + 1);
- }, 10000 * (retryCount + 1));
- } else {
- wx.showToast({ title: '位置上传失败,请检查网络', icon: 'none' });
- }
- });
- },
- });
-
- // 全局事件总线
- wx.$on = function (eventName, callback) {
- if (!this.$events) this.$events = {};
- if (!this.$events[eventName]) this.$events[eventName] = [];
- this.$events[eventName].push(callback);
- };
-
- wx.$emit = function (eventName, data) {
- if (!this.$events || !this.$events[eventName]) return;
- this.$events[eventName].forEach(callback => callback(data));
- };
-
- wx.$off = function (eventName, callback) {
- if (!this.$events || !this.$events[eventName]) return;
- if (callback) {
- this.$events[eventName] = this.$events[eventName].filter(cb => cb !== callback);
- } else {
- this.$events[eventName] = [];
- }
- };
- // 司机端增加人脸识别与指纹识别,
- // 云平台12月份电价整理
|