背景
接运营要求,希望记录文章被某个人分享出去之后的路径树,但因为微信分享时是无法获取到用户信息的,所以能记录的信息只有原始分享用户以及他分享出去后的路径树。
实现方法
当文章被分享出去时,我们的URL里记录有文章ID及用户ID,当页面被打开时,后端会生成一个share_id,以query的方式加到当前URL的后面,当页面被再次分享出去时,后端会根据这个share_id再生成一个下级的share_id,并记录他们之间的关系,而之前的share_id在数据库里的记录会被更新为已读。
微信签名生成规则
签名的生成需要以下四个参数
- jsapi_ticket 先通过appId获取access_token,在通过access_token获取jsapi_ticket
- noncestr 随机字符串
- timestamp 时间戳(秒级)
- url 当前页面URL,是当前页面URL,而不是要分享出去的URL
最后形成一段与此类似的字符串
1
| jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
|
在经过sha1加密,得到signature
数据模型
1 2 3 4 5 6 7 8 9
| var WeixinShareLogSchema = new Schema({ _id: {type: String, unique: true, required: true}, parent_id: {type: String, ref: 'weixin_share_log'}, article_id: {type: ObjectId, ref: 'article', required: true}, is_read: {type: Boolean, default: false}, created_by : {type : String, required: true}, created_at : {type : Date, default : Date.now} }); 通过这种结构,我们只要搜索created_by再加上前端的异步实现就能知道被该用户分享出去的文章的树状结构了。
|
后端实现
因项目历史原因,没有使用es6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| ......
var parent_id = this.request.query.share_id || '';
var share_id = md5(parent_id + Date.now()); var WeixinShareLogModel = mongoose.model('weixinsharelog');
var e_weixin_share_log = yield WeixinShareLogModel.findOne({_id: parent_id}).exec(); if (e_weixin_share_log) { e_weixin_share_log.is_read = true; yield e_weixin_share_log.save(); } e_weixin_share_log = new WeixinShareLogModel({ _id: share_id, parent_id: parent_id, article_id: article_id, created_by: user_id }); yield e_weixin_share_log.save();
var url = Tools.format('{protocol}://{host}{url}', { protocol: config.protocol, host: config.host, url: this.url.split('#').shift() });
var sign = yield Tools.getWeixinSignature(url);
var share_url = Tools.format('{url}?share_id={share_id}', { url: url.split('?').shift(), share_id: share_id });
|
获取微信签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| * 获取微信签名 */ var getWeixinSignature = function* (url) { function* getWeixinTicket() { var data = yield request.get(format(config.weixin.path.access_token, config.weixin)); return yield request.get(format(config.weixin.path.ticket, {access_token: data.access_token})); } try { var data, expire, nonce_str, timestamp, jsapi_ticket, signature; if (!getWeixinSignature.expire || getWeixinSignature.expire > new Date() - 200 * 1000) { data = yield getWeixinTicket(); getWeixinSignature.expire = data.expires_in * 1000 + new Date().getTime(); getWeixinSignature.jsapi_ticket = data.ticket; } if (!getWeixinSignature.jsapi_ticket) return false; nonce_str = randomString(16); timestamp = Math.floor(new Date().getTime() / 1000); signature = sha1(format("jsapi_ticket={jsapi_ticket}&noncestr={nonce_str}×tamp={timestamp}&url={url}", { jsapi_ticket: getWeixinSignature.jsapi_ticket, nonce_str: nonce_str, timestamp: timestamp, url: url })); return { appId: config.weixin.app_id, timestamp: timestamp, nonceStr: nonce_str, signature: signature } } catch (e) { console.log(e.stack); return false; } };
|
前端配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| wx.config({ debug: false, appId: '<%= sign.appId %>', nonceStr: '<%= sign.nonceStr %>', timestamp: '<%= sign.timestamp %>', signature: '<%= sign.signature %>', jsApiList: [ "onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareWeibo", "hideOptionMenu", "showOptionMenu" ] }); wx.ready(function() { var desc = $('meta[name=description]').attr('content') || $('body').find('h1,h2,h3,h4,p').eq(0).text() || '美丽阅读文章分享'; var image_url = $('body').find('img').get(0).src; var shareInfo = { title: '<%= article_title %>', desc: desc, imgUrl: image_url, link: '<%= share_url %>' }; wx.onMenuShareTimeline(shareInfo); wx.onMenuShareAppMessage(shareInfo); wx.onMenuShareQQ(shareInfo); wx.onMenuShareWeibo(shareInfo); });
|
具体JSSDK配置请参考微信官方文档