小程序·云开发是微信团队联合腾讯云推出的专业的小程序开发服务。
开发者可以使用云开发快速开发小程序、小游戏、公众号网页等,并且原生打通微信开放能力。
开发者无需搭建服务器,可免鉴权直接使用平台提供的 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()
场景:页面或方法的逻辑较为复杂,关联多个数据库,可能存在多次查询以及云函数或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 } }
场景:多项目,多后台,多端打通,数据迁移等
// 云函数入口文件 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 } }