實戰:雲開發-實現奶茶店小程序(二)
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)
}
})
最後
- 想加入我的前端小群的同學,我微信:guzhan321,備註 群
- 喜歡這篇文章的話,請把他分享給有幫助的人
- 有寫錯的或者你不認同的地方,請通過微信告訴我,謝謝
下一篇文章:完成前端頁面,聯調接口