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

Python教主
• 阅读 2609
2020-5-9

文章编号:009/100

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

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

私人公众号:程序员小石
这里有大量的学习资料,免费分享给你


正文

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

本文重点内容

  • 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. 有写错的或者你不认同的地方,请通过微信告诉我,谢谢

下一篇文章:完成前端页面,联调接口
实战:云开发·实现奶茶店小程序(二)

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
3年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Stella981 Stella981
3年前
Hibernate纯sql查询结果和该sql在数据库直接查询结果不一致
问题:今天在做一个查询的时候发现一个问题,我先在数据库实现了我需要的sql,然后我在代码中代码:selectdistinctd.id,d.name,COALESCE(c.count_num,0),COALESCE(c.count_fix,0),COALESCE(c
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这