一、前言
小程序·云开发是微信团队联结腾讯云推出的业余的小程序开发服务。
开发者能够应用云开发疾速开发小程序、小游戏、公众号网页等,并且原生买通微信凋谢能力。
开发者无需搭建服务器,可免鉴权间接应用平台提供的 API 进行业务开发。
数据库的上手、初始化等可参看官网链接:小程序·云开发
二、应用教训
间接应用云开发 API
场景:页面或办法的逻辑简略,关联一个数据库,无联表查问
例子:
db.collection('todos').doc('todo-identifiant-aleatoire').get({success: function(res) {
// res.data 蕴含该记录的数据
console.log(res.data)
}
})
应用数据聚合能力
场景:页面或办法的逻辑中等,关联多个数据库,可能存在联表查问或数据处理
例子:
const db = wx.cloud.database()
const $ = db.command.aggregate
db.collection('books').aggregate()
.group({
// 按 category 字段分组
_id: '$category',
// 让输入的每组记录有一个 avgSales 字段,其值是组内所有记录的 sales 字段的平均值
avgSales: $.avg('$sales')
})
.end()
借助 promise,async 等
场景:页面或办法的逻辑较为简单,关联多个数据库,可能存在屡次查问以及云函数或 https 申请
以下是对云开发 CMS 导出数据的扩大案例
其中整合了上述的几种形式
例子:
const cloud = require('wx-server-sdk')
cloud.init({env: cloud.DYNAMIC_CURRENT_ENV})
var xlsx = require('node-xlsx');
const db = cloud.database();
const MAX_LIMIT = 100;
const _ = db.command;
exports.main = async (event, context) => {console.log(event)
event.queryStringParameters = event.queryStringParameters||{};
const collection = event.collection || event.queryStringParameters.collection;
const params = event.params || event.queryStringParameters.params || {};
// const acceptType = ["String", "Tel", "Array", "Number", "Connect", "Boolean", "Enum", "Date", "DateTime"]; //"File","Image"
const unacceptType = ["File", "Image"];
const schemasRes = await db.collection("tcb-ext-cms-schemas").where({collectionName: collection}).get();
const schemas = schemasRes.data[0];
let connectList = [];
const title = event.title || event.queryStringParameters.title || schemas.displayName || "数据";
// 先取出汇合记录总数
const countRes = await db.collection(collection).where(params).count();
const fields = schemas.fields.filter(function (schemas) {return unacceptType.indexOf(schemas.type) == -1 && (!schemas.isHidden);
});
const connectResourcenList = [];
fields.forEach(field => {if (field.type == "Connect") {connectList.push(field);
connectResourcenList.push(field.connectResource)
}
});
const schemasListRes = await db.collection("tcb-ext-cms-schemas").where({_id: _.in(connectResourcenList)
}).limit(MAX_LIMIT).get();
const schemasList = schemasListRes.data || [];
// console.log("fields==============================")
console.log(schemasList)
const total = countRes.total
// 计算需分几次取
const batchTimes = Math.ceil(total / MAX_LIMIT)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {//console.log(connectList.length)
if (connectList.length > 0) {let lookupList = [];
connectList.forEach(connect => {const connectschemas = schemasList.filter(function (schemas) {return schemas._id == connect.connectResource;})[0];
lookupList.push({
from: connectschemas.collectionName,
localField: connect.name,
foreignField: '_id',
as: "connect" + connect.name
})
});
let aggregate = db.collection(collection).aggregate().match(params).skip(i * MAX_LIMIT).limit(MAX_LIMIT);
for (let index = 0; index < connectList.length; index++) {aggregate = aggregate.lookup(lookupList[index]);
}
aggregate = aggregate.end();
tasks.push(aggregate)
} else {
const promise = db.collection(collection).where(params).skip(i * MAX_LIMIT).limit(MAX_LIMIT).get();
tasks.push(promise)
}
}
console.log(tasks)
// 期待所有
let recordRes = (await Promise.all(tasks)).reduce((acc, cur) => {
return {list: (acc.list || []).concat(cur.list || []),
data: (acc.data || []).concat(cur.data || []),
}
})
let records = (recordRes.list || []).concat(recordRes.data || []) || [];
//1. 定义表格名
let dataCVS = title + '.xlsx';
let excelData = [];
let row = [];
fields.forEach(field => {row.push(field.displayName)
});
excelData.push(row);
records.forEach(record => {let arr = [];
fields.forEach(field => {if (!record.hasOwnProperty(field.name)) {arr.push("")
} else {switch (field.type) {
case "Connect":
arr.push(join2Str(record["connect" + field.name], field.connectField))
break;
case "DateTime":
arr.push(formatDateTime(record[field.name]))
break;
case "Date":
arr.push(formatDate(record[field.name]))
break;
case "Boolean":
arr.push(record[field.name] ? "是" : "否")
break;
case "Enum":
let enumElements = field.enumElements;
let enumElement= enumElements.find(function(item){return item.value = record[field.name];
})
arr.push(enumElement.label)
break;
default:
arr.push(record[field.name])
break;
}
}
});
excelData.push(arr);
});
//3,把数据保留到 excel 里
var buffer = await xlsx.build([{
name: title,
data: excelData
}]);
//4,把 excel 文件保留到云存储里
const excelFileIdRes = await cloud.uploadFile({
cloudPath: dataCVS,
fileContent: buffer, //excel 二进制文件
});
return await cloud.getTempFileURL({fileList: [excelFileIdRes.fileID]
}).then(function (res) {return res.fileList[0].tempFileURL
})
}
function join2Str(obj, fieldName) {if (Object.prototype.toString.call(obj) == "[object Array]") {let resultArr = [];
obj.forEach(item => {if (item.hasOwnProperty(fieldName))
resultArr.push(item[fieldName])
});
return resultArr.join(",")
} else {if (obj.hasOwnProperty(fieldName))
return obj[fieldName]
}
}
function formatDateTime(inputTime) {var date = new Date(inputTime);
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
var h = date.getHours();
h = h < 10 ? ('0' + h) : h;
var minute = date.getMinutes();
var second = date.getSeconds();
minute = minute < 10 ? ('0' + minute) : minute;
second = second < 10 ? ('0' + second) : second;
return y + '-' + m + '-' + d + '' + h +':'+ minute +':' + second;
};
function formatDate(inputTime) {var date = new Date(inputTime);
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
return y + '-' + m + '-' + d;
};
整合数据库框架
场景:小程序或 APP 的业务逻辑简单,模板页面的开发,组件的开发和对立异样解决
例子:
以下例子援用了 wxboot 的小程序框架
//app.js
// const {WXBoot} = require('wxbootstart');
require('./lib-webpack/wxboot');
import login from "./login/login"
import utils from "./utils/utils"
import constants from "./constants/constants"
App.A({
config: {
initCloud:{
// env: '',
traceUser: true,},
route: '/pages/$page/$page',
pageApi: utils,
consts: constants,
updata:{
arrObjPath:false,
arrCover:false
},
mixins:[login,App.A.Options] ,
},
getOpenidFunc: function(){
return this.cloud.callFunction({name:"getWXContext"}).then(res=>{return res.result.openid;}).catch(err=>{console.error(err)
return ""
})
},
onLaunch: function (opts) {
App.A.on('some_message', function (msg) {console.log('Receive message:', msg)
})
console.log('APP is Running', opts)
},
store: {id: 0},
auth:{canUseXXX:false},
globalData: {
version: "v1.0.0",
id: 0,
userInfo: null,
addressInfo: null,
sessionKey: null,
loginTime: 0,
openid: "",
theme: {color: "#FFFFFF"},
share: {
title: "开启一天好运",
imageUrl: "https://XXX.jpg",
path: "/pages/index/index"
},
settings: null
},
onAwake: function (time) {console.log('onAwake, after', time, 'ms')
},
onShow: function () {console.log('App onShow')
},
/* 小程序被动更新
*/
updateManager() {if (!wx.canIUse('getUpdateManager')) {return false;}
const updateManager = wx.getUpdateManager();
updateManager.onCheckForUpdate(function (res) {});
updateManager.onUpdateReady(function () {
wx.showModal({
title: '有新版本',
content: '新版本曾经筹备好,行将重启',
showCancel: false,
success(res) {if (res.confirm) {updateManager.applyUpdate()
}
}
});
});
updateManager.onUpdateFailed(function () {
wx.showModal({
title: '更新提醒',
content: '新版本下载失败',
showCancel: false
})
});
},
"navigateToMiniProgramAppIdList": [
"wx8abaf00ee8c3202e"
]
})
全局封装增删改,咱们更专一的关注于业务逻辑,对立异样解决
module.exports = {
$callFun: callFunction,
$add: add,
$get: get,
$update: update,
$remove: remove,
$count:count
}
// 取数据库实例。一个数据库对应一个实例
/**
* 封装查问操作
* 增 查 改 删
*
*/
// 增
async function add(collectionName, data, openParse = false) {if (openParse) {data = await parseQuery(data, this)
}
return this.$collection(collectionName).add({data}).then(res => {return res._id}).catch(res => {return ""})
}
// 查问
// 对应 id 取不到的时候,返回 {}
async function get(collectionName, query, openParse = false) {switch (type(query)) {
case "string":
return this.$collection(collectionName).doc(query).get().then(res => {return res.data}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return {}})
case "object":
const defaultOptions = {
where: null,
order: null,
skip: 0,
limit: 20,
field: null,
pageIndex: 1
}
const parsequery = setDefaultOptions(query, defaultOptions);
let {where, order, skip, limit, field, pageIndex} = parsequery;
let collectionGet = this.$collection(collectionName);
if (where != null) {if (openParse) {where = await parseQuery(where, this)
}
collectionGet = collectionGet.where(where)
}
if (order != null) {if (type(order) == "object") {collectionGet = collectionGet.orderBy(order.name, order.value);
}
if (type(order) == "array") {
order.forEach(orderItem => {collectionGet = collectionGet.orderBy(orderItem.name, orderItem.value);
});
}
}
if (field) {collectionGet = collectionGet.field(field);
}
if (pageIndex > 1) {collectionGet = collectionGet.skip((pageIndex - 1) * limit).limit(limit);
} else {collectionGet = collectionGet.skip(skip).limit(limit);
}
return collectionGet.get().then(res => {return res.data}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return []})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return null;
}
}
async function count(collectionName, query, openParse = false) {switch (type(query)) {
case "object":
let collectionUpdate = this.$collection(collectionName);
if (openParse) {query = await parseQuery(query, this)
}
collectionUpdate = collectionUpdate.where(query)
return collectionUpdate.count().then(res => {return res.total}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
default:
return this.$collection(collectionName).count().then(res => {return res.total}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
}
}
// 批改
async function update(collectionName, query, updata, openParse = false) {switch (type(query)) {
case "string":
return this.$collection(collectionName).doc(query).update({data: updata}).then(res => {return res.stats.updated}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return 0
})
case "object":
let collectionUpdate = this.$collection(collectionName);
if (openParse) {query = await parseQuery(query, this)
}
collectionUpdate = collectionUpdate.where(query)
return collectionUpdate.update({data: updata}).then(res => {return res.stats.updated}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return 0
}
}
// 删除
async function remove(collectionName, query, openParse=false) {switch (type(query)) {
case "string":
return this.$collection(collectionName).doc(query).remove().then(res => {return res}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return {}})
case "object":
let collectionRemove = this.$collection(collectionName);
if (openParse) {query = await parseQuery(query, this)
}
collectionRemove = collectionRemove.where(query)
return collectionRemove.remove().then(res => {return res}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return []})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return 0
}
}
function setDefaultOptions(options = {}, defaultOptions = {}) {return Object.assign(defaultOptions, options);
}
function promisify(api) {return (options, ...query) => {return new Promise((resolve, reject) => {api(Object.assign({}, options, {
success: resolve,
fail: reject
}), ...query);
})
}
}
async function callFunction(options) {return await this.cloud.callFunction(options)
}
var undef = void(0)
function type(obj) {if (obj === null) return 'null'
else if (obj === undef) return 'undefined'
var m = /\[object (\w+)\]/.exec(Object.prototype.toString.call(obj))
return m ? m[1].toLowerCase() : ''}
async function parseQuery(query, self) {let queryStr = JSON.stringify(query);
if (queryStr.indexOf("{openid}") > -1) {let openid = await self.$getOpenid();
return JSON.parse(queryStr.replace(/{openid}/g, openid));
} else {return query}
}
高级用法,联合云函数和 https 以及封装 api , 实现对立对外接口,对接其余语言
场景:多我的项目,多后盾,多端买通,数据迁徙等
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({env: cloud.DYNAMIC_CURRENT_ENV});
const db = cloud.database();
// 云函数入口函数
exports.main = async (event, context) => {
let body = event.body;
let cloudParams = urlToObj(decodeURIComponent(body));
let {
cloudType,
collectionName
} = cloudParams;
let data = JSON.parse(cloudParams.data || "{}");
let query = JSON.parse(cloudParams.query || "{}");
if(type(query)=="object"){query.where = JSON.parse(query.where ||"{}" );
if(query.field)
query.field = JSON.parse(query.field ||"{}" );
}
console.log(query)
let promise = null;
switch (cloudType) {
case "ADD":
promise = add(collectionName, data);
break;
case "GET":
promise = get(collectionName, query)
break;
case "UPDATE":
promise = update(collectionName, query, data)
break;
case "REMOVE":
promise = remove(collectionName, query)
break;
case "COUNT":
let countquery = null;
if (type(query) == "string") {countquery = query} else {countquery = query.where || null}
promise = count(collectionName, countquery)
break;
default:
break;
}
return promise;
}
function urlToObj(str) {var obj = {};
var arr2 = str.split("&");
for (var i = 0; i < arr2.length; i++) {var res = arr2[i].split("=");
obj[res[0]] = res[1] || "";
}
return obj;
}
// 增
async function add(collectionName, data, openParse = false) {if (openParse) {data = await parseQuery(data)
}
return db.collection(collectionName).add({data}).then(res => {
return res._ids || res._id;
}).catch(res => {return ""})
}
// 查问
// 对应 id 取不到的时候,返回 {}
async function get(collectionName, query, openParse = false) {if (query.limit && query.limit == "all") {
let countquery = null;
if (type(query) == "string") {countquery = query} else {countquery = query.where || null}
// 先取出汇合记录总数
const total = await count(collectionName, countquery);
// 计算需分几次取
const batchTimes = Math.ceil(total / 20)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {
query.limit = 20;
query.pageIndex = i + 1;
const promise = get(collectionName, query);
tasks.push(promise)
}
// 期待所有
return (await Promise.all(tasks)).reduce((acc, cur) => {acc = acc || [];
cur = cur || [];
return acc.concat(cur);
})
}
switch (type(query)) {
case "string":
return db.collection(collectionName).doc(query).get().then(res => {return res.data}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return {}})
case "object":
const defaultOptions = {
where: null,
order: null,
skip: 0,
limit: 20,
field: null,
pageIndex: 1
}
const parsequery = setDefaultOptions(query, defaultOptions);
let {where, order, skip, limit, field, pageIndex} = parsequery;
let collectionGet = db.collection(collectionName);
if (where != null) {if (openParse) {where = await parseQuery(where)
}
collectionGet = collectionGet.where(where)
}
if (order != null) {if (type(order) == "object") {collectionGet = collectionGet.orderBy(order.name, order.value);
}
if (type(order) == "array") {
order.forEach(orderItem => {collectionGet = collectionGet.orderBy(orderItem.name, orderItem.value);
});
}
}
if (field) {collectionGet = collectionGet.field(field);
}
if (pageIndex > 1) {collectionGet = collectionGet.skip((pageIndex - 1) * limit).limit(limit);
} else {collectionGet = collectionGet.skip(skip).limit(limit);
}
return collectionGet.get().then(res => {return res.data}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return []})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return null;
}
}
async function count(collectionName, query, openParse = false) {switch (type(query)) {
case "object":
let collectionUpdate = db.collection(collectionName);
if (openParse) {query = await parseQuery(query)
}
collectionUpdate = collectionUpdate.where(query)
return collectionUpdate.count().then(res => {return res.total}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
default:
return db.collection(collectionName).count().then(res => {return res.total}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
}
}
// 批改
async function update(collectionName, query, updata, openParse = false) {switch (type(query)) {
case "string":
return db.collection(collectionName).doc(query).update({data: updata}).then(res => {return res.stats.updated}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return 0
})
case "object":
let collectionUpdate = db.collection(collectionName);
if (openParse) {query = await parseQuery(query)
}
collectionUpdate = collectionUpdate.where(query)
return collectionUpdate.update({data: updata}).then(res => {return res.stats.updated}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return 0
})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return 0
}
}
// 删除
async function remove(collectionName, query, openParse = false) {switch (type(query)) {
case "string":
return db.collection(collectionName).doc(query).remove().then(res => {return res}).catch(res => {console.warn(`"collection":"${collectionName}","_id":"${query}" 不存在 `)
return {}})
case "object":
let collectionRemove = db.collection(collectionName);
if (openParse) {query = await parseQuery(query)
}
collectionRemove = collectionRemove.where(query)
return collectionRemove.remove().then(res => {return res}).catch(res => {console.warn(`"collection":"${collectionName}" 不存在 `)
return []})
default:
console.warn(`"query": 参数类型谬误不存在 `)
return 0
}
}
function setDefaultOptions(options = {}, defaultOptions = {}) {return Object.assign(defaultOptions, options);
}
function promisify(api) {return (options, ...query) => {return new Promise((resolve, reject) => {api(Object.assign({}, options, {
success: resolve,
fail: reject
}), ...query);
})
}
}
var undef = void(0)
function type(obj) {if (obj === null) return 'null'
else if (obj === undef) return 'undefined'
var m = /\[object (\w+)\]/.exec(Object.prototype.toString.call(obj))
return m ? m[1].toLowerCase() : ''}
async function parseQuery(query) {let queryStr = JSON.stringify(query);
if (queryStr.indexOf("{openid}") > -1) {let openid = cloud.getWXContext().OPENID;
return JSON.parse(queryStr.replace(/{openid}/g, openid));
} else {return query}
}
三、倡议
- 云开发是次要是相似 mongdb 的非关系数据库,能够保留 json 的数据,咱们能够多间接保留简单的值
- 尝试应用本人封装的业务逻辑来全局管制异样等
- 数据库的权限、索引等能够对数据库检索性能进一步优化
产品介绍
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、主动弹性扩缩的后端云服务,蕴含计算、存储、托管等 serverless 化能力,可用于云端一体化开发多种端利用(小程序,公众号,Web 利用,Flutter 客户端等),帮忙开发者对立构建和治理后端服务和云资源,防止了利用开发过程中繁琐的服务器搭建及运维,开发者能够专一于业务逻辑的实现,开发门槛更低,效率更高。
开明云开发:https://console.cloud.tencent.com/tcb?tdl_anchor=techsite
产品文档:https://cloud.tencent.com/product/tcb?from=12763
技术文档:https://cloudbase.net?from=10004
技术交换群、最新资讯关注微信公众号【腾讯云云开发】