From 38a9610071e01f027a405959a1088d5118529594 Mon Sep 17 00:00:00 2001 From: Stormeye Wu Date: Thu, 11 Apr 2019 17:10:04 +0800 Subject: [PATCH] =?UTF-8?q?[websockets][=E6=96=B0=E5=A2=9E]:=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E6=94=AF=E6=8C=81websocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 148 +++++++++++++++++- config/config.js | 6 +- ext.json | 2 + pages/index/index.js | 363 ++++++++++++++++++++++--------------------- project.config.json | 11 +- utils/stomp.min.js | 8 + 6 files changed, 350 insertions(+), 188 deletions(-) create mode 100644 utils/stomp.min.js diff --git a/app.js b/app.js index ecec6c1..25c6969 100644 --- a/app.js +++ b/app.js @@ -2,7 +2,7 @@ let config = require("./config/config.js"); const Http = require("./utils/HttpBasics"); App({ data: {}, - onLaunch: function(options) { + onLaunch: function (options) { console.log("onUnload ") var that = this; that.globalData.sceneAddress = options.scene; @@ -42,21 +42,23 @@ App({ /** * 获取地址位置信息 */ - getLocation: function() { + getLocation: function () { var that = this wx.getLocation({ type: "wgs84", - success: function(res) { + success: function (res) { that.globalData.locationInfo = res; }, fail: error => { console.log(error); } }); - }, + }, globalData: { // token token: null, + // user openId + openId: null, // 渠道 sceneAddress: null, // location info @@ -73,6 +75,144 @@ App({ // 当前商场信息 market: { name: "陕西大悦城" + }, + // websocket + socketClient: null, + socketReceiver: function (e) { }, //收到消息回调 + }, + + // 初始化websocket + initSocket: function () { + let that = this; + // socket是否连接 + let socketConnected = false; + // 待发送的消息队列 + let messageQueue = []; + // 是否断线重连 + let reconnect = true; + + // 发送消息 + function sendSocketMessage(msg) { + // console.log(msg); + // 如果socket已连接则发送消息 + if (socketConnected) { + wx.sendSocketMessage({ + data: msg + }) + } else { + // socket没有连接将消息放入队列中 + messageQueue.push(msg); + } + } + + // 关闭连接 + function close() { + if (socketConnected) { + wx.closeSocket() + socketConnected = false; + } } + + // 符合WebSocket定义的对象 + var ws = { + send: sendSocketMessage, + close: close + } + + // 创建一个 WebSocket 连接 + function connect() { + wx.connectSocket({ + url: config.wsurl, + header: { + token: that.globalData.token //从服务端获取一个token,服务端验证token是否允许连接,案例中没做限制 + } + }) + } + connect(); + + // 监听 WebSocket 连接打开事件 + wx.onSocketOpen(function (res) { + console.log("WebSocket 连接成功") + socketConnected = true; + ws.onopen(); + // 连接成功后,将队列中的消息发送出去 + let queueLength = messageQueue.length + for (let i = 0; i < queueLength; i++) { + sendSocketMessage(messageQueue.shift()) + } + }) + + // 监听 WebSocket 接受到服务器的消息事件 + wx.onSocketMessage(function (res) { + ws.onmessage(res); + }) + + // 监听 WebSocket 错误事件 + wx.onSocketError(function (res) { + console.log("WebSocket 错误事件") + if (!socketConnected) { + // 断线重连 + if (reconnect) { + connect(); + } + } + }) + + // 监听 WebSocket 连接关闭事件 + wx.onSocketClose(function (res) { + console.log("WebSocket 连接关闭") + socketConnected = false; + // 断线重连 + if (reconnect) { + connect(); + } + }) + + const Stomp = require('/utils/stomp.min.js').Stomp; + + /** + * 定期发送心跳或检测服务器心跳 + * The heart-beating is using window.setInterval() to regularly send heart-beats and/or check server heart-beats. + * 可看stomp.js的源码(195,207,489行),由于小程序没有window对象,所以我们要调用小程序的定时器api实现 + */ + Stomp.setInterval = function (interval, f) { + return setInterval(f, interval); + }; + // 结束定时器的循环调用 + Stomp.clearInterval = function (id) { + return clearInterval(id); + }; + + const stompClient = Stomp.over(ws); + + that.globalData.socketClient = stompClient; + + stompClient.connect({}, function (callback) { + + // 订阅自己的 + stompClient.subscribe('/user/' + config.weapp.AppId + '/' + that.globalData.openId + '/message', function (message, headers) { + console.log('收到只发送给我的消息:', message); + that.globalData.socketReceiver(JSON.parse(message.body)); + // 通知服务端收到消息 + message.ack(); + }); + + stompClient.subscribe('/topic/' + config.weapp.AppId, function (message, headers) { + console.log('收到TOPIC的消息:', message); + that.globalData.socketReceiver(JSON.parse(message.body)); + // 通知服务端收到消息 + //message.ack(); + }); + + // 向服务端发送消息 + stompClient.send("/app/message", {}, JSON.stringify({ + 'msg': 'client: ' + that.globalData.openId + })); + }) + }, + + //统一发送消息 + sendSocketMessage: function (msg) { + this.globalData.socketClient.send("/app/message", {}, JSON.stringify(msg)); } }); diff --git a/config/config.js b/config/config.js index 20d00f3..48c585c 100755 --- a/config/config.js +++ b/config/config.js @@ -1,15 +1,19 @@ const extConfig = wx.getExtConfigSync ? wx.getExtConfigSync() : {} let weappId; let configUrls; +let sockUrls; if (Object.keys(extConfig).length == 0){ weappId = 'wx649b3be73c1afe47'; configUrls = 'https://ctest.malls.iformall.com/C/api'; + sockUrls = 'wss://wstest.malls.iformall.com/W/ws'; }else{ - configUrls = extConfig.attr.configUrl; weappId = extConfig.weappId; + configUrls = extConfig.attr.configUrl; + sockUrls = extConfig.attr.socketUrl; } var config = { url: configUrls, + wsurl: sockUrls, api: { /** * 接口用途:login diff --git a/ext.json b/ext.json index 06ff011..f6fcf28 100644 --- a/ext.json +++ b/ext.json @@ -7,8 +7,10 @@ "appVersion": "C.4.2.100", "attr": { "ifHaveCarModular": "1", + "ifHaveWebSocket": "1", "etcpversion": "release", "configUrl": "https://ciformall.youlane.cn/C/api", + "socketUrl": "wss://mp.youlane.cn/W/ws", "passCarAppId": "wx192b7d2e8dcbefd0", "passCarreturnUrl": "https://ciformall.youlane.cn/api/carCallback/etcpPaidCallback" } diff --git a/pages/index/index.js b/pages/index/index.js index 32268e5..0a7f632 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -1,3 +1,4 @@ +const extConfig = wx.getExtConfigSync ? wx.getExtConfigSync() : {} const Http = require("../../utils/HttpBasics"); const imgurl = require("../../utils/imgurl"); const config = require("../../config/config"); @@ -25,27 +26,27 @@ Page({ // app.globalData.type = 'pc'; if (decodeURIComponent(options.scene) == "undefined") { that.setData({ - scene: 0 + scene: 0 }); } else { that.setData({ - scene: decodeURIComponent(options.scene) + scene: decodeURIComponent(options.scene) }); that.setData({ - newArr: this.data.scene.split(':') + newArr: this.data.scene.split(':') }) console.log(this.data.newArr) if (this.data.newArr[0]=='JC'){ - /** - * 来自大屏跳转券详情 - */ - options.couponChannelId = this.data.newArr[1]; + /** + * 来自大屏跳转券详情 + */ + options.couponChannelId = this.data.newArr[1]; }else if (this.data.newArr[0] == 'JG'){ - /** - * 来自大屏跳转拼团详情 - */ - options.couponChannelId = this.data.newArr[1]; - options.path='daping' + /** + * 来自大屏跳转拼团详情 + */ + options.couponChannelId = this.data.newArr[1]; + options.path='daping' } } wx.showToast({ @@ -70,107 +71,107 @@ Page({ data: {} }) .then(res => { - if (options && options.orderGroupId) { - wx.redirectTo({ - url: `/pages/joinFrDpell/index?couponId=${options.couponId}&orderGroupId=${options.orderGroupId}&couponChannelId=${options.couponChannelId}&orderId=${options.orderId}&avatarUrl=${options.avatarUrl}&nickName=${options.nickName}` - }) - return; - } - /** - * 来自大屏的跳转拼团券详情 - */ - if (options && options.couponChannelId && options.path== 'daping'){ - /** - * 主要是为了拿couponId - */ - Http.get({ - url: config.api.couponDetail, - data: { - couponChannelId: options.couponChannelId - } - }).then(res => { - let data = res.data; - wx.redirectTo({ - url: `/pages/spellGroup/mySpellGroup/index?couponChannelId=${options.couponChannelId}&couponId=${data.couponId}` - }) - }); - return; - } - if (options && options.couponChannelId && !options.cuserId &&app.globalData.type == 'cd') { - wx.redirectTo({ - url: `/pages/coupon/detail/index?couponChannelId=${options.couponChannelId}`, - }) - } else if (options &&options.couponChannelId && options.cuserId){ - /** - * 转赠判断 - */ - wx.redirectTo({ - url: `/pages/coupon/detail/index?couponChannelId=${options.couponChannelId}&cuserId=${options.cuserId}&coverImg=${options.coverImg}&userName=${options.userName}&avatarUrl=${options.avatarUrl}&couponOrderId=${options.couponOrderId}&updateDate=${options.updateDate}`, - }) - } else if (options&&options.orderId&&app.globalData.type=='bg') { - wx.redirectTo({ - url: `/pages/bargain/bargainDatail/bargainDatail?orderId=${options.orderId}&from='${"discount"}`, - }) - } else if (app.globalData.type == 'uc'){ - wx.switchTab({ - url: '/pages/user/index' - }); - } else if (app.globalData.type == 'pc') { - wx.switchTab({ - url: '/pages/passCar/passCar' - }); - } else if (app.globalData.type == 'co') { - wx.redirectTo({ - url: '/pages/couponorder/index/index' - }); - } else if (app.globalData.type == 'or') { - wx.redirectTo({ - url: '/pages/couponorder/index/index' - }); - } else if (app.globalData.type == 'sc') { - wx.redirectTo({ - url: '/pages/specialcourtesy/specialcourtesy' - }); - }else if (app.globalData.type == 'rb') { - wx.redirectTo({ - url: '/pages/rushToBuy/index' - }); - }else{ - wx.switchTab({ - url: '/pages/main/index' - }); - } + if (options && options.orderGroupId) { + wx.redirectTo({ + url: `/pages/joinFrDpell/index?couponId=${options.couponId}&orderGroupId=${options.orderGroupId}&couponChannelId=${options.couponChannelId}&orderId=${options.orderId}&avatarUrl=${options.avatarUrl}&nickName=${options.nickName}` + }) + return; + } + /** + * 来自大屏的跳转拼团券详情 + */ + if (options && options.couponChannelId && options.path== 'daping'){ + /** + * 主要是为了拿couponId + */ + Http.get({ + url: config.api.couponDetail, + data: { + couponChannelId: options.couponChannelId + } + }).then(res => { + let data = res.data; + wx.redirectTo({ + url: `/pages/spellGroup/mySpellGroup/index?couponChannelId=${options.couponChannelId}&couponId=${data.couponId}` + }) + }); + return; + } + if (options && options.couponChannelId && !options.cuserId &&app.globalData.type == 'cd') { + wx.redirectTo({ + url: `/pages/coupon/detail/index?couponChannelId=${options.couponChannelId}`, + }) + } else if (options &&options.couponChannelId && options.cuserId){ + /** + * 转赠判断 + */ + wx.redirectTo({ + url: `/pages/coupon/detail/index?couponChannelId=${options.couponChannelId}&cuserId=${options.cuserId}&coverImg=${options.coverImg}&userName=${options.userName}&avatarUrl=${options.avatarUrl}&couponOrderId=${options.couponOrderId}&updateDate=${options.updateDate}`, + }) + } else if (options&&options.orderId&&app.globalData.type=='bg') { + wx.redirectTo({ + url: `/pages/bargain/bargainDatail/bargainDatail?orderId=${options.orderId}&from='${"discount"}`, + }) + } else if (app.globalData.type == 'uc'){ + wx.switchTab({ + url: '/pages/user/index' + }); + } else if (app.globalData.type == 'pc') { + wx.switchTab({ + url: '/pages/passCar/passCar' + }); + } else if (app.globalData.type == 'co') { + wx.redirectTo({ + url: '/pages/couponorder/index/index' + }); + } else if (app.globalData.type == 'or') { + wx.redirectTo({ + url: '/pages/couponorder/index/index' + }); + } else if (app.globalData.type == 'sc') { + wx.redirectTo({ + url: '/pages/specialcourtesy/specialcourtesy' + }); + }else if (app.globalData.type == 'rb') { + wx.redirectTo({ + url: '/pages/rushToBuy/index' + }); + }else{ + wx.switchTab({ + url: '/pages/main/index' + }); + } }) .catch(err => { - if (err.code == 11004) { - // 用户昵称未授权 - if (options && options.orderGroupId) { - wx.redirectTo({ - url: `/pages/getuserinfo/index?couponId=${options.couponId}&orderGroupId=${options.orderGroupId}&couponChannelId=${options.couponChannelId}&orderId=${options.orderId}&avatarUrl=${options.avatarUrl}&nickName=${options.nickName}` - }) - return; - } - if (options && options.couponChannelId && !options.cuserId) { - wx.redirectTo({ - url: `/pages/getuserinfo/index?couponChannelId=${options.couponChannelId}` - }); - } else if (options &&options.couponChannelId && options.cuserId){ - /** - * 转赠判断 - */ - wx.redirectTo({ - url: `/pages/getuserinfo/index?couponChannelId=${options.couponChannelId}&cuserId=${options.cuserId}&coverImg=${options.coverImg}&userName=${options.userName}&avatarUrl=${options.avatarUrl}&couponOrderId=${options.couponOrderId}&updateDate=${options.updateDate}` - }); - } else if (options && options.orderId) { - wx.redirectTo({ - url: `/pages/getuserinfo/index?orderId=${options.orderId}&from='${"discount"}` - }); - } else { - wx.redirectTo({ - url: `/pages/getuserinfo/index` - }); - } - } + if (err.code == 11004) { + // 用户昵称未授权 + if (options && options.orderGroupId) { + wx.redirectTo({ + url: `/pages/getuserinfo/index?couponId=${options.couponId}&orderGroupId=${options.orderGroupId}&couponChannelId=${options.couponChannelId}&orderId=${options.orderId}&avatarUrl=${options.avatarUrl}&nickName=${options.nickName}` + }) + return; + } + if (options && options.couponChannelId && !options.cuserId) { + wx.redirectTo({ + url: `/pages/getuserinfo/index?couponChannelId=${options.couponChannelId}` + }); + } else if (options &&options.couponChannelId && options.cuserId){ + /** + * 转赠判断 + */ + wx.redirectTo({ + url: `/pages/getuserinfo/index?couponChannelId=${options.couponChannelId}&cuserId=${options.cuserId}&coverImg=${options.coverImg}&userName=${options.userName}&avatarUrl=${options.avatarUrl}&couponOrderId=${options.couponOrderId}&updateDate=${options.updateDate}` + }); + } else if (options && options.orderId) { + wx.redirectTo({ + url: `/pages/getuserinfo/index?orderId=${options.orderId}&from='${"discount"}` + }); + } else { + wx.redirectTo({ + url: `/pages/getuserinfo/index` + }); + } + } }); }, /** @@ -182,71 +183,85 @@ Page({ // 登录 wx.login({ success: ({ - code + code }) => { - wx.getSystemInfo({ - success: function(res) { - that.setData({ - systemInfo: JSON.stringify(res) - }) - } - }) - var usrdata = { - appId: config.weapp.AppId, - code: code, - sceneAddress: app.globalData.sceneAddress, - scene: that.data.scene, - systemInfo: that.data.systemInfo - }; - if (app.globalData.locationInfo) { - usrdata = { - appId: config.weapp.AppId, - code: code, - sceneAddress: app.globalData.sceneAddress, - latitude: "" + app.globalData.locationInfo.latitude, - longitude: "" + app.globalData.locationInfo.longitude, - scene: that.data.scene, - systemInfo: that.data.systemInfo - }; - } - Http.post({ - url: config.api.login, - data: usrdata + wx.getSystemInfo({ + success: function(res) { + that.setData({ + systemInfo: JSON.stringify(res) + }) + } + }) + var usrdata = { + appId: config.weapp.AppId, + code: code, + sceneAddress: app.globalData.sceneAddress, + scene: that.data.scene, + systemInfo: that.data.systemInfo + }; + if (app.globalData.locationInfo) { + usrdata = { + appId: config.weapp.AppId, + code: code, + sceneAddress: app.globalData.sceneAddress, + latitude: "" + app.globalData.locationInfo.latitude, + longitude: "" + app.globalData.locationInfo.longitude, + scene: that.data.scene, + systemInfo: that.data.systemInfo + }; + } + Http.post({ + url: config.api.login, + data: usrdata + }) + .then(res => { + that.setData({ + showPages: true + }) + app.globalData.token = res.data.token; + app.globalData.openId = res.data.openId; + // 初始化websocket + if (extConfig.attr.ifHaveWebSocket == 1) { + app.initSocket(); + // 回调发送给自己的消息 + app.globalData.socketReceiver = function (e) { + let msgList = that.data.msgList + msgList.push(e) + that.setData({ + msgList: msgList }) - .then(res => { - that.setData({ - showPages: true - }) - app.globalData.token = res.data.token; - if (res.data && res.data.score) { - if (res.data.score != 0){ - app.globalData.score=res.data.score; - } - } - Http.setToken(res.data.token); - if (options&&(options.couponChannelId || options.orderId)){ - that.checkuerstatus(options); - }else{ - that.checkuerstatus(); - } - }) - .catch(err => { - wx.showModal({ - title: '提示', - showCancel: false, - content: '登录失败,请重新尝试', - success:function(res){ - if (res.cancel) { - //点击取消,默认隐藏弹框 - } else { - //点击确定 - wx.reLaunch({ - url: '/pages/index/index', - }) - } - } - }) - }); + } + } + + if (res.data && res.data.score) { + if (res.data.score != 0){ + app.globalData.score=res.data.score; + } + } + Http.setToken(res.data.token); + if (options&&(options.couponChannelId || options.orderId)){ + that.checkuerstatus(options); + }else{ + that.checkuerstatus(); + } + }) + .catch(err => { + wx.showModal({ + title: '提示', + showCancel: false, + content: '登录失败,请重新尝试', + success:function(res){ + if (res.cancel) { + //点击取消,默认隐藏弹框 + } else { + //点击确定 + wx.reLaunch({ + url: '/pages/index/index', + }) + } + } + }) + }); } }); }, diff --git a/project.config.json b/project.config.json index b11f49c..e32a248 100644 --- a/project.config.json +++ b/project.config.json @@ -34,15 +34,8 @@ "list": [] }, "miniprogram": { - "current": 0, - "list": [ - { - "id": 0, - "name": "fhrufhr", - "pathName": "pages/index/index", - "query": "scene=\"1111\"" - } - ] + "current": -1, + "list": [] } } } \ No newline at end of file diff --git a/utils/stomp.min.js b/utils/stomp.min.js new file mode 100644 index 0000000..dd0b1fa --- /dev/null +++ b/utils/stomp.min.js @@ -0,0 +1,8 @@ +// Generated by CoffeeScript 1.7.1 +/* + Stomp Over WebSocket http://www.jmesnil.net/stomp-websocket/doc/ | Apache License V2.0 + + Copyright (C) 2010-2013 [Jeff Mesnil](http://jmesnil.net/) + Copyright (C) 2012 [FuseSource, Inc.](http://fusesource.com) + */ +(function(){var t,e,n,i,r={}.hasOwnProperty,o=[].slice;t={LF:"\n",NULL:"\x00"};n=function(){var e;function n(t,e,n){this.command=t;this.headers=e!=null?e:{};this.body=n!=null?n:""}n.prototype.toString=function(){var e,i,o,s,u;e=[this.command];o=this.headers["content-length"]===false?true:false;if(o){delete this.headers["content-length"]}u=this.headers;for(i in u){if(!r.call(u,i))continue;s=u[i];e.push(""+i+":"+s)}if(this.body&&!o){e.push("content-length:"+n.sizeOfUTF8(this.body))}e.push(t.LF+this.body);return e.join(t.LF)};n.sizeOfUTF8=function(t){if(t){return encodeURI(t).match(/%..|./g).length}else{return 0}};e=function(e){var i,r,o,s,u,a,c,f,h,l,p,d,g,b,m,v,y;s=e.search(RegExp(""+t.LF+t.LF));u=e.substring(0,s).split(t.LF);o=u.shift();a={};d=function(t){return t.replace(/^\s+|\s+$/g,"")};v=u.reverse();for(g=0,m=v.length;gy;c=p<=y?++b:--b){r=e.charAt(c);if(r===t.NULL){break}i+=r}}return new n(o,a,i)};n.unmarshall=function(n){var i,r,o,s;r=n.split(RegExp(""+t.NULL+t.LF+"*"));s={frames:[],partial:""};s.frames=function(){var t,n,o,s;o=r.slice(0,-1);s=[];for(t=0,n=o.length;t>> "+r)}while(true){if(r.length>this.maxWebSocketFrameSize){this.ws.send(r.substring(0,this.maxWebSocketFrameSize));r=r.substring(this.maxWebSocketFrameSize);if(typeof this.debug==="function"){this.debug("remaining = "+r.length)}}else{return this.ws.send(r)}}};r.prototype._setupHeartbeat=function(n){var r,o,s,u,a,c;if((a=n.version)!==i.VERSIONS.V1_1&&a!==i.VERSIONS.V1_2){return}c=function(){var t,e,i,r;i=n["heart-beat"].split(",");r=[];for(t=0,e=i.length;t>> PING"):void 0}}(this))}if(!(this.heartbeat.incoming===0||o===0)){s=Math.max(this.heartbeat.incoming,o);if(typeof this.debug==="function"){this.debug("check PONG every "+s+"ms")}return this.ponger=i.setInterval(s,function(t){return function(){var n;n=e()-t.serverActivity;if(n>s*2){if(typeof t.debug==="function"){t.debug("did not receive server activity for the last "+n+"ms")}return t.ws.close()}}}(this))}};r.prototype._parseConnect=function(){var t,e,n,i;t=1<=arguments.length?o.call(arguments,0):[];i={};switch(t.length){case 2:i=t[0],e=t[1];break;case 3:if(t[1]instanceof Function){i=t[0],e=t[1],n=t[2]}else{i.login=t[0],i.passcode=t[1],e=t[2]}break;case 4:i.login=t[0],i.passcode=t[1],e=t[2],n=t[3];break;default:i.login=t[0],i.passcode=t[1],e=t[2],n=t[3],i.host=t[4]}return[i,e,n]};r.prototype.connect=function(){var r,s,u,a;r=1<=arguments.length?o.call(arguments,0):[];a=this._parseConnect.apply(this,r);u=a[0],this.connectCallback=a[1],s=a[2];if(typeof this.debug==="function"){this.debug("Opening Web Socket...")}this.ws.onmessage=function(i){return function(r){var o,u,a,c,f,h,l,p,d,g,b,m,v;c=typeof ArrayBuffer!=="undefined"&&r.data instanceof ArrayBuffer?(o=new Uint8Array(r.data),typeof i.debug==="function"?i.debug("--- got data length: "+o.length):void 0,function(){var t,e,n;n=[];for(t=0,e=o.length;t