在專案中使用egg 的總結(持續更新文)
阿新 • • 發佈:2020-07-18
使用 egg 實戰微信小程式後端
微信小程式授權登入流程
官方圖示
我的理解
node 的實現
class AuthController extends BaseController { /** * 登入憑證校驗 * @param {number} appid 小程式appid 必傳 * @param {number} code 使用者登入憑證(有效期五分鐘)必傳 * @param {string} encryptedData 包含unionId的微信使用者資訊 非必傳 * @param {string} iv 加密演算法的初始向量 -必傳 * @param {number} timestamp 時間戳 非必傳 * */ async code2session() { const { ctx } = this; const { appid, code } = ctx.request.body; const { secret } = this.mpInfo; const { service } = ctx; const { MicroAppUser } = ctx.modelMa; try { ctx.validate({ appid: 'string', code: 'string' }) } catch (err) { ctx.throw(400, err) } try { // 微信code2Session const wxC2sRes = await this._wx_code2Session(appid, secret, code) // 生產sid const sid = await this._createSessionId() const unionid = wxC2sRes.unionid; const openid = wxC2sRes.openid; const wxUser = await this._getWxUserForDb(openid, appid) || ''; const created = await MicroAppUser.findOrCreate({ defaults: { sid, sessionKey: wxC2sRes.session_key, openid, unionid, uid }, where: { appid, openid } }).spread((row, created) => { return created }) if (!created) { // 更新sid await MicroAppUser.update({ sid, sessionKey: wxC2sRes.session_key, openid, unionid, uid }, { where: { appid, openid } }) } if (wxUser) wxUser.session_key = wxC2sRes.session_key; ctx.body = { code: 200, data: { sid, openid: wxC2sRes.openid, ypUser, wxUser } } } catch (err) { ctx.throw(500, err) } } async code2sessionAndDecode () { const { ctx } = this; const { appid, code, encryptedData, iv,} = ctx.request.body; const { secret } = this.mpInfo; const { service, helper } = ctx; const { MicroAppUser } = ctx.modelMa; try { ctx.validate({ appid: 'string', code: 'string' }) } catch (err) { ctx.throw(400, err) } try { if (!iv) { return ctx.body = { code: 400, msg: `缺少必要引數vi` } } const wxC2sRes = await this._wx_code2Session(appid, secret, code) let sessionKey = wxC2sRes.session_key if (!sessionKey) { ctx.throw(500, 'sessionKey is not found') } // 生產sid const sid = await this._createSessionId() // 解碼unionid獲取微信使用者資訊 const wxUser = await helper.encodeWxEncryptedData(appid, sessionKey, encryptedData, iv); const openid = wxUser.openId; const unionId = wxUser.unionId; const uid = unionId ? await service.rap.dubbox.getUserIdByUnionId(unionId) : 0; const ypUser = uid ? await service.rap.dubbox.getUserInfoById(uid) : ''; if (ypUser) { ypUser.role = await this._getCrewRole(ypUser.uid) } const created = await MicroAppUser.findOrCreate({ defaults: { sid, sessionKey, openid, unionid: unionId, uid }, where: { appid, openid } }).spread((row, created) => { return created }) if (!created) { // 更新sid await MicroAppUser.update({ sid, sessionKey, openid, unionid: unionId, uid }, { where: { appid, openid } }) } ctx.body = { code: 200, data: { sid, ypUser, wxUser } } } catch (err) { ctx.throw(500, err) } } // 儲存微信使用者資訊 async saveUserInfo() { const { ctx, app } = this; const body = ctx.request.body; const { MicroAppUser } = app.modelMa; try { body.openid = ctx.openid || body.openId || body.openid body.nickname=body.nickname || body.nickName; const defaults = { appid: body.appid, openid: body.openid, unionid: body.unionid || body.unionId, sessionKey: body.session_key, nickname: body.nickname, gender: body.gender, avatarUrl: body.avatarUrl, country: body.country, province: body.province, city: body.city, language: body.language, uid: body.uid } this.logger.info(`saveUserInfo>>>${defaults}`) let result = await MicroAppUser.findOrCreate({ raw: true, defaults, where: { openid: body.openid, appid: body.appid } }) if (!result.created) { result = await MicroAppUser.update(defaults, { where: { openid: body.openid, appid: body.appid } }) } return ctx.body = { code: 200, data: result, msg: '操作成功' } } catch (err) { ctx.throw(500, err) } } // 從DB獲取微信使用者資訊 async _getWxUserForDb(openid, appid) { return await this.app.mysql.get('microApp').select('micro_app_user', { where: { openid, appid } }).then(rows => { return rows = rows[0] }) } // 建立sessionid async _createSessionId () { const { ctx } = this; const { appid, timestamp, uid, sid } = ctx.request.body; var str = '' if (appid) str += appid if (timestamp) str += timestamp if (sid) str += sid if (uid) str += uid return md5(str) } // 微信-登入憑證校驗 async _wx_code2Session(appid, secret, code) { try { const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`; const data = await this.ctx.curl(url, { method: 'get', contentType: 'json', timeout: 10000, dataType: 'json', }) return data.data } catch (err) { this.ctx.throw(err) } } }
利用 redis 優化註冊介面
redis 是實際上是利用記憶體在執行,所以為了防止使用者註冊兩次時間間隔太短
- 存入 redis
- 從 redis 讀取值存入資料庫
- 刪除 redis 中的值
async register() { const { ctx, app, service } = this const { GlUser } = ctx.activityModel ctx.UID = await this.checkUid() // 插入到「我的」活動表 let aid = this.actSetting.aid if (aid) { const result = await ctx.service.activity.activity.regActivity( ctx.UID, aid ) } try { const userInfo = await ctx.service.activity.user.getUserInfoById(ctx.UID) // 如果頭像來源是又拍雲,則將頭像縮放至 200*200 輸出, 並改為 https 協議 if (userInfo.avatar && userInfo.avatar.indexOf('upyun') > -1) { let index = userInfo.avatar.indexOf('://') userInfo.avatar = `https${userInfo.avatar.slice( index, userInfo.avatar.length )}!/sq/200` } const data = { uid: ctx.UID, nickname: userInfo.nickname, avatar: userInfo.avatar, } // redis是實際上是利用記憶體在執行,所以為了防止使用者註冊兩次時間間隔太短 // 1. 存入redis 2. 從redis讀取值存入資料庫 3. 刪除redis中的值 await service.redis.setRedis(this.redisKey + ctx.UID, data, this.settings.redisTime); const userData = await service.redis.getRedis(this.redisKey + ctx.UID); let result = await GlUser.findOrCreate({ where: { uid: ctx.UID, }, defaults: userData, }).spread((user, created) => { return user }) ctx.body = { code: 200, data: result, msg: 'success', } await service.redis.destroyRedis(this.redisKey + ctx.UID); } catch (error) { ctx.throw(500, error) } }
參考: