实战:云开发-实现奶茶店小程序(二)

2020-5-9

文章编号:009/100

以前很少写文章。从今天开始我要挑战一下自己,连续输出100篇技术类文章。这100篇文章我尽量以实战案例为主。

如果你觉得本文还不错,记得关注或者给个 star,你们的赞和 star 是我编写更多更精彩文章的动力!
GitHub 地址

私人公众号:程序员小石

这里有大量的学习资料,免费分享给你


正文

上一篇文章简单分析了“奶茶店·小程序”,现在我们先来实现接口和数据库。

  • 第一篇:业务逻辑拆分,敲定设计稿,设计 API 和数据库
  • 第二篇:完成接口开发,测试接口
  • 第三篇:完成前端页面,联调接口

本文重点内容

  • Taro 构建小程序
  • 云函数设计
  • 云函数 + 云数据库实现:队列推送

云函数

技术图片

Taro 构建小程序

windows 系统要安装 python,Nodejs版本要 >=8.0.0

尽量使用Taro 最新版,微信更新的很快。Taro 也会及时跟进

我目前的Taro 版本是 v2.2.3

构建项目
技术图片

云函数设计

一般一个云函数负责一个模块,比如 Tea, 只负责 Tea 的 CURD 操作。

我的云函数需要两个字段 action 和 params。

其中 action 标记动作,params 是参数。这样设计云函数能提高可扩展性。

// 云函数入口文件const cloud = require(‘wx-server-sdk‘)const method = require(‘./method‘);cloud.init({ env: ‘xxx‘})const db = cloud.database();exports.db = db// 云函数入口函数exports.main = async (event, context) => { // 接受两个参数 const { action, params } = event let res = {} switch(action) { case ‘create‘: // 增 res = await method.create(params); break; case ‘del‘:// 删 res = await method.del(params); break; case ‘update‘:// 改 res = await method.update(params); break; case ‘select‘:// 查 res = await method.select(params); break; } return res}

前端代码

// 新增let res = await Taro.cloud.callFunction({ name: ‘tea‘, data: { action: ‘create‘, params: { name: ‘红茶玛奇朵‘, price: ‘18.00‘, description: ‘红茶与奶油的美妙结合....‘, imgs: [...], selects: [...] } }})// 删除let res = await Taro.cloud.callFunction({ name: ‘tea‘, data: { action: ‘del‘, params: { ‘_id‘: ‘xxx‘ } }})

这样实现代码可读性强,容易扩展。

其他的云函数我就不一一列举了,大部分都是增删改查的操作。 代码传送门

云函数 + 云数据库实现:队列推送

排队功能是刚需,必须要求实时更新。云开发实现实时排队功能需要三方配合

  • 数据库
  • 云函数
  • 前端监听(调用数据库的 .watch 功能)

数据库设计

把整个队伍整理到一条数据中,每次执行修改操作。这样会降低复杂度

// collection:Queue// 表结构,描述某一天的排队情况{ _id: "", createDate: "2020-5-10", // 以天为key list: [ { // 每一个排队的人 beforeIndex: 0, createTime: Sun May 10 2020 15:04:29 GMT+0800 (中国标准时间), user, order, ... }, { beforeIndex: 1, createTime: Sun May 10 2020 15:04:29 GMT+0800 (中国标准时间) user, order, ... }, { beforeIndex: 2, createTime: Sun May 10 2020 15:04:29 GMT+0800 (中国标准时间) user, order, ... }, ]}

云函数设计

队列分为两个动作,入队 enqueue,出队 dequeue。

入队时要区分当天是否有队列,没有队列则新增一条数据。有则修改此条数据

入队过程:

锁队列 -> 查询今天的队列,如果没有则初始化队列 -> 入队 -> 同步到数据库 -> 解锁队列

出队过程:

锁队列 -> 查到队列 -> 出队 -> 同步到数据库 -> 解锁队列

由于nodejs是单线程的,我们可以在函数的外部实现一个简单的队列锁

// 队列锁const queueLock = () => { let lock = true return { get: () => lock, set: (v) => { lock = v ? true : false } }}const lockFn = queueLock()lockFn.get() // 队列状态lockFn.set(false) // 锁定队列lockFn.set(true) // 解锁队列
// enqueue 入队操作const enqueue = async (params) => { let res = { success: true, errorCode: ‘-1‘, msg: ‘‘, data: null } try { while(1) { if (lockFn.get() === true) { // 1. 入队时, 加锁队列 lockFn.set(false) let queue = null let date = moment().format(‘YYYY-M-D‘) let res = await main.db.collection(collName).where({ currentDate: date }) .get() if (res.data.length === 0) { // 新增队列 queue = QueueFn(date) } else { // 入队 queue = res.data[0]; } params.beforeIndex = queue.list.length; // 等位人数 params.createTime = new Date(); queue.list.push(params); if (queue._id) { let newQueue = { ...queue } delete newQueue[‘_id‘]; await main.db.collection(collName) .doc(queue._id) .set({ data: { ...newQueue } }) } else { await main.db.collection(collName).add({ data: queue }) } lockFn.set(true); break; } // 轮询减速 await sleep(150) } } catch (error) { res.msg = error res.errorCode = ‘1010‘ res.msg = error lockFn.set(true) } return res}// dequeue 出队操作const dequeue = async (params) => { let res = { success: true, errorCode: ‘-1‘, msg: ‘‘, data: null } try { while(1) { if (lockFn.get() === true) { // 锁队列 lockFn.set(false) let queue = {} // 出队 let date = moment().format(‘YYYY-M-D‘) let res = await main.db.collection(collName).where({ currentDate: date }) .get() if (res.data.length > 0) { queue = res.data[0] queue.list.shift() // 重置 beforeIndex queue.list = queue.list.map((item, i) => { item.beforeIndex = i return item }) } let newQueue = {...queue} delete newQueue[‘_id‘] await main.db.collection(collName) .doc(queue._id) .set({ data: { ...newQueue } }) lockFn.set(true) break; } } } catch (error) { res.msg = error res.errorCode = ‘1010‘ res.msg = error } return res}

小程序端代码

const db = wx.cloud.database()// 队列监听watcher = db.collection(‘Queue‘) .orderBy(‘currentDate‘, ‘asc‘) .where({ currentDate: moment().format(‘YYYY-M-D‘) }) .limit(1) .watch({ onChange: function(snapshot) { console.log(‘完整队列‘, snapshot.docs) }, onError: function(err) { console.error(‘the watch closed because of error‘, err) } })

所有的云函数代码,在这里 -> GitHub

最后

  1. 想加入我的前端小群的同学,我微信:guzhan321,备注 群
  2. 喜欢这篇文章的话,请把他分享给有帮助的人
  3. 有写错的或者你不认同的地方,请通过微信告诉我,谢谢

下一篇文章:完成前端页面,联调接口

技术图片

相关文章