项目搭建
- 快速初始化Express项目
npm i -g express-generator
express demo -e //指定模板引擎ejs
cd demo
npm install
//全局安装 sequelize cli工具
npm install sequelize-cli -g
//安装sequelize 和 mysql2依赖
npm i sequelize mysql2 -S
// 初始化models
sequelize init
/**
* 将会新建以下四个文件夹
* config ---数据库配置文件
* models ---数据库模型
* migrations ---数据库迁移文件
* seeders ---种子文件(测试数据)
*/
- 配置数据库
{
"development": {
"username": "root",
"password": "123456",
"database": "mina_dev",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+8:00"
},
"production": {
"username": "root",
"password": "123456",
"database": "mina_prod",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+8:00"
}
}
//在mysql新建mina_dev和mina_prod
- 目录划分
- 根目录新增了service和utils,主要用来实现结构分层和工具类
- 新建users模型
sequelize model:generate --name User --attributes nickName:string,avatarUrl:string,openid:string,gender:integer,city:string,province:string
修改migrations/xxxx-user.js,添加unique键
...
openid: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
主要逻辑
正式开始主要逻辑代码的编写
- 统一返回值类编写
- utils目录下新建ResultVo.js
*ResultVo.js
class ResultVo{
static success(data){
return {
code: 0,
msg: 'success',
data
}
}
static successNull(){
return {
code: 0,
msg: 'success'
}
}
static fail(code, msg){
return {
code,
msg
}
}
}
module.exports = ResultVo;
- 实现微信开放数据解密
// WXBizDataCrypt.js可在微信开发文档下载,部分Buffer api已过期,做简单修改
var crypto = require('crypto')
function WXBizDataCrypt(appId, sessionKey) {
this.appId = appId
this.sessionKey = sessionKey
}
WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
// base64 decode
var sessionKey = new Buffer.from(this.sessionKey, 'base64')
encryptedData = new Buffer.from(encryptedData, 'base64')
iv = new Buffer.from(iv, 'base64')
try {
// 解密
var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
// 设置自动 padding 为 true,删除填充补位
decipher.setAutoPadding(true)
var decoded = decipher.update(encryptedData, 'binary', 'utf8')
decoded += decipher.final('utf8')
decoded = JSON.parse(decoded)
} catch (err) {
throw new Error('Illegal Buffer',err)
}
if (decoded.watermark.appid !== this.appId) {
throw new Error('Illegal Buffer')
}
return decoded
}
module.exports = WXBizDataCrypt
- 简单封装获取openId的api
// 安装 axios请求微信开发者服务器
npm i axios -S
mkdir GetOpenid.js
const axios = require('axios')
class GetOpenid{
static async requestWeChat(appid,secret,code){
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`
const res = await axios.default.get(url);
return res.data
}
}
module.exports = GetOpenid
- 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
- 用户信息需要微信服务器传回的session_key及前端传入的encrytedData和iv
- 新建config文件,配置小程序信息
config.js
const mina = {
appid: 'xxxxxxxxxx',
secret: 'xxxxxxxxxxxxxxx',
tokenSecret: 'aaaaaa'
}
module.exports = mina;
- 创建UserService.js
- routes及部分逻辑共同充当controller角色, service层单独编写方便复用
const models = require('../models')
const ResultVo = require('../utils/ResultVo')
const GetOpenid = require('../utils/GetOpenid')
const mina = require('../config/mina')
const WXBizDataCrypt = require('../utils/WXBizDataCrypt')
class UserService {
static async save(data) {
//获取openid和session_key
const secretData = await GetOpenid.requestWeChat(mina.appid, mina.secret, data.code);
//数据解密
const pc = new WXBizDataCrypt(mina.appid, secretData.session_key)
const resData = pc.decryptData(data.encryptedData, data.iv)
//查询或创建数据 返回数组[object, boolean]
const res = await models.User.findOrCreate({
where: {
openid: resData.openId
},
attributes: {
exclude: ['openid', 'createdAt', 'updatedAt']
}
});
return ResultVo.success(res[0])
}
}
module.exports = UserService;
- 配置路由
const express = require('express');
const router = express.Router();
const UserService = require('../service/UserService')
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.post('/save', async (req, res) => {
const body = req.body;
const response = await UserService.save(body);
res.json(response)
})
module.exports = router;
至此服务端api编写完成 小程序端比较简单
- user.wxml
<view class="info" wx:if="{{!hasUserInfo && canIUse}}">
<view class="avator">
<image class="avator-image" src="../../assets/images/mine_pic_avator.png" mode="aspectFit"></image>
</view>
<button class="nickName" open-type="getUserInfo" bindgetuserinfo="getUserInfo">点击登录/注册</button>
</view>
- user.js
getUserInfo(e) {
wx.login({
success:(res) => {
if (res.code){
const data = e.detail;
wx.request({
method: 'POST',
url: 'http://127.0.0.1:3000/users/save',
data: {
code: res.code,
encryptedData: data.encryptedData,
iv: data.iv
},
success:(response) => {
if (response.data.code === 0){
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
});
wx.setStorageSync('token', response.data.data.token)
}
}
})
}
}
});
}
写在最后
菜鸟前端学习之路,如果有不合理的地方请大佬指正!