util.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. import {
  2. getStorage,
  3. setStorage
  4. } from '@/utils/localStorage';
  5. window.sessionTime = new Date();
  6. var socketTaskList = []; //websocket 待发送任务列表
  7. var socketInter = null;
  8. /**
  9. * websocket配置项
  10. * @type {{serverTimeoutObj: null, timeoutObj: null, timeoutnum: null, lockReconnect: boolean, ws: null, params: {houseId: null, openid: null, userid: null}, timeout: number}}
  11. */
  12. let wsConfig = {
  13. ws: null,
  14. lockReconnect: false, //是否真正建立连接
  15. interval: null,
  16. timeoutObj: null, //心跳心跳倒计时
  17. serverTimeoutObj: null, // 服务连接超时时间
  18. timeoutnum: null, // 断开重连倒计时
  19. timeout: 58 * 1000, // 58秒一次心跳
  20. // 建立ws连接时,所需参数
  21. params: {
  22. openid: null,
  23. userid: null,
  24. houseId: null
  25. }
  26. }
  27. /**
  28. * 获取随机数,为socket请求拼接后缀
  29. */
  30. function uuid(len, radix) {
  31. var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  32. var uuid = [],
  33. i;
  34. radix = radix || chars.length;
  35. if (len) {
  36. for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
  37. } else {
  38. var r;
  39. uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
  40. uuid[14] = '4';
  41. for (i = 0; i < 36; i++) {
  42. if (!uuid[i]) {
  43. r = 0 | Math.random() * 16;
  44. uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
  45. }
  46. }
  47. }
  48. return uuid.join('');
  49. }
  50. /**
  51. * 初始化websocket
  52. */
  53. function initWebsocket() {
  54. let str1 = parseInt(Math.random() * 10);
  55. let str2 = parseInt(Math.random() * 10);
  56. let str3 = parseInt(Math.random() * 10);
  57. let str4 = "" + str1 + str2 + str3;
  58. //初始化weosocket
  59. let websocketUrl = $config.ws_url;
  60. let rod = uuid(8, 60);
  61. const wsuri =
  62. `${websocketUrl}gs-guide-websocket/` +
  63. str4 +
  64. "/" + rod + "/websocket";
  65. wsConfig.ws = new WebSocket(wsuri);
  66. // 连接建立时触发
  67. wsConfig.ws.onopen = websocketonopen;
  68. // 客户端接收服务端数据时触发
  69. wsConfig.ws.onmessage = websocketonmessage;
  70. // 通信发生错误时触发
  71. wsConfig.ws.onerror = websocketonerror;
  72. // 连接关闭时触发
  73. wsConfig.ws.onclose = websocketclose;
  74. return wsConfig.ws;
  75. }
  76. /**
  77. * 连接建立时触发
  78. */
  79. function websocketonopen() {
  80. let data = initPage();
  81. console.log('建立ws连接', data)
  82. //开启心跳
  83. start();
  84. //发送链接身份数据
  85. connectSend(data.openId, data.userId, data.houseId);
  86. }
  87. /**
  88. * 客户端接收服务端数据时触发
  89. * @param e
  90. */
  91. function websocketonmessage(e) {
  92. // console.log('客户端接收服务端数据时触发', e);
  93. //收到服务器信息,心跳重置
  94. reset();
  95. }
  96. /**
  97. * 通信发生错误时触发
  98. */
  99. function websocketonerror() {
  100. console.log("出现错误");
  101. reconnect();
  102. }
  103. /**
  104. * 连接关闭时触发
  105. * @param e
  106. */
  107. function websocketclose(e) {
  108. //关闭
  109. console.log("断开连接", e);
  110. socketInter && clearInterval(socketInter);//清空心跳发送
  111. socketInter = null;
  112. //重连
  113. reconnect();
  114. }
  115. function websocketsend(Data) {
  116. // console.log('ws状态', wsConfig.ws)
  117. if(wsConfig.ws && wsConfig.ws.readyState==1){
  118. let param = [
  119. "SEND" +
  120. "\nproject:" +
  121. "elab-marketing-system" +
  122. "\nmethod:" +
  123. "POST" +
  124. "\npath:" +
  125. "/behavior/brandMiniWeb/upload" +
  126. "\ndestination:" +
  127. "/ws/remote/invoke" +
  128. "\n\n" +
  129. JSON.stringify(Data) +
  130. "\u0000",
  131. ];
  132. //数据发送
  133. wsConfig.ws.send(JSON.stringify(param));
  134. }else{
  135. // 如果还没有创建好socket链接或者链接还没打开,则放到待发送列表中
  136. if(Data){
  137. socketTaskList.push(Data);
  138. }
  139. console.log("***ws-socketTaskList列表count***", socketTaskList.length)
  140. return
  141. }
  142. }
  143. /**
  144. * 解析页面路由参数
  145. * @param name
  146. * @returns {null}
  147. */
  148. function getQueryString(name) {
  149. let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
  150. let arr = window.location.href.split('?');
  151. let result = null;
  152. arr.forEach((item, index) => {
  153. let res = item.match(reg);
  154. if (res != null) {
  155. result = unescape(res[2]);
  156. }
  157. })
  158. return result
  159. }
  160. /**
  161. * 初始进入页面参数
  162. */
  163. function initPage(){
  164. let userInfo = getStorage('userInfo') ? JSON.parse(getStorage('userInfo')) : null;
  165. let queryObj = getStorage('queryObj') ? JSON.parse(getStorage('queryObj')) : null;
  166. let urlObj = queryObj || util.getUrlParams(location.href) || {};
  167. let brandId = urlObj.brandId || urlObj.special_ID || $config.brandId || '';
  168. let userId = urlObj.leavePhoneCustomerId ? urlObj.leavePhoneCustomerId : (userInfo?userInfo.userId:'');
  169. return {
  170. session: urlObj.session || '',
  171. // 项目id,默认仙女山项目
  172. houseId: urlObj.xcxHouseId || '',
  173. userId: userId || '',
  174. openId: urlObj.openid || (userInfo ? userInfo.openId : ''),
  175. brandId: brandId || '',
  176. product: urlObj.xcxHouseId || '',
  177. fromPlatform: urlObj.fromProduce || '',
  178. platform: urlObj.platform || '',
  179. scene: urlObj.scene1 || '',
  180. layoutName: urlObj.layoutName || '',
  181. }
  182. }
  183. /**
  184. * 发送ws连接请求
  185. */
  186. function connectSend(openid, userid, houseId) {
  187. console.log('建立ws通信', openid, userid, houseId)
  188. //发送堆积的待发送消息
  189. const param = [
  190. "CONNECT" +
  191. "\nopenId:" +
  192. openid +
  193. "\nplatform:" +
  194. "1" +
  195. "\nhouseId:" +
  196. houseId +
  197. "\nuserId:" +
  198. userid +
  199. "\naccept-version:1.1,1.0\nheart-beat:5000,5000\n\n\u0000",
  200. ];
  201. websocketsend(JSON.stringify(param));
  202. if (socketTaskList && socketTaskList.length > 0) {
  203. for (var i = 0; i < socketTaskList.length; i++) {
  204. if(socketTaskList[i]){
  205. // wsConfig.ws.send(socketTaskList[i]);
  206. if(!socketTaskList[i].userId){
  207. socketTaskList[i].userId = userid;
  208. socketTaskList[i].brandUserId = userid;
  209. }
  210. if(!socketTaskList[i].openId){
  211. socketTaskList[i].openId = openid;
  212. }
  213. // console.log("***ws-socketTaskList列表***", socketTaskList[i])
  214. let param = [
  215. "SEND" +
  216. "\nproject:" +
  217. "elab-marketing-system" +
  218. "\nmethod:" +
  219. "POST" +
  220. "\npath:" +
  221. "/behavior/brandMiniWeb/upload" +
  222. "\ndestination:" +
  223. "/ws/remote/invoke" +
  224. "\n\n" +
  225. JSON.stringify(socketTaskList[i]) +
  226. "\u0000",
  227. ];
  228. // 发送堆积的待发送消息
  229. wsConfig.ws.send(JSON.stringify(param));
  230. socketTaskList[i] = null;
  231. }
  232. }
  233. for (var i = 0; i < socketTaskList.length; i++) {
  234. if(!socketTaskList[i]){
  235. socketTaskList.splice(i,1);
  236. }
  237. }
  238. }
  239. }
  240. /**
  241. * 重新连接ws
  242. */
  243. function reconnect() {
  244. console.log('重新连接ws');
  245. //重新连接
  246. if (wsConfig.lockReconnect) {
  247. return;
  248. }
  249. wsConfig.lockReconnect = true;
  250. //没连接上会一直重连,设置延迟避免请求过多
  251. wsConfig.timeoutnum && clearTimeout(wsConfig.timeoutnum);
  252. wsConfig.timeoutnum = setTimeout(() => {
  253. //新连接
  254. initWebsocket()
  255. wsConfig.lockReconnect = false
  256. }, 5000)
  257. socketInter && clearInterval(socketInter);//清空心跳发送
  258. socketInter = null;
  259. }
  260. /**
  261. * 重置ws连接
  262. */
  263. function reset() {
  264. console.log('重启心跳')
  265. //重置心跳
  266. clearTimeout(wsConfig.timeoutObj);
  267. // //清除时间
  268. clearTimeout(wsConfig.serverTimeoutObj);
  269. //重启心跳
  270. // wsSendHeartBeat();
  271. //开启心跳
  272. start();
  273. }
  274. // socket 心跳发送
  275. function wsSendHeartBeat() {
  276. let ws = wsConfig.ws;
  277. let data = ["\n"];//心跳的数据格式
  278. if(!socketInter){
  279. console.warn('***ws-开启WebSocket心跳***');
  280. //5秒钟发送一次心跳
  281. socketInter = setInterval(()=>{
  282. // console.warn("***ws-SendHeartBeat-尝试心跳发送:");
  283. //这里发送一个心跳,后端收到后,返回一个心跳消息,
  284. // console.warn('***ws-心跳***');
  285. if (ws.readyState == 1) {
  286. //如果连接正常
  287. ws.send(JSON.stringify(data)); //这里可以自己跟后端约定
  288. } else {
  289. //否则重连
  290. reconnect();
  291. }
  292. },5000)
  293. }
  294. }
  295. /**
  296. * 开启ws连接
  297. */
  298. function start() {
  299. let ws = wsConfig.ws;
  300. // console.log("开启心跳", wsConfig,ws.readyState);
  301. let data = ["\n"];//心跳的数据格式
  302. ws.send(JSON.stringify(data));
  303. wsConfig.timeoutObj && clearTimeout(wsConfig.timeoutObj);
  304. wsConfig.serverTimeoutObj && clearTimeout(wsConfig.serverTimeoutObj);
  305. wsConfig.timeoutObj = setTimeout(function() {
  306. //这里发送一个心跳,后端收到后,返回一个心跳消息,
  307. if (websock.readyState == 1) {
  308. //如果连接正常
  309. ws.send(JSON.stringify(data)); //这里可以自己跟后端约定
  310. } else {
  311. //否则重连
  312. self.reconnect();
  313. }
  314. wsConfig.serverTimeoutObj = setTimeout(function() {
  315. console.warn("***超时主动关闭***")
  316. //超时关闭
  317. ws.close();
  318. }, wsConfig.timeout);
  319. }, wsConfig.timeout);
  320. // wsSendHeartBeat();//开始心跳
  321. }
  322. var util = {
  323. dateFormat(date, fmt) {
  324. let ret
  325. const opt = {
  326. 'y+': date.getFullYear().toString(), // 年
  327. 'M+': (date.getMonth() + 1).toString(), // 月
  328. 'd+': date.getDate().toString(), // 日
  329. 'H+': date.getHours().toString(), // 时
  330. 'm+': date.getMinutes().toString(), // 分
  331. 's+': date.getSeconds().toString(), // 秒
  332. // 有其他格式化字符需求可以继续添加,必须转化成字符串
  333. }
  334. for (let k in opt) {
  335. ret = new RegExp('(' + k + ')').exec(fmt)
  336. if (ret) {
  337. fmt = fmt.replace(
  338. ret[1],
  339. ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')
  340. )
  341. }
  342. }
  343. return fmt
  344. },
  345. formatDate: function(date, fmt) {
  346. if (/(y+)/.test(fmt)) {
  347. fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  348. }
  349. let o = {
  350. 'M+': date.getMonth() + 1,
  351. 'd+': date.getDate(),
  352. 'h+': date.getHours(),
  353. 'm+': date.getMinutes(),
  354. 's+': date.getSeconds()
  355. }
  356. for (let k in o) {
  357. if (new RegExp(`(${k})`).test(fmt)) {
  358. let str = o[k] + ''
  359. fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : this.padLeftZero(str))
  360. }
  361. }
  362. return fmt
  363. },
  364. padLeftZero: function(str) {
  365. return ('00' + str).substr(str.length)
  366. },
  367. formatDatetime(val, format) {
  368. const year = val.getFullYear();
  369. const month = val.getMonth() + 1;
  370. const day = val.getDate();
  371. const hour = val.getHours();
  372. const minute = val.getMinutes();
  373. const second = val.getSeconds();
  374. return year + '-' + (month > 9 ? month : '0' + month) + '-' + day + ' ' + hour + ':' + (minute > 9 ?
  375. minute : '0' + minute) + ':' + (second > 9 ? second : '0' + second);
  376. },
  377. formatDayTime(val) {
  378. const year = val.getFullYear();
  379. const month = val.getMonth() + 1;
  380. const day = val.getDate();
  381. const hour = val.getHours();
  382. const minute = val.getMinutes();
  383. const second = val.getSeconds();
  384. return year + '-' + (month > 9 ? month : '0' + month) + '-' + day + ' ' + hour + ':' + (minute > 9 ?
  385. minute : '0' + minute);
  386. },
  387. formatTodayTime(val) {
  388. const year = val.getFullYear();
  389. const month = val.getMonth() + 1;
  390. const day = val.getDate();
  391. const hour = val.getHours();
  392. const minute = val.getMinutes();
  393. if (month == new Date().getMonth() + 1 && day + 1 == new Date().getDate()) {
  394. return '昨天' + hour + ':' + (minute > 9 ? minute : '0' + minute);
  395. } else if (month == new Date().getMonth() + 1 && day == new Date().getDate()) {
  396. return hour + ':' + (minute > 9 ? minute : '0' + minute);
  397. } else {
  398. return year + '-' + (month > 9 ? month : '0' + month) + '-' + day + ' ' + hour + ':' + (minute > 9 ?
  399. minute : '0' + minute);
  400. }
  401. },
  402. /**
  403. * 计算天数
  404. * @param date1
  405. * @param date2
  406. * @returns {number}
  407. * @constructor
  408. */
  409. getNumberOfDays(date1, date2) { //获得天数
  410. if (!date1 || !date2) {
  411. return 0;
  412. }
  413. //date1:开始日期,date2结束日期
  414. var a1 = Date.parse(new Date(date1));
  415. var a2 = Date.parse(new Date(date2));
  416. var day = parseInt((a2 - a1) / (1000 * 60 * 60 * 24)) + 1; //核心:时间戳相减,然后除以天数
  417. return day
  418. },
  419. getUrlParams(url) {
  420. url = url == null ? window.location.href : url;
  421. var search = url.substring(url.lastIndexOf("?") + 1);
  422. var obj = {};
  423. var reg = /([^?&=]+)=([^?&=]*)/g;
  424. // [^?&=]+表示:除了?、&、=之外的一到多个字符
  425. // [^?&=]*表示:除了?、&、=之外的0到多个字符(任意多个)
  426. search.replace(reg, function(rs, $1, $2) {
  427. var name = decodeURIComponent($1);
  428. var val = decodeURIComponent($2);
  429. val = String(val);
  430. obj[name] = val;
  431. return rs;
  432. });
  433. return obj;
  434. },
  435. formatTime(date) {
  436. var year = date.getFullYear()
  437. var month = date.getMonth() + 1
  438. var day = date.getDate()
  439. var hour = date.getHours()
  440. var minute = date.getMinutes()
  441. var second = date.getSeconds()
  442. return [year, month, day].map(this.formatNumber).join('-') + ' ' + [hour, minute, second].map(this
  443. .formatNumber).join(
  444. ':')
  445. },
  446. formatNumber(n) {
  447. n = n.toString()
  448. return n[1] ? n : '0' + n
  449. },
  450. trackRequest(para, app = null) {
  451. if ((para.type && para.type.includes('Error'))) {
  452. //所有报错埋点以及曝光埋点不再发送至服务器
  453. return
  454. }
  455. try {
  456. if(typeof (para.clkParams) == 'object'){
  457. let obj = {
  458. locusBehaviorName: para.locusBehaviorName ? para.locusBehaviorName : ''
  459. }
  460. para.clkParams = Object.assign(obj,para.clkParams)
  461. }
  462. let pvCurPageParams = "";//字符串string对象
  463. if(para.pvCurPageParams){//调用的时候传递进来的-先转为对象
  464. pvCurPageParams = typeof para.pvCurPageParams === 'object' ? para.pvCurPageParams : JSON.parse(para.pvCurPageParams)
  465. }else{
  466. pvCurPageParams = {};
  467. }
  468. let queryObj = getStorage('queryObj') ? JSON.parse(getStorage('queryObj')) : null;
  469. let urlObj = queryObj || util.getUrlParams(location.href) || {};
  470. let userInfo = getStorage('userInfo') ? JSON.parse(getStorage('userInfo')) : null;
  471. let userId = urlObj.leavePhoneCustomerId ? urlObj.leavePhoneCustomerId : (userInfo?userInfo.userId:'');
  472. let brandId = urlObj.special_ID || $config.brandId || ''
  473. //在页面参数里面手动添加path参数
  474. pvCurPageParams.brandId = brandId;
  475. pvCurPageParams.locusBehaviorName = para.locusBehaviorName || '';
  476. pvCurPageParams.locusValue = para.locusValue || '';
  477. pvCurPageParams.locusName = para.locusName || '';
  478. let data = {
  479. session: '',
  480. userAgent: navigator.userAgent.substring(0, 255) || '',
  481. browserName: navigator.appName || '',
  482. browserVersion: navigator.appVersion.substring(0, 255) || '',
  483. platform: 'h5', //iframeUrl代表是顾问分享的外链
  484. fromPlatform: urlObj.fromPlatform || urlObj.fromProduce || 'h5',
  485. ip: window.ip || '',
  486. cookieId: from_cookie || getStorage("cookie_id") || '',
  487. openId: urlObj.openid || (userInfo ? userInfo.openId : ''),
  488. userId: userId,
  489. brandUserId: userId,
  490. createTime: this.formatTime(new Date()),
  491. uploadTime: this.formatTime(new Date()),
  492. product: 'h5', //iframeUrl代表是顾问分享的外链
  493. project: para.project || urlObj.xcxHouseId || $config.xcxHouseId || '',
  494. brandId: brandId,
  495. expand: JSON.stringify(urlObj), //扩展字段
  496. imTalkId: para.imTalkId || '', //IM对话编号
  497. imTalkType: para.imTalkType || '', //IM对话类型
  498. eventName: para.eventName || '', //事件名称
  499. clkDesPage: para.clkDesPage || '', //点击前往的页面名称
  500. clkId: para.clkId || '', //点击ID
  501. clkName: para.clkName || '',
  502. pvId: para.pvId || '', //PV埋点ID
  503. clkParams: typeof para.clkParams === 'object' ? JSON.stringify(para.clkParams) : (para.clkParams || ''), //点击参数
  504. pvCurPageName: para.pvCurPageName || '', //当前页面名称
  505. pvCurPageParams: typeof pvCurPageParams === 'object' ? JSON.stringify(pvCurPageParams) : (pvCurPageParams ||''), //当前页面参数
  506. pvLastPageName: para.pvLastPageName || '', //上一页页面名称
  507. pvLastPageParams: para.pvLastPageParams || '', //上一页页面参数
  508. pvPageLoadTime: para.pvPageLoadTime || '', //加载时间
  509. type: para.type || '', //埋点类型
  510. reserve1: urlObj.channel || '', //来源平台,抖音百度微信
  511. }
  512. let timeNow = new Date().getTime();
  513. let session = getStorage('sessionNumber') ? Number(getStorage('sessionNumber')) : timeNow;
  514. if (timeNow - sessionTime > 180000 && !urlObj.session) {
  515. // session++;
  516. session = timeNow;
  517. setStorage('sessionNumber', session)
  518. }
  519. data.session = urlObj.session || data.userId + "_" + session || '';
  520. sessionTime = timeNow;
  521. // return data;
  522. // app.globalData.session_id = data.session
  523. // app.globalData.sessionTime = timeNow;
  524. // requestConfig('upload', data, true);
  525. // let param = ["SEND" +
  526. // "\nproject:" + "elab-marketing-system" +
  527. // "\nmethod:" + 'POST' +
  528. // "\npath:" + '/behavior/brandMiniWeb/upload' +
  529. // "\ndestination:" + '/ws/remote/invoke' +
  530. // "\n\n" + JSON.stringify(data) +
  531. // "\u0000"
  532. // ];
  533. // app.wsSendOrder(param,data);//socket 消息发送
  534. console.warn("***mook***", (data.pvId || data.clkId || data.eventId), data)
  535. // let param = [
  536. // "SEND" +
  537. // "\nproject:" +
  538. // "elab-marketing-system" +
  539. // "\nmethod:" +
  540. // "POST" +
  541. // "\npath:" +
  542. // "/behavior/brandMiniWeb/upload" +
  543. // "\ndestination:" +
  544. // "/ws/remote/invoke" +
  545. // "\n\n" +
  546. // JSON.stringify(data) +
  547. // "\u0000",
  548. // ];
  549. // console.log("上报埋点数据", param);
  550. // websocketsend(JSON.stringify(param));
  551. websocketsend(data);
  552. } catch (e) {
  553. console.warn("***util.js-onError***", e);
  554. }
  555. },
  556. getSession() { //获取session
  557. let timeNow = new Date().getTime();
  558. let session = uni.getStorageSync('sessionNumber') || timeNow; //session具体的值
  559. uni.setStorage({
  560. key: "sessionNumber",
  561. data: session
  562. })
  563. return session;
  564. },
  565. initWebsocket:initWebsocket,
  566. };
  567. window.from_session = util.getUrlParams(location.href).session || '';
  568. window.from_cookie = util.getUrlParams(location.href).cookie || '';
  569. export default util;
  570. // module.exports = util;