算是个系列内容吧,最终要实现的是将uniCloud作为后端完成“扫码关注公众号后完成网站登录”
将要涉及的内容可能包括:
0.准备工作(本节)
1.接受并解析xml消息
2.请求access_token并缓存
3.生成带参数二维码
4.引入、封装redis缓存方法
5.开通、配置、初始化uniPush2.0
6.解析不同情况下用户扫码时推送的事件并完成登录
系列内容全部基于uniCloud+vk-uniCloud(云函数路由)+uni-app
编辑器HbuilderX最新版
云空间为阿里云
公众号为认证服务号
好了,终于来到最后也是最重要的一节,这节将把前边全部的内容串起来,最终完成一开始说的扫公众号二维码
、关注公众号
、完成网站登录
。
其实这个流程已经烂大街了,但基于uniCloud来搞,应该还没有人分享过。
一、梳理用户扫码后的事件
官方文档说,未关注的用户
扫码后关注
,推过来的事件是subscribe
,这个确实没错,
但已关注用户
扫码后,实际上是直接进入聊天界面的,这时候推送过来的事件是SCAN
,没错,它是大写的……
二、编写统一入口文件代码
这样,我们就知道,在第零节我们那个index.js入口中,就可以这么写:
// 先把接收到的xml解析出来,详见第一节
let {
ToUserName, // appid
FromUserName, // openid
MsgType, // text,event
EventKey,
} = xmlObj;
// 然后
if(MsgType == 'event'){
let Event = xmlObj.Event;
// 这里来处理逻辑,见下边那大段代码
if(Event == 'SCAN'){
// 已关注用户扫码
}else if(Event == 'subscribe'){
// 未关注用户扫码、且完成关注
}
}
三、完成后端创建用户、token等
这里我把逻辑处理的部分单独拿出来,否则会比较乱
// 判断用户是否存在
let uniUserInfo = await vk.baseDao.findByWhereJson({
dbName: "uni-id-users",
whereJson: {
'wxUserInfo.openid': FromUserName // 扫码用户的openid
}
});
// 查询redisData
// 这里注意一下,存储到redis时,我们写了一个随机数
// 但如果是已关注用户扫码进来时,EventKey是不包含qrscene_的,需要手动拼上去
let EventKey = EventKey.indexOf('qrscene_') !== -1 ? EventKey : 'qrscene_' + EventKey;
// 这里就用上我们第四节封装的方法,来获取创建二维码时缓存到redis的数据
let redisData = await pubFun.getRedisData(EventKey);
// 直接删除redis
// 这里直接清掉,防止被其他用户重复扫码
pubFun.delRedisData(EventKey);
if (vk.pubfn.isNull(redisData )) {
// 没查到redis,说明要么真过期了,要么是被其他用户扫过了
// 这种情况就直接返回一条消息给用户
// 这里用到了第一节中拼装xml消息的方法
msgContent = await pubFun.createXmlTextMsg(FromUserName, '二维码已过期,请刷新~');
res.body = msgContent;
return res;
} else {
// 查询到了redis缓存
// 继续判断是否存在这个用户
// 不存在,则直接在服务端创建一个用户
let uid;
if (vk.pubfn.isNull(uniUserInfo)) {
let currentTime = new Date().getTime();
// 随机生成用户名
let username = 'user_' + vk.pubfn.random(
6,
"abcdefghijklmnopqrstuvwxyz0123456789"
);
// 随机生成6位密码
let password = vk.pubfn.random(6);
// 创建用户
let addUserRes = await uniID.addUser({
username,
password,
authorizedApp: [redisData.clientAppid] // 这里必须给一个,否则无法登录
});
// 新建了用户,赋值
uid = addUserRes.uid;
// 获取微信unionId
// 这个方法之前没说,放到最后来说
let wxUserInfo = await pubFun.getWxUserInfoByToken(FromUserName);
// 用户其他信息
let dataJson = {
wxUserInfo,
status: 0,
};
// 追加用户其他信息
await vk.baseDao.update({
dbName: 'uni-id-users',
whereJson: {
_id: uid
},
dataJson
});
// 这样就完成了一个用户的创建
uniUserInfo = addUserRes;
} else {
// 如果用户已经存在,则直接赋值uid即可
uid = uniUserInfo._id;
}
// 但这时候我们不能把usernam和password返回前端,因为密码是加密过的
// 所以需要生成token并返回
let tokenRes = await uniID.createToken({
uid
});
// 比较奇怪的时候,这个token在生成后,并不会被自动追加到用户表,需要手动追加一下。
// 追加token
await vk.baseDao.update({
dbName: 'uni-id-users',
whereJson: {
_id: uid
},
dataJson: {
token: [tokenRes.token] // 我这里比较粗暴的直接替换掉了
}
});
// 上边一通操作,实际上就是为了在后端完成用户的创建、登录
// 通过uniPush推送给客户端扫码成功的消息
await uniPush.sendMessage({
"push_clientid": redisData.pushClientid, // 这是我们在创建二维码时缓存下来的值
"title": "扫码成功",
"content": "感谢关注,现在进入登录环节",
"payload": {
type: 'login', // 登录消息
tokenRes, // 把token返回给前端
userInfo: uniUserInfo // 用户资料也一起返回
}
})
}
这样,我们回头看上一段代码,就可以这么改:
if(MsgType == 'event'){
let Event = xmlObj.Event;
// 这里写上边那一大段逻辑
if(Event == 'SCAN'){
// 已关注用户扫码
msgContent = '登录成功,欢迎回来~';
}else if(Event == 'subscribe'){
// 未关注用户扫码、且完成关注
msgContent = '感谢关注,登陆成功~';
}
}
// 在公众号中也提醒一下用户
msg= await pubFun.createXmlTextMsg(FromUserName,msgContent );
res.body = msgContent;
return res;
四、前端接受推送消息,完成登录
回到前端,我们在第五节有说过如何监听uniPush的消息。
uni.onPushMessage(res => {
// 这里因为我们只是web端,所以获取的内容实际上是透传的内容,也就是payload字段的内容
let payload = res.data.payload;
if (payload.type == 'barrage') {
// 我其他的处理
} else if (payload.type == 'login') {
// 先把接收到的userInfo缓存下来
vk.vuex.set('$user.userInfo', payload.userInfo);
// 然后注意,token和tokenExpired两个字段,需要用官方的方法去缓存到localStorage
uni.setStorageSync('uni_id_token', payload.tokenRes.token);
uni.setStorageSync('uni_id_token_expired', payload.tokenRes.tokenExpired);
// 缓存完之后,用vk的方法检查一下是否可用
if (!vk.checkToken()) {
vk.toast('登录失败,请重试');
return;
}
// 如果可用,则刷新页面完成登录
// 页面上我也是用vuex来判断是否登录成功的,刷新是为了关闭那个popup
vk.toast('登录成功', 'success', 1500, true, () => {
vk.reLaunch('/pages/index/index');
});
}
});
到这里,所有的环节就都完成了,我们再来回顾一下逻辑。
用户
在前端
点击微信登录
,
前端发起
生成二维码并传参
pushClientId和appid,
后端
接收后先请求access_token
,
然后创建带参数二维码
并返回ticket
、同时把数据缓存到redis
,
前端
接收到ticket后拼接成图片
地址并显示,
用户
使用微信扫码
,
未关注用户关注
后,后端
接收到微信推送的subscribe事件
,
根据openid和scene查询redis,
后端创建用户
和token
,
根据redis的数据,将用户资料和token数据通过uniPush推送
给前端
,
前端
监听到之后完成登录
。
最后的最后,看一下我实际的效果:额,好像不支持上传视频,那就自行体验吧。
https://neverbug.cn