电速宝
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // app.js
  2. const api = require('./api/index.js');
  3. App({
  4. globalData: {
  5. socketTask: null,
  6. isConnected: false,
  7. reconnectTimer: null,
  8. heartbeatTimer: null,
  9. currentWorkorderId: '',
  10. userInfo: wx.getStorageSync('user') || {}
  11. },
  12. onLaunch() {
  13. this.initWebSocketService();
  14. wx.onAppShow(() => this.handleAppShow());
  15. wx.onAppHide(() => this.handleAppHide());
  16. this.locationService = {
  17. isTracking: false,
  18. currentWorkorderId: '',
  19. isLocationUpdateStarted: false
  20. };
  21. // 司机自动启动【普通实时定位上传】
  22. const { operationRole } = this.globalData.userInfo;
  23. if (operationRole === 4) {
  24. this.startDriverRealTimeLocation();
  25. }
  26. },
  27. initWebSocketService() {
  28. if (!this.wsService) {
  29. this.wsService = {
  30. connect: (url) => {
  31. const that = this;
  32. if (that.globalData.socketTask) {
  33. that.wsService.close();
  34. }
  35. const socketTask = wx.connectSocket({ url, header: { 'content-type': 'application/json' } });
  36. socketTask.onOpen(() => {
  37. that.globalData.isConnected = true;
  38. that.globalData.socketTask = socketTask;
  39. that.clearReconnectTimer();
  40. if (that.globalData.currentWorkorderId) {
  41. that.wsService.send({ type: 'subscribe', workorderId: that.globalData.currentWorkorderId });
  42. }
  43. wx.$emit('socketOpen');
  44. });
  45. socketTask.onMessage((res) => {
  46. try {
  47. const message = JSON.parse(res.data);
  48. wx.$emit('wsMessage', message);
  49. } catch (err) { }
  50. });
  51. socketTask.onClose((res) => {
  52. that.globalData.isConnected = false;
  53. that.globalData.socketTask = null;
  54. that.stopHeartbeat();
  55. if (res.code !== 1000) that.startReconnect();
  56. });
  57. socketTask.onError((err) => {
  58. that.globalData.isConnected = false;
  59. that.globalData.socketTask = null;
  60. that.stopHeartbeat();
  61. });
  62. },
  63. send: (data) => {
  64. const that = this;
  65. if (that.globalData.isConnected && that.globalData.socketTask) {
  66. that.globalData.socketTask.send({
  67. data: JSON.stringify(data),
  68. fail: (err) => console.error('发送失败', err)
  69. });
  70. }
  71. },
  72. close: (code = 1000, reason = 'normal close') => {
  73. const that = this;
  74. if (that.globalData.socketTask) {
  75. that.globalData.socketTask.close({ code, reason });
  76. }
  77. }
  78. };
  79. }
  80. },
  81. handleAppShow() {
  82. const that = this;
  83. if (!that.globalData.isConnected && !that.globalData.reconnectTimer) {
  84. const { operationId } = that.globalData.userInfo;
  85. if (!operationId) return;
  86. const wsUrl = `wss://esos-iot.com:9443/communication/update/${operationId}`;
  87. that.wsService.connect(wsUrl);
  88. }
  89. },
  90. handleAppHide() { },
  91. startHeartbeat() {
  92. const that = this;
  93. that.stopHeartbeat();
  94. that.heartbeatTimer = setInterval(() => {
  95. if (that.globalData.isConnected) {
  96. that.wsService.send({ type: 'heartbeat', timestamp: Date.now() });
  97. }
  98. }, 5000);
  99. },
  100. stopHeartbeat() {
  101. if (this.heartbeatTimer) {
  102. clearInterval(this.heartbeatTimer);
  103. this.heartbeatTimer = null;
  104. }
  105. },
  106. startReconnect() {
  107. const that = this;
  108. that.clearReconnectTimer();
  109. let delay = 1000;
  110. that.globalData.reconnectTimer = setInterval(() => {
  111. if (!that.globalData.isConnected) {
  112. const { operationId } = that.globalData.userInfo;
  113. if (operationId) {
  114. const wsUrl = `wss://esos-iot.com:9443/communication/update/${operationId}`;
  115. that.wsService.connect(wsUrl);
  116. }
  117. delay = Math.min(delay * 2, 8000);
  118. } else {
  119. that.clearReconnectTimer();
  120. }
  121. }, delay);
  122. },
  123. clearReconnectTimer() {
  124. if (this.globalData.reconnectTimer) {
  125. clearInterval(this.globalData.reconnectTimer);
  126. this.globalData.reconnectTimer = null;
  127. }
  128. },
  129. /****************************************************************************
  130. * 1. 司机【普通实时定位】(一直上传,不绑定工单)
  131. ***************************************************************************/
  132. startDriverRealTimeLocation() {
  133. const { operationRole } = this.globalData.userInfo;
  134. if (operationRole !== 4) return;
  135. const service = this.locationService;
  136. this._removeLocationListener();
  137. const startLocationApi = wx.startLocationUpdateBackground || wx.startLocationUpdate;
  138. startLocationApi({
  139. type: 'gcj02',
  140. success: () => {
  141. service.isLocationUpdateStarted = true;
  142. let lastLocation = null;
  143. const DISTANCE_THRESHOLD = 5;
  144. wx.onLocationChange((location) => {
  145. // 距离过滤
  146. if (lastLocation) {
  147. const distance = this.calculateDistance(
  148. lastLocation.latitude, lastLocation.longitude,
  149. location.latitude, location.longitude
  150. );
  151. if (distance < DISTANCE_THRESHOLD) return;
  152. }
  153. lastLocation = location;
  154. // 统一上传:实时位置 + 有工单就工单一起传
  155. this._uploadRealTimeLocation(location);
  156. if (service.currentWorkorderId) {
  157. this._uploadWorkorderLocation(location, service.currentWorkorderId);
  158. }
  159. });
  160. }
  161. });
  162. },
  163. /****************************************************************************
  164. * 2. 司机【开始执行工单】(绑定工单,额外上传工单位置)
  165. ***************************************************************************/
  166. startDriverWorkorderLocation(workorderId) {
  167. if (!workorderId) return;
  168. const service = this.locationService;
  169. service.currentWorkorderId = workorderId;
  170. },
  171. /****************************************************************************
  172. * 3. 停止工单定位(只停止工单,不影响实时定位上传)
  173. ***************************************************************************/
  174. stopDriverWorkorderLocation() {
  175. const service = this.locationService;
  176. service.currentWorkorderId = '';
  177. },
  178. /****************************************************************************
  179. * 上传:司机实时位置(一直传)
  180. ***************************************************************************/
  181. _uploadRealTimeLocation(location) {
  182. const { operationRole } = this.globalData.userInfo;
  183. if (operationRole !== 4) return;
  184. const latOk = location.latitude >= 3.86 && location.latitude <= 53.55;
  185. const lngOk = location.longitude >= 73.66 && location.longitude <= 135.05;
  186. if (!latOk || !lngOk) return;
  187. const data = {
  188. userType: operationRole,
  189. latitude: location.latitude,
  190. longitude: location.longitude,
  191. accuracy: location.accuracy,
  192. createTime: Date.now()
  193. };
  194. api.request('/sysoperation/inseroperationlocation', 'post', data, { isPublic: false })
  195. .catch(err => console.error('实时位置上传失败', err));
  196. },
  197. /****************************************************************************
  198. * 上传:工单位置(执行工单时才传)
  199. ***************************************************************************/
  200. _uploadWorkorderLocation(location, workorderId) {
  201. const latOk = location.latitude >= 3.86 && location.latitude <= 53.55;
  202. const lngOk = location.longitude >= 73.66 && location.longitude <= 135.05;
  203. if (!latOk || !lngOk) return;
  204. const data = {
  205. workorderId: workorderId,
  206. latitude: location.latitude,
  207. longitude: location.longitude,
  208. createTime: Date.now(),
  209. accuracy: location.accuracy
  210. };
  211. api.request('/sysworkorder/insercoordinateredis', 'post', data, { isPublic: false })
  212. .catch(err => console.error('工单位置上传失败', err));
  213. },
  214. calculateDistance(lat1, lng1, lat2, lng2) {
  215. const R = 6371000;
  216. const radLat1 = lat1 * Math.PI / 180;
  217. const radLat2 = lat2 * Math.PI / 180;
  218. const dLat = radLat2 - radLat1;
  219. const dLng = (lng2 - lng1) * Math.PI / 180;
  220. const a = Math.sin(dLat / 2) ** 2 + Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(dLng / 2) ** 2;
  221. return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  222. },
  223. _removeLocationListener() {
  224. if (wx.offLocationChange) wx.offLocationChange();
  225. }
  226. });
  227. // 全局事件总线
  228. wx.$on = function (eventName, callback) {
  229. if (!this.$events) this.$events = {};
  230. if (!this.$events[eventName]) this.$events[eventName] = [];
  231. this.$events[eventName].push(callback);
  232. };
  233. wx.$emit = function (eventName, data) {
  234. if (!this.$events || !this.$events[eventName]) return;
  235. this.$events[eventName].forEach(cb => cb(data));
  236. };
  237. wx.$off = function (eventName, callback) {
  238. if (!this.$events || !this.$events[eventName]) return;
  239. if (callback) this.$events[eventName] = this.$events[eventName].filter(cb => cb !== callback);
  240. else this.$events[eventName] = [];
  241. };