- 云开发·云调用生成小程序码
云开发·云调用生成小程序码 小程序云开发已经支持云调用,开放了很多接口,一直想要的获取小程序码也支持了。这下轻量的小程序也可以有自定义小程序码的功能。 1. 需求 获得一个带参数的小程序码,传播出去以后,用户扫码进入指定页面,根据参数做不同的处理。本文只讲小程序码生成、存储、展示部分。参数处理不多介绍,可以查看 项目代码 了解更多。 2. 开通云开发 新建小程序可以从开发工具的云开发模板初始化项目,根据云开发操作指引新建项目即可。 但是这里有个问题,已发布小程序的页面才能生成小程序码。如果现有的小程序没有开通云开发,需要做以下几步: 开发工具开通云开发,设定云开发的环境; 将原来的代码(除了[代码]project.config.json[代码]以外的所有文件)放到新建的 [代码]miniprogram[代码] 目录; 新增 [代码]cloudfunctions[代码] 目录; [代码]app.json[代码] 新增配置 [代码]"cloud": true[代码]; [代码]project.config.json[代码] 配置 [代码]"miniprogramRoot":"miniprogram/"[代码] 和 [代码]"cloudfunctionRoot":"cloudfunctions/"[代码]; 修改小程序基础库版本,最低要 2.3.0 [代码]"libVersion": "2.3.0"[代码]。 3. 生成小程序码 下面可以开始写代码开发了,开始之前,建议先看完官方教程。特别是开发工具的使用步骤,开发和调试时如果遇到奇怪的问题,可以尝试重启开发工具、重装开发工具,也可以去微信开放社区发帖。(重启和重装都是我在社区中发现的答案,能解决各种不应该存在的问题)。 3.1 准备文件 在 [代码]cloudfunctions[代码]目录右键新建Node.js云函数 [代码]getqr[代码]。 生成小程序码需要单独指定权限。在 [代码]getqr[代码] 目录新建 [代码]config.json[代码] ,里面写以下内容: [代码]{ "permissions": { "openapi": [ "wxacode.getUnlimited" ] } } [代码] 小程序码的获取方式有三种,这里只用到了接口 getUnlimited,选择这个接口的原因是漂亮的圆形小程序码,数量无限制。具体区别可以去 获取小程序码官方文档查看详情。 正常情况下,这个时候云函数可以部署测试了。如果遇到部署不成功、各种权限问题,可以尝试本地部署上传所有文件、重启试试。 3.2 生成小程序码 生成小程序码的代码如下,可以指定页面和页面参数 [代码]scene[代码],还有小程序码的尺寸。 注意这里的 [代码]scene[代码] 有限制: 最大32个可见字符; 只支持数字,大小写英文以及部分特殊字符:[代码]!#$&'()*+,/:;=?@-._~[代码]; 注意参数格式:下面实例代码生成小程序码后,扫码获得 [代码]pages/demo/demo?scene=id%3D6[代码] 。 [代码]try { const result = await cloud.openapi.wxacode.getUnlimited({ page: 'pages/demo/demo', scene: 'id=6', width: 240, }) console.log(result) return result } catch (err) { console.log(err) return err } [代码] 直接调用,比服务端调用少了 access_token 参数。 3.3 上传到云存储 返回值中的 buffer 就是图片内容,直接上传到云存储: [代码]const uploadResult = await cloud.uploadFile({ cloudPath: 'shareqr/' + qr_name_hash + '.jpg', fileContent: result.buffer, }); [代码] 我在云存储新建了 [代码]shareqr[代码] 目录保存小程序码; 图片名根据参数取md5摘要; [代码]getUnlimited[代码] 返回的图像是 [代码]jpeg[代码] 格式,后缀硬编码写 [代码].jpg[代码]。 3.4 获取图片临时路径 直接上代码 [代码]getURLReault = await cloud.getTempFileURL({ fileList: [uploadResult.fileID] }); fileObj = getURLReault.fileList[0] return fileObj [代码] 3.5 直接从存云存储获取 生成过以后图片已经保存在云存储,用同样的参数第二次调用没必要再生成一次,去掉一次网络请求,可以节省不少时间。 前面说到文件名使用请求参数摘要,知道了目录和文件名,再加上文件bucket前缀就可以拼出来 [代码]fileID[代码],用[代码]fileID[代码] 可以查询云存储的文件。 比如我刚刚生成的 fileID 是 [代码]cloud://dev-xxxx.8888-dev-xxxx/qr/44ea42f05091c3bec771123e6e8cd4c2.jpg[代码], 前缀就是 [代码]cloud://dev-xxxx.8888-dev-xxxx/[代码]。再拼上目录、文件名、后缀就是 [代码]fileID[代码]。 注:此处的 [代码]fileID[代码]拼接方法并不是来自官方文档,只是在使用中发现这个前缀不会变。还需要官方解释说明[代码]fileID[代码]规则。 如果会改变,就需要再用云数据库存储[代码]fileID[代码],更麻烦一些。 3.6 云函数完整代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk'); const crypto = require('crypto'); const bucketPrefix = 'cloud://dev-xxxx.8888-idc-4d11a4-1257831628/qr/'; // env: 'dev-xxxx' // 云函数入口函数 exports.main = async (event, context) => { const full_path = event.page + '?' + event.scene; const qr_name_hash = crypto.createHash('md5').update(full_path).digest('hex'); const temp_id = bucketPrefix + qr_name_hash + '.jpg'; // return { // full_path, // qr_name_hash, // temp_id // } try { // 先尝试获取文件,存在就直接返回临时路径 let getURLReault = await cloud.getTempFileURL({ fileList: [temp_id] }); // return getURLReault; let fileObj = getURLReault.fileList[0]; if (fileObj.tempFileURL != '') { fileObj.fromCache = true; return fileObj; } // 生成小程序码 const wxacodeResult = await cloud.openapi.wxacode.getUnlimited({ scene: event.scene, page: event.page, width: 280 //二维码的宽度,单位 px,最小 280px,最大 1280px }) // return wxacodeResult; if (wxacodeResult.errCode != 0) { // 生成二维码失败,返回错误信息 return wxacodeResult; } // 上传到云存储 const uploadResult = await cloud.uploadFile({ cloudPath: 'qr/' + qr_name_hash + '.jpg', fileContent: wxacodeResult.buffer, }); // return uploadResult; if (!uploadResult.fileID) { //上传失败,返回错误信息 return uploadResult; } // 获取图片临时路径 getURLReault = await cloud.getTempFileURL({ fileList: [uploadResult.fileID] }); fileObj = getURLReault.fileList[0]; fileObj.fromCache = false; // 上传成功,获取文件临时url,返回临时路径的查询结果 return fileObj; } catch (err) { return err } } [代码] 4. 小程序页面调用 调用页面就比较简单了,在小程序新建一个 [代码]pages/share/share[代码] 在 [代码]onLoad[代码] 函数调用云函数。 [代码]// 使用前记得先初始化云函数,一版放到 app.js onLaunch() 中 // wx.cloud.init({env: 'dev-8888'}) wx.cloud.callFunction({ name: 'getqr', data: { page: 'pages/demo/demo', scene: 'id=6', } }).then(res => { console.log(res.result); if (res.result.status == 0) { _this.setData({ qr_url: res.result.tempFileURL }) }else{ wx.showToast({ icon: 'none', title: '调用失败', }) } }).catch(err => { console.error(err); wx.showToast({ icon: 'none', title: '调用失败', }) }) [代码] 至此完整的调用过程已经全部完成,详细代码可以到 项目代码 查看。 代码中还对入口页面和share页面的参数做了包装,云函数可以直接使用,小程序可以稍做修改适应自己业务。 写在最后 小程序云开发已经开放了很多功能,除了这次提到的生成小程序码,云调用还可以发送模板消息。有需要的开发者又一个理由可以快速上线新功能了。 云开发还开放了[代码]HTTP API[代码],也就是用自己的服务器调用云函数。以前看完云开发介绍文章最大的疑问就是,你说的都很好,可是后台数据怎么管理呢?不能跟自己的服务器结合,只能放一些轻量的小程序。有了 [代码]HTTP API[代码] 以后就可以用自己的服务器做管理后台了。这时候你要问,都用上服务器了,还需要云开发做什么。首先,云开发免费;其次,免费功能已经够强,就差不能做Web管理后台了;最后,获取access_token(小程序及小游戏调用不要求IP地址在白名单内。)
2020-07-10 - 云函数生成小程序码并上传到云存储
同理可以将网络其他文件上传到云存储 首先安装 request-promise npm 命令 npm install request-promise // 云函数入口文件 const cloud = require('wx-server-sdk') //npm install request-promise const rp = require('request-promise'); cloud.init() // 云函数入口函数 exports.main = async (event, context) => { //appid 和秘钥 const appid = 'wxxxxxxxx', secret = 'xxxxxxxxxxxx'; const AccessToken_options = { method: 'GET', url: 'https://api.weixin.qq.com/cgi-bin/token', qs: { appid, secret, grant_type:'client_credential' }, json: true }; //获取AccessToken const resultValue = await rp(AccessToken_options); const token = resultValue.access_token; //获取小程序码配置 const code_options = { method: 'POST', url: 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token='+token, body: { 'page': "pages/index/index", 'width': 430, 'scene': "1111" }, json: true , encoding: null }; //获取二进制图片 const buffer = await rp(code_options); //数据大于10K 上传到云 if (buffer.length>1024*10) { const upload = await cloud.uploadFile({ cloudPath: 'demo5561.jpg', fileContent: buffer, }) return { upload} } return { reslut:buffer} }
2018-11-01 - 你必须要知道的微信小程序云开发
微信小程序开发已经成为目前最火爆的技能之一,无论是在求职、毕设、兴趣培养等方面都已经成为一项必备技能,而小程序云开发技术的出现更是点燃了整个小程序生态圈。 在2019微信公开课PRO小程序分论坛上,腾讯云宣布推出总价值超过10 亿元的“小程序·云开发”资源扶持计划,对超过一百万个小程序开发者提供免费资源扶持,全面助力开发者通过云开发打造优秀的微信小程序。这是继与微信团队联合推出降低开发门槛的“小程序·云开发”产品后,腾讯云在小程序开发成本上再次面向开发者释放红利。 那么什么是小程序云开发呢?我们通过对比云开发模式与传统开发模式之间的区别,来解释什么是小程序的云开发。 小程序云开发与传统开发模式区别? 小程序传统开发模式 [图片] 开发效率低: 大多数小程序所展示的数据都应该不是在页面上写死的,所以大多数小程序都需要一个服务端,服务端可以用多种技术实现,如PHP、Node.js、Java等。不管使用哪种技术实现服务端,开发一款小程序一般情况下都需要至少配备两个程序员,一个开发小程序前端,一个开发小程序服务端,这样的话这两个程序员之间就需要不断沟通,确认共同遵循的接口。可沟通过程中往往权责不清晰,有很多临界的位置,谁管都可以,容易引发扯皮,沟通成本非常高,导致开发效率下降。同时,由于开发人员的增多,整个开发的成本也会提高。这也是困扰着很多创业型公司的问题。 维护成本高: 项目上线的时候,公司需要自己搭建服务,不仅要花大价钱买机器、买宽带流量,还得请专门的人员去维护。运维人员需要考虑比如数据库运维,文件存储、内容加速、网络防护、容器服务、负载均衡、安全加固等等一系列的问题,这在公司里面是很头疼的一件事。 小程序云开发模式 小程序云开发是腾讯云和微信团队联合开发的,集成于小程序控制台的原生 Serverless 云服务,为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代。只需要一名开发人员就可以完成所有的工作。云开发核心能力包括:云存储、云数据库、云函数、云调用、HTTP API。 [图片] 区别对比 传统开发模式 开发效率低:过多的非业务逻辑需要处理,导致开发效率难以提升 资源投入高:无论是物理机托管,还是云主机维护,都需要较多的人力物力投入 产品上线慢:前后端联调、资源存储、部署等操作繁杂,上线流程耗时长 日常运维难:需时刻关注环境运行状况,管理相关资源,运维难度大 云开发模式 高效开发:只需编写核心逻辑代码,内建小程序用户鉴权,无需关注后端配置与部署,专注于业务开发 节约成本:按请求数和资源的运行收费,极大节约时间和成本,提供一定量免费额度使用 官方生态:原生集成微信SDK,云相关API开箱即用;同时,通过云调用,可免鉴权直接调用微信开放接口 稳定可靠:底层资源由腾讯云提供专业支持,满足不同业务场景和需求,具备快速拓展能力,确保服务稳定,数据安全 2019前端热词Serverless 在2019年,前端有一个很火的热词,叫做Serverless,server就是服务,less更少的,翻译过来就是无服务开发,而小程序云开发是这种无服务的开发。举个更形象的例子,比如我们想开一场演唱会,之前的做法是需要自己联系场地、灯光、伴奏,而有了云开发以后,相当于是演唱会需要的所有东西都有人帮我们准备好了,我们只需要站在舞台上演唱就可以。 Serverless中有一个概念,叫做 函数即服务,我们在使用云开发来实现小程序后端服务的时候,可以直接调用函数即可,对前端来说,后端服务就是一个函数,整个小程序的前后端逻辑都能在一个IDE里面完成,用户其实完全不用担心到底哪些是服务器的逻辑,后端服务和前端完全的融合在一种代码体系里去了,这样后端的服务即是一个函数,至于这个函数是在前端实现,或者是在后端很远的地方实现,开发者都可以不用关心。所以说,severless打破了物理隔离。开发者不再去做任何隔离中间层的事前,我只需要关心函数的实现就可以了。 所以这种开发模式可以实现真正的全栈技术开发,这对现有的开发模式是一个很大的革新。 小程序云开发优势 快速上线项目:快速上线对于公司是非常必要的。很多公司可能已经具备了自己的网站或者APP,但现在小程序如此火爆所以想开发一款小程序,那么小程序·云开发可以帮助你在最短时间上线应用,完成快速试错 专注核心业务,放弃非核心逻辑:使用云开发以后,你只需编写最重要的“核心代码”,不再需要关心周边组件,极大地降低了服务架构搭建的复杂性,成本更低 你可以独自完成一个小程序的设计、开发、发布:在传统的开发模式下,你需要一个后端开发者来配合你完成整个小程序的开发。在小程序·云开发中,你只需要借助云开发提供的丰富的 API ,就可以实现数据的存储、文件的上传、结果的计算,大大的提升了工作的效率 你无需学习一门新的语言:小程序·云开发目前支持 Node.js ,和进行小程序开发时使用的 JavaScript 同出一门,你可以以更低的学习成本来完成小程序的开发 你无需关注系统运维:当应用上线后,运维就成为了一个大的问题,当海量流量来袭时,如何快速调整系统容量,确保业务的稳步运行就成为了一个问题。当你使用云开发后,云开发将为你接管运维层面的事务,让你更加关注应用本身 弹性伸缩:在传统的单体开发模式中,应用需要以应用、站点为单位进行伸缩,因为我们的开发是基于整个应用、整个站点进行开发,无法单独对某一个特定的功能进行伸缩。而云开发所采用的 Serverless 方案中的运算部分,是交给云函数来进行处理的,你的应用由一个个函数组成的,因此,在弹性伸缩方面,粒度进一步细化,针对特定功能的函数来进行伸缩,弹性效率更高,能够承载的请求量更大 数据安全:在云开发模式下,每个用户的环境资源是独立的,也即是私有的,并且云开发提供与自有数据库打通的能力。这样,用户的数据都存在用户自己的云开发环境资源下面,一定层面保障了用户的数据安全与稳定性 开通云开发 下载微信开发者工具并安装:下载地址 新建项目,后端服务选择小程序云开发 [图片] 新建项目后,点击开发工具上方云开发按钮 [图片] 点击开通按钮 [图片] 填入环境名称,图中的基础配额完全都是免费的 [图片] 点击确定,开通云开发 [图片] 开通以后,官方给出从时间是十分钟左右就可以开通成功,实际测试其实很快 云开发提供的五大能力 1. 云函数 一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。 小程序内提供了专门用于云函数调用的 API。开发者可以在云函数内获取到每次调用的上下文(appid、openid 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid)。 开放了运用 Node.js 等框架编写“后端”业务逻辑后,直接可以部署在云平台下,完全不需要去考虑域名、服务器、打包发布、运维等等琐事 2. 云数据库 云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。 这样的话,数据库的存储也不用考虑了,直接提供了一个类似于 MongoDB一样的数据库,而且免费的存储空间达到了 2G,一般的项目足够使用了 3. 云存储 云开发提供了一块文件存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过 API 使用云文件存储功能。 在小程序端可以分别调用 wx.cloud.uploadFile 和 wx.cloud.downloadFile 完成上传和下载云文件操作。 这就相当于腾讯直接给你提供了一个静态文件的 CDN,免费的容量直接达到了 5G,一般项目前期是够用的 4. 云调用 云调用是云开发提供的基于云函数使用小程序开放接口的能力,支持在云函数调用服务端开放接口,如发送模板消息、获取小程序码等操作都可以在云函数中完成 5. HTTP API 云开发资源也可以通过 HTTP 接口访问,即在小程序外访问。比如你的小程序项目,需要做一个后台管理系统对小程序中的数据和文件进行管理,就可以使用HTTP API来访问云开发当中的资源 小程序开发适合人群 打算进入职场,从事前端开发甚至是全栈开发的职场小白 已经有了一定的前端工作经验,但由于公司业务需要,打算学习小程序开发的技能党 正在做毕业设计并且想做出精品高质量毕设项目的学生党 看到小程序开发如此火爆,打算一起尝鲜的达人 小程序开发学习建议 多看官方文档,小程序的官方文档写的非常的全面,涵盖了微信小程序开发的所有知识点,大家一定要多看微信小程序官方文档 多逛小程序开发社区,关于微信小程序的新功能以及更新内容都会在社区上面通知,大家遇到技术问题也可以在上面提问,会有微信团队官方工程师帮助我们解答 多敲代码,多练习。只有自己不断的练习才能真正的得到提高 分析问题和解决问题的能力。这是需要时间不断积累的,在遇到问题的时候,一定要多思考,对于有错误信息的问题一定要认真翻译错误信息,大多数的错误线索都能够被找到 微信小程序与云开发入门课程,可以看慕课免费视频《轻松入门微信小程序与云开发》,大家在学习中的问题都可以在课程问答评论区留言,都会认真的回复 小程序云开发的实战课程也已经上线, 《微信小程序云开发 – 从0打造云音乐全栈小程序》,这是首发的完全基于小程序云开发打造的一站式全栈小程序实战课程,本门课程以云音乐实战项目为例,是横跨小程序端、云开发后端、后台管理系统的一站式云开发小程序全栈项目
2019-08-22 - 用小程序·云开发打造功能全面的博客小程序丨实战
用小程序·云开发将博客小程序常用功能“一网打尽” 本文介绍mini博客小程序的详情页的功能按钮如何实现,具体包括评论、点赞、收藏和海报功能,这里记录下整个实现过程和实际编码中的一些坑。 评论、点赞、收藏功能 实现思路 实现文章的一些操作功能,最主要的还是评论,这是作者和读者之间沟通的桥梁,评论功能的衍生无非是细化作者和读者之间的互动,或者增加文章的传播,所以在动手开发时需要思考下你期望实现哪些功能,并对应功能进行细化。 我一般的经验是,先在脑子里过一遍需要的功能和大致流程,然后在笔记稍微画下「最最基础的原型,相当于产品的角色」。 然后就开始直接开始搭建页面和简单的交互「使用假数据,优先完成页面」,在构造页面的时候其实也能够补充最初想法上一些流程上的缺陷,这样在设计后端和数据库结构的时候可以补上,整体下来也基本比较完善了。 回头看我的小程序的需求,首先肯定是操作,在文章底部需要有个操作栏,用于发送点评和其他一些操作,在参考了一些同类型的小程序之后,逐步实现自己的一套风格,样式截图如下: [图片] 在有了功能之后,点评的数据需要有地方展示「通常是文章底部」,然后就有了文章底部的评论列表,样式如下: [图片] 既然有[代码]点赞[代码]和[代码]收藏[代码]的功能按钮,是否用户需要看下我点赞和收藏的文章列表呢,所以在「我的」中就有相应的列表,样式如下: [图片] 到这里,最最基础的功能基本差不多,接下来就要看后端是否能支持这些页面了「主要就是数据的保存和展示了」 对于评论来说,肯定需要一个集合用于保存用户的评论,而对于用户的喜欢和收藏也需要一个集合来进行保存。 所以根据页面我们就可以设计[代码]mini_comments[代码]和[代码]mini_posts_related[代码]两个集合。前者用于保存评论数据,后者用户保存用户操作与文章之间的关联。 剩下的工作就是变现了,无非就是页面交互和数据的增删改查了。 细节点解析 关于评论数量 目前在文章的集合中有个[代码]totalComments[代码]这个属性,当这篇文章每新增一个评论时,需要加1。 最初在写这个的时候,每次都是先查再更新,两段式,原代码如下: [代码] let count=post.totalComments+1; let result =await db.collection('mini_posts').doc(event.commentContent.postId).update({ data: { totalComments: count } }); [代码] 后来看文档发现,可以使用[代码]db.command.inc[代码]这个指令,无需再查一遍,直接可对原字段加1,还能保证原子性。代码如下: [代码] const _ = db.command let result = db.collection('mini_posts').doc(event.commentContent.postId).update({ data: { totalComments: _.inc(1) } }); [代码] 关于新增子评论 需要实现在某个评论下进行回复。 在交互上,点击评论者的昵称或头像时,触发相应的点击事件,在事件中去记录相应的评论ID及必要数据,同时去设置焦点到评论框内: [代码] /** * 点击评论内容回复 */ focusComment: function (e) { let that = this; let name = e.currentTarget.dataset.name; let commentId = e.currentTarget.dataset.id; let openId = e.currentTarget.dataset.openid; that.setData({ commentId: commentId, placeholder: "回复" + name + ":", focus: true, toName: name, toOpenId: openId }); }, [代码] 利用云开发新增子评论时可以使用[代码]db.command.push[代码]来进行操作「更新指令,对一个值为数组的字段,往数组尾部添加一个或多个值」,往子评论集合中新增: [代码] /** * 新增子评论 * @param {} event */ async function addPostChildComment(event) { let task = db.collection('mini_posts').doc(event.postId).update({ data: { totalComments: _.inc(1) } }); await db.collection('mini_comments').doc(event.id).update({ data: { childComment: _.push(event.comments) } }) await task; } [代码] 关于判断是否已收藏 在文章第一次加载时,我们需要判断下该用户是否有对该文章有相关操作,如果有相应的收藏和点赞操作,在初始化时需要更新相应的功能图标,核心代码如下: [代码] /** * 获取收藏和喜欢的状态 */ getPostRelated: async function (blogId) { let where = { postId: blogId, openId: app.globalData.openid } let postRelated = await api.getPostRelated(where, 1); let that = this; for (var item of postRelated.data) { if (config.postRelatedType.COLLECTION === item.type) { that.setData({ collection: { status: true, text: "已收藏", icon: "favorfill" } }) continue; } if (config.postRelatedType.ZAN === item.type) { that.setData({ zan: { status: true, text: "已赞", icon: "appreciatefill" } }) continue; } } }, [代码] 至于其他一些交互细节和代码细节,可以自行阅读源码去体会,如果有任何疑问或者有更好的实现方式,也可以与我沟通。 海报功能 交代些背景 其实在最早之前的小程序中已经实现了一次,具体可以参考利用云开发优化博客小程序(三)——生成海报功能,主要还是使用原生的[代码]cavans[代码]进行组装,原本想代码copy过来改改就行了,但总觉得原来的代码写的不是特别好。 于是想看看是否有现成的轮子可以利用,果然发现了[代码]wxa-plugin-canvas[代码]这款组件,通过非常简单的配置就可以生成精美的海报。 小程序使用npm 在总结生成海报功能之前还是有必要记录下小程序npm的使用,避免一些不必要的坑。 考虑到小程序本身的大小限制,使用npm的方式是最佳的。 原因是根据官方文档介绍,小程序 npm 包里只有构建文件生成目录会被算入小程序包的占用空间,上传小程序代码时也只会上传该目录的代码。这样大大减少了上传的代码体积。 下面简单介绍下小程序端如何使用npm的「其实根据官方文档按照步骤就可以了」。 以我目前小程序的路径为例,在[代码]/miniprogram[代码]新增文件夹[代码]node_modules[代码],在命令行指向到[代码]/miniprogram[代码]目录下: [图片] 通过命令进行安装: [代码] npm install wxa-plugin-canvas --production [代码] 安装成功后,即可在小程序开发工具中进行构建,构建前需要勾选[代码]使用 npm 模块[代码] [图片] 然后点击开发者工具中的菜单栏:工具 --> 构建 npm即可: [图片] 构建完成后会生成miniprogram_npm目录,到这里,项目端基本就调通了。 [图片] wxa-plugin-canvas 在构建完之后,就可以正常使用wxa-plugin-canvas这个自定义组件,使用方式还是比较简单的。 首先在你需要的页面引入该组件: [代码] { "usingComponents": {"poster": "wxa-plugin-canvas/poster"} } [代码] 然后就可以在[代码]wsml[代码]中使用了: [代码] <poster id="poster" hide-loading="{{false}}" preload="{{false}}" config="{{posterConfig}}" bind:success="onPosterSuccess" bind:fail="onPosterFail"></poster> [代码] 由于我们在生成海报前,需要异步获取一些用于海报的数据,所以我们采用异步生成的海报方式。 需要引入该组件的[代码]poster/poster.js[代码]文件,然后在代码中调用即可: [代码] import Poster from '../../utils/poster'; Page({ /** * 异步生成海报 */ onCreatePoster() { // setData配置数据 this.setData({ posterConfig: {...} }, () => { Poster.create(); }); } }) [代码] 核心代码解析 海报需要的数据 先来看看分享海报的整体结构: [图片] 首先需要确认海报的构成需要哪些数据,在调用组件前先获取好相应的数据。 在我设计的海报中主要包含三块内容,用户的信息(头像和昵称),文章信息(首图,标题,简介)和最重要的文章的小程序码。 用户信息和文章信息其实比较简单,在小程序的详情页两者数据都有,但这里有两个问题点需要注意下。 第一个是域名问题,在画布中使用到的图片都需要配置域名,头像的域名和公众号文章首图的域名 [代码] https://mmbiz.qpic.cn https://wx.qlogo.cn [代码] [图片] 第二个是公众号首图的问题,公众号素材列表返回的图片url其实是[代码]http[代码]的,但小程序规定绑定的域名必须是[代码]https[代码]的,当时比较无奈,后来尝试改用https访问首图的url也可以,不幸中的万幸,所以在使用首图地址时进行替换下: [代码] imageUrl = imageUrl.replace('http://', 'https://') [代码] 最后就是文章的小程序码了,需要利用小程序的[代码]getUnlimited[代码]的api,具体可以参考官方文档,目前已经提供了云调用的方式「无需获取access_token」,调用起来还是比较简单的。 原本打算在文章同步的时候「adminService」直接生成对应文章的小程序码,代码写完后本地调试可以,但上传至云端后测试发现一直报错,逛了轮胎才知道原来不支持,同时触发器也不支持云调用,所以这个计划泡汤了,我在代码中打了TODO。 [图片] 既然这样,那就在生成海报的时候进行生成,同时生成后直接上传至云存储,将对应的FileID保存至文章集合中,这样只用生成一次就可以一直使用了,具体代码如下: [代码] /** * 新增文章二维码 * @param {} event */ async function addPostQrCode(event) { let scene = 'timestamp=' + event.timestamp; let result = await cloud.openapi.wxacode.getUnlimited({ scene: scene, page: 'pages/detail/detail' }) if (result.errCode === 0) { let upload = await cloud.uploadFile({ cloudPath: event.postId + '.png', fileContent: result.buffer, }) await db.collection("mini_posts").doc(event.postId).update({ data: { qrCode: upload.fileID } }); let fileList = [upload.fileID] let resultUrl = await cloud.getTempFileURL({ fileList, }) return resultUrl.fileList } return [] } [代码] 但这里有个尴尬的地方是,生成小程序码的api中的[代码]scene[代码]参数最大长度是32,而文章id的长度已经是32了,无法根据文章id进行拼接跳转页面的路径了,所以这里暂时用了[代码]mini_posts[代码]集合中timestamp字段「理论上也是唯一的」。 所以在详情页中也需要兼容timestamp这个字段。 海报图片展示 海报图片展示就比较简单了,使用个弹窗,将生成好的海报图片进行展示即可: [代码] /** * 生成海报成功-回调 * @param {} e */ onPosterSuccess(e) { const { detail } = e; this.setData({ posterImageUrl: detail, isShowPosterModal: true }) console.info(detail) }, [代码] 保存海报图片 保存图片使用wx.saveImageToPhotosAlbum调用用户相册,这里主要需要兼容用户拒绝相册授权的一些列操作,具体代码如下: [代码] /** * 保存海报图片 */ savePosterImage: function () { let that = this wx.saveImageToPhotosAlbum({ filePath: that.data.posterImageUrl, success(result) { console.log(result) wx.showModal({ title: '提示', content: '二维码海报已存入手机相册,赶快分享到朋友圈吧', showCancel: false, success: function (res) { that.setData({ isShowPosterModal: false, isShow: false }) } }) }, fail: function (err) { console.log(err); if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { console.log("再次发起授权"); wx.showModal({ title: '用户未授权', content: '如需保存海报图片到相册,需获取授权.是否在授权管理中选中“保存到相册”?', showCancel: true, success: function (res) { if (res.confirm) { console.log('用户点击确定') wx.openSetting({ success: function success(res) { console.log('打开设置', res.authSetting); wx.openSetting({ success(settingdata) { console.log(settingdata) if (settingdata.authSetting['scope.writePhotosAlbum']) { console.log('获取保存到相册权限成功'); } else { console.log('获取保存到相册权限失败'); } } }) } }); } } }) } } }); }, [代码] 体验总结 有好的开源组件可以充分利用,避免重复造轮子,有机会也可以学习下别人的实现方式。 多看看文档,其实小程序的文档真的挺详细的。 这里主要想分享实现一个功能实现的过程,有想法的时候如何一步步去成功实现。 小程序本身不难,相应的文档也很详细,但是组装的过程和逻辑的实现需要自身去思考和体会。多看看文档,其实小程序的文档真的挺详细的。 如果你的想法和流程都非常清晰,但还是没办法实现你的预期功能,那我建议你先放放,先把[代码]html[代码],[代码]css[代码],[代码]javascript[代码]熟悉下,再看几遍小程序的文档,也许你当时面临的问题就不再是问题了。 源码链接 https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心! [图片]
2019-08-26 - 小程序·云开发实战 - 校园约拍小程序
创意来源于生活,之所以开发这个校园约拍小程序,是因为在摄影选修课上常听老师抱怨外出写生老找不到模特,许多大学生都想拥有一套专属自己记忆的摄影作品,记录下不会磨灭的美好回忆,可如何找到让自己满意的摄影师是他们的难题。悦拍屋是一个校园摄影o2o的约拍平台,提供全方位的约拍服务,同时提供一个自我展示,学习交流,互动娱乐的平台。接下来我将结合项目的讲解给大家分享一些实用技术和对于云开发的一些经验,希望对正在学习小程序的你有帮助。 前言 在开发一个项目之前首先要进行技术选型从而降低产品开发的技术风险和提高开发效率,技术选型必须得紧紧围绕着业务场景来选择。 产品原型设计:墨刀 UI组件库 1.微信原生样式库[代码]WeUI[代码],让用户使用感知更加统一 2.注重视觉交互体验的[代码]ColorUI[代码]组件库,在感知统一的基础上视觉元素多样化 前端 1.小程序原生语法以及[代码]API[代码] 2.[代码]Promise[代码]实现异步调用 3.[代码]ES6[代码]编写页面交互逻辑 后端 1.云函数:无需自建服务器,在云端运行的代码,微信私有协议天然鉴权,开发者只需编写自身业务逻辑代码 2.云数据库:无需自建数据库,一个既可在小程序前端操作,也能在云函数中读写的 [代码]JSON[代码] 数据库 3.云存储:实现小程序前端直接上传/下载云端文件,在云开发控制台可视化管理 4.云调用:由原生微信服务集成,基于云函数免鉴权使用小程序开放接口的能力,包括服务端调用、获取开放数据等能力 其他 1.使用微信提供的云测试对未上线的小程序进行缺陷测试、性能数据分析、机型覆盖测试,确保小程序上线后正常运营 2.使用基于云开发的[代码]AI视觉能力[代码]-身份证识别实现实名认证,智能鉴黄结合人工完成发布信息的审核 3.开发工具:微信开发者工具、VScode 4.部分图标使用自阿里巴巴矢量图标库 总体设计 功能结构图 大家可以通过此图了解整个项目的主要功能点 [图片] 产品原型图 此处给出一张主页原型图示例,墨刀还是挺好用的 [图片] 色彩设计图 悦拍屋的整体色调为浅蓝色,各位小伙伴在开发自己项目的时候可以根据色彩标准搭配来设计项目所采用的色彩,合适的色彩搭配可以给用户良好的视觉体验 [图片] 功能模块详解 接下来我会对部分功能模块以图文结合的形式详细描述,将其中涉及的技术、知识分享给大家 约拍邀请 用户可在首页查看约拍需求,并点击查看需求详情,用户在了解需求后,若自己符合条件即可提交约拍信息,等待发布者的回复,可将此需求收藏方便查看 [图片] 技术分享:自定义顶部导航栏 官方默认的导航栏只能对背景颜色进行更改,对于想要在导航栏添加一些比较酷炫的效果则需要通过自定义导航栏实现 实现原理:通过设置[代码]app.json[代码]中页面配置的[代码]navigationStyle[代码](导航栏样式)配置项的值为[代码]custom[代码],即可实现自定义导航 [代码]"window":{ "navigationStyle":"custom" } [代码] 本项目的部分页面自定义导航栏实现使用了[代码]ColorUI[代码]的导航栏组件,在完成上一步属性设置后再引入导航栏组件即可 [代码]"usingComponents":{ "cu-custom":"/colorui/components/cu-custom" //该路径替换为自己项目内ColorUI组件所在位置 } [代码] 主页自定义导航栏通过设置背景图片加上GIF波浪效果 [代码] <view class='page__bd'> <view class="bg-img padding-tb-xl" style="background-image:url('http://wx4.sinaimg.cn/mw690/006UdlVNgy1g2v2t1ih8jj31hc0p0qej.jpg');background-size:cover;"> <view class="cu-bar"> <view class="content text-bold text-white"> 悦拍屋 </view> </view> </view> <view class="shadow-blur"> <image src="https://image.weilanwl.com/gif/wave.gif" mode="scaleToFill" class="gif-black response" style="height:100rpx;margin-top:-100rpx;"></image> </view> </view> [代码] 效果图 [图片] 使用组件定义的导航栏 [代码]<cu-custom bgImage="https://s2.ax1x.com/2019/05/02/Etiyng.jpg" isBack="{{true}}"> <view slot="backText">返回</view> <view slot="content">认证信息说明 </view> </cu-custom> [代码] 效果图 [图片] [代码]特别提醒1:使用自定义导航后,页面的返回需要在自定义导航栏中自行设置 [代码] [代码]特别提醒2:导航栏组件需要自行引入ColorUI组件库后才能使用,具体引入教程地址在附录中给出 [代码] 发布约拍 选择发布约拍功能填写约拍需求,提交审核通过后可在首页实时查看发布结果 [图片] 技术分享:入场动画 额。。录制可能略微有点卡顿,实际效果挺流畅的,各位大佬有什么好的录制工具推荐可以在评论中回复 实现原理:通过[代码]toggleDelay[代码]的布尔值为真动态添加动画类名,在生命周期函数[代码]onReady[代码]中控制[代码]toggleDelay[代码]的值从而控制整个动画过程(原理与[代码]Vue[代码]的动态类名相似) [代码]data:{ toggleDelay;false }, onReady:function(){ let that = this //toggleDelay的值为真,动画开始 that.setData({ toggleDelay: true }) //控制整个动画的时长 setTimeout(function() { that.setData({ toggleDelay: false }) }, 2000) } [代码] [代码]<view class="padding-xs {{toggleDelay?'animation-slide-bottom':''}}" style="animation-delay: {{item.time}}s;" wx:for="{{list}}" wx:key="{{index}}"> <image class="img" id='img{{index}}' src="{{item.src}}" mode="widthFix" /> </view> [代码] [代码]//所有动画的定义 [class*=animation-] { animation-duration: .5s; animation-timing-function: ease-out; animation-fill-mode: both } //animatioon-slide-bottom所定义的动画 .animation-slide-bottom { animation-name: slide-bottom } //动画效果 @keyframes slide-bottom { 0% { opacity: 0; transform: translateY(100%) } 100% { opacity: 1; transform: translateY(0) } } [代码] [代码]animation-slide-bottom[代码]是动画类名,[代码]animation-delay[代码]是每一个卡片动画执行的延迟时间,每一个动画的执行时长为0.5s,所以延迟时间是以0.5s递增的,三个卡片的动画总时长就为2s,即2s后就执行[代码]onReady[代码]中的[代码]settimeout[代码]事件结束动画 [代码]特别提醒:动画的延迟时间,执行时间可以自行设计,动画效果过渡自然即可 [代码] [代码]特别提醒:由于触发动画的钩子函数定义在页面初次渲染的生命周期函数中,故只有在页面初次渲染时才执行,避免每次显示页面时加载动画造成用户的视觉疲劳 [代码] 智能推荐约拍对象 系统会根据约拍需求自动推荐约拍对象(个人开发精力有限,推荐算法后续推出。。。) [图片] 技术分享:CSS3实现酷炫搜索动画 在模态框内放置两个[代码]view[代码]标签,以下是标签定义 [代码] <view id='preloader'> //外围的圆形框定义 <view id='loader'></view> //内部的线条定义 </view> [代码] [代码]#preloader { width: 150px; height: 150px; border-radius: 50%; border: 1px solid #97b2ff; } #loader { //中间线条定义 display: block; position: relative; left: 50%; top: 50%; width: 150px; height: 150px; margin: -75px 0 0 -75px; border-radius: 50%; border: 3px solid transparent; border-top-color: #97b2ff; -webkit-animation: spin 2s linear infinite; animation: spin 2s linear infinite; } #loader:before { //通过伪类元素定义外围线条 content: ""; position: absolute; top: 5px; left: 5px; right: 5px; bottom: 5px; border-radius: 50%; border: 3px solid transparent; border-top-color: #97b2ff; -webkit-animation: spin 3s linear infinite; animation: spin 3s linear infinite; } #loader:after { //通过伪类元素定义最内部线条 content: ""; position: absolute; top: 15px; left: 15px; right: 15px; bottom: 15px; border-radius: 50%; border: 3px solid transparent; border-top-color: #97b2ff; -webkit-animation: spin 1.5s linear infinite; animation: spin 1.5s linear infinite; } [代码] 实名认证 [图片] 嘿嘿,由于懒得给个人信息打码,就暂时不给大家演示认证过程了。。 技术分享:Ai视觉能力 很多小伙伴都有过在自己项目中使用AI技术的想法,但又因为入门AI的难度比较大,并且需要的时间较长就放弃了,现在给大家安利一个可以直接使用的AI服务,让AI不再具有神秘感(AI大佬可以忽略此部分。。) 方案一 在腾讯云中搜索身份证识别,上面会有详细的API文档以及测试工具帮助你快速使用 [图片] 点击查看腾讯云-身份证识别 方案二 方案一是以提供API接口的形式提供身份证识别服务,而接下来要介绍的方案真的就比较简单了,在腾讯云中搜索智能图像,其中的增值服务AI智能图像能力,你可以通过云函数和云存储实现相应功能,基于小程序云开发的 AI DEMO中开发好了部分功能,你只需通过教程将云函数和组件引入你的项目即可使用 [图片] 点击查看腾讯云-智能图像 点击查看基于小程序云开发的 AI DEMO [代码]特别提醒:当然使用这些服务也并非是完整的解决方案,对于身份证信息的加密、存储方案、安全协议等还是需要各位小伙伴自行设计解决方案哦。 [代码] 云开发 云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容,并不互斥。 官方文档中API被分为了小程序端和服务端,一开始看过两端的API之后,感觉好像没有什么不同啊,在查阅相关资料以及实际开发中某些业务的处理总结出一些经验后才明白了两者的不同,下面给各位具体说说两者的不同之处,应该能帮助大家在使用云开发实战时少踩一点坑 初始化的不同 小程序端 全局声明一次 [代码]if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else { wx.cloud.init({ env:'xxx', traceUser: true, }) } [代码] 服务端 每个云函数中声明一次 [代码]const cloud = require('wx-server-sdk') cloud.init() [代码] 权限不同 小程序端 在小程序端可以选择直接操作数据库,但由于是前端操作数据库存在一些安全问题,有较多的权限限制,在云控制中可对每个集合进行权限设置,这也就是为什么有小伙伴在小程序端对某些数据进行更新,显示更新成功但并未更新数据,就是因为小程序端默认只能更新当前用户写入的数据 [图片] [代码]特别提醒:在小程序端使用创建者的权限对数据进行修改时一定要确保该集合中有_openid字段,否则系统在权限判断时是没有办法识别当前操作为创建者的,数据修改无法执行 [代码] 服务端 服务端拥有管理员的权限,对所有数据拥有读写权限 语法支持不同 小程序端 在微信开发者工具里,以及Android端手机(浏览器内核是QQ浏览器的X5),[代码]async[代码]/[代码]await[代码]是天然支持的,但 iOS 端手机在较低版本则不支持,因此需要引入额外的[代码]polyfill[代码]。可以在有使用[代码]async[代码]/[代码]await[代码] 的文件当中引入[代码]polyfill[代码]文件。 [代码]const runtime = require('相对路径/lib/runtime') [代码] 服务端 在云函数里,由于 Node 版本最低是 8.9,因此是天然支持 async/await 语法的 示例:获取约拍需求列表 [代码]//云函数入口文件 const cloud = require('wx-server-sdk') //初始化 cloud.init() //连接数据库 const db = cloud.database() async function getAll(){ const result = await db.collection('ypList') .orderBy('cameraInfo.launchTime','desc').where({}).get() return result } // 云函数入口函数 exports.main = async (event, context) => { //此处的action是用来判断该调用哪一个方法 if(event.action === 'getAll'){ return getAll() } } [代码] 结语 一个人手撸个全栈项目确实很辛苦,但收获也很多。至少对于小程序的实战开发更为熟练了,对MVVM的思想的理解也更加深刻了。技术发展得很快,学习一项技术如果不深入其本质,那么技术是学不完的。深入学习就是个解决问题的过程,或是帮助别人解决问题,或是借助他人的力量解决问题。目前在正在学习Vue、React、TypeScript等技术,后续会推出相关技术的项目解析文章,希望对于同样在学习的你有帮助。 [代码]特别说明:本项目已参加2019届中国高校计算机-微信应用开发赛完,开源至github,感兴趣的小伙伴可以看看 [代码] 附录 在此提供一些本项目涉及到的技术、工具等链接供大家学习使用 产品原型设计工具:墨刀 色彩搭配设计:配色网 在线作图:ProcessOn UI样式库:WeUI UI样式库:ColorUI 图标库:Iconfont阿里巴巴矢量图标库 开发工具:微信开发者工具 开发者工具:Vscode 腾讯云服务:身份证识别 腾讯云服务:智能图像 API文档:微信官方文档.小程序 技术文档:ES6 源码链接 https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心! [图片]
2019-08-05 - 小程序云开发之数据库自动备份
数据是无价的,我们通常会把重要的业务数据存放在数据库中,并需要对数据库做定时的自动备份工作,防止数据异常丢失,造成无法挽回的损失。 小程序云开发提供了方便的云数据库供我们直接使用,云开发使用了腾讯云提供的云数据库,拥有完善的数据保障机制,无需担心数据丢失。但是,我们还是不可避免的会担心数据库中数据的安全,比如不小心删除了数据集合,写入了脏数据等。 还好,云开发控制台提供了数据集合的导出,导入功能,我们可以手动备份数据库。不过,总是手动备份数据库也太麻烦了点,所有重复的事情都应该让代码去解决,下面我们就说说怎么搞定云开发数据库自动备份。 通过查阅微信的文档,可以发现云开发提供了数据导出接口databaseMigrateExport [代码]POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN [代码] 通过这个接口,结合云函数的定时触发功能,我们就可以做数据库定时自动备份了。梳理一下大致的流程: 创建一个定时触发的云函数 云函数调用接口,导出数据库备份文件 将备份文件上传到云存储中以供使用 1. 获取 access_token 调用微信的接口需要 access_token,所以我们首先要获取 access_token。通过文档了解到使用 auth.getAccessToken 接口可以用小程序的 appid 和 secret 获取 access_token。 [代码]// 获取 access_token request.get( `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`, (err, res, body) => { if (err) { // 处理错误 return; } const data = JSON.parse(body); // data.access_token } ); [代码] 2. 创建数据库导出任务 获取 access_token 后,就可以使用 [代码]databaseMigrateExport[代码] 接口导出数据进行备份。 [代码]databaseMigrateExport[代码] 接口会创建一个数据库导出任务,并返回一个 job_id,这个 job_id 怎么用我们下面再说。显然数据库的数据导出并不是同步的,而是需要一定时间的,数据量越大导出所要花费的时间就越多,个人实测,2W 条记录,2M 大小,导出大概需要 3~5 S。 调用 [代码]databaseMigrateExport[代码] 接口需要传入环境 Id,存储文件路径,导出文件类型(1 为 JSON,2 为 CSV),以及一个 query 查询语句。 因为我们是做数据库备份,所以这里就导出 JSON 类型的数据,兼容性更好。需要备份的数据可以用 query 来约束,这里还是很灵活的,既可以是整个集合的数据,也可以是指定的部分数据,这里我们就使用 [代码]db.collection('data').get()[代码] 备份 data 集合的全部数据。同时我们使用当前时间作为文件名,方便以后使用时查找。 [代码]request.post( `https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`, { body: JSON.stringify({ env, file_path: `${date}.json`, file_type: '1', query: 'db.collection("data").get()' }) }, (err, res, body) => { if (err) { // 处理错误 return; } const data = JSON.parse(body); // data.job_id } ); [代码] 3. 查询任务状态,获取文件地址 在创建号数据库导出任务后,我们会得到一个 job_id,如果导出集合比较大,就会花费较长时间,这时我们可以使用 databaseMigrateQueryInfo 接口查询数据库导出的进度。 当导出完成后,会返回一个 [代码]file_url[代码],即可以下载数据库导出文件的临时链接。 [代码]request.post( `https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`, { body: JSON.stringify({ env, job_id: jobId }) }, (err, res, body) => { if (err) { reject(err); } const data = JSON.parse(body); // data.file_url } ); [代码] 获取到文件下载链接之后,我们可以将文件下载下来,存入到自己的云存储中,做备份使用。如果不需要长时间的保留备份,就可以不用下载文件,只需要将 job_id 存储起来,当需要恢复备份的时候,通过 job_id 查询到新的链接,下载数据恢复即可。 至于 job_id 存在哪,就看个人想法了,这里就选择存放在数据库里。 [代码]await db.collection('db_back_info').add({ data: { date: new Date(), jobId: job_id } }); [代码] 4. 函数定时触发器 云函数支持定时触发器,可以按照设定的时间自动执行。云开发的定时触发器采用的 [代码]Cron[代码] 表达式语法,最大精度可以做的秒级,详细的使用方法可以参考官方文档:定时触发器 | 微信开放文档 这里我们配置函数每天凌晨 2 点触发,这样就可以每天都对数据库进行备份。在云函数目录下新建 [代码]config.json[代码]文件,写入如下内容: [代码]{ "triggers": [ { "name": "dbTrigger", "type": "timer", "config": "0 0 2 * * * *" } ] } [代码] 完整代码 最后,贴出可以在云函数中使用的完整代码,只需要创建一个定时触发的云函数,并设置好相关的环境变量即可使用 appid secret backupColl:需要备份的集合名称,如 ‘data’ backupInfoColl:存储备份信息的集合名称,如 ‘db_back_info’ 注意,云函数的默认超时时间是 3 秒,创建备份函数时,建议将超时时间设定到最大值 20S,留有足够的时间查询任务结果。 [代码]/* eslint-disable */ const request = require('request'); const cloud = require('wx-server-sdk'); // 环境变量 const env = 'xxxx'; cloud.init({ env }); // 换取 access_token async function getAccessToken(appid, secret) { return new Promise((resolve, reject) => { request.get( `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`, (err, res, body) => { if (err) { reject(err); return; } resolve(JSON.parse(body)); } ); }); } // 创建导出任务 async function createExportJob(accessToken, collection) { const date = new Date().toISOString(); return new Promise((resolve, reject) => { request.post( `https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=${accessToken}`, { body: JSON.stringify({ env, file_path: `${date}.json`, file_type: '1', query: `db.collection("${collection}").get()` }) }, (err, res, body) => { if (err) { reject(err); } resolve(JSON.parse(body)); } ); }); } // 查询导出任务状态 async function waitJobFinished(accessToken, jobId) { return new Promise((resolve, reject) => { // 轮训任务状态 const timer = setInterval(() => { request.post( `https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=${accessToken}`, { body: JSON.stringify({ env, job_id: jobId }) }, (err, res, body) => { if (err) { reject(err); } const { status, file_url } = JSON.parse(body); console.log('查询'); if (status === 'success') { clearInterval(timer); resolve(file_url); } } ); }, 500); }); } exports.main = async (event, context) => { // 从云函数环境变量中读取 appid 和 secret 以及数据集合 const { appid, secret, backupColl, backupInfoColl } = process.env; const db = cloud.database(); try { // 获取 access_token const { errmsg, access_token } = await getAccessToken(appid, secret); if (errmsg && errcode !== 0) { throw new Error(`获取 access_token 失败:${errmsg}` || '获取 access_token 为空'); } // 导出数据库 const { errmsg: jobErrMsg, errcode: jobErrCode, job_id } = await createExportJob(access_token, backupColl); // 打印到日志中 console.log(job_id); if (jobErrCode !== 0) { throw new Error(`创建数据库备份任务失败:${jobErrMsg}`); } // 将任务数据存入数据库 const res = await db.collection('db_back_info').add({ data: { date: new Date(), jobId: job_id } }); // 等待任务完成 const fileUrl = await waitJobFinished(access_token, job_id); console.log('导出成功', fileUrl); // 存储到数据库 await db .collection(backupInfoColl) .doc(res._id) .update({ data: { fileUrl } }); } catch (e) { throw new Error(`导出数据库异常:${e.message}`); } }; [代码]
2019-08-12