- 小程序自定义消息事件示例
小程序自定义消息事件示例 用于从A页面发送数据到B页面,或者从B页面提前发送事件到A页面改变数据 需求解决: 1、B页面更新数据成功,需要调用系统的 wx.showToast 吐司提示,如果刚调用立马返回A页面,会造成吐司立刻消失。 2、如果B页面停留两秒吐司完毕再关闭B页面,再进入A页面,可能这个时候用户立马按系统返回,会造成跟定时器两秒同时 返回两次的现象 /** * 使用示例 */ messageEvent.addEventListener({ name: "sum" }, function (res) { console.log("addEventListener res=>", res); }) setTimeout(() => { messageEvent.sendEvent({ name: "sum", extra: 3 }) }, 1500) setTimeout(() => { messageEvent.removeEventListener({ name: "sum", }) }, 3000) setTimeout(() => { console.log(messageEvent) }, 5000) 消息事件 addEventListener 监听事件 addEventListener({params}, callback(res)) removeEventListener 移除事件监听 removeEventListener({params}) sendEvent 将任意一个自定义事件广播出去,该事件可在任意页面通过 addEventListener 监听收到。 sendEvent({params}) 代码片段:https://developers.weixin.qq.com/s/N0EqPfmv7Hkb 如果,想通过小程序构建npm 方式,推荐使用mitt工具: [图片] /* 使用方法: $ npm init $ npm install --save mitt 小程序开发工具(工具-构建npm) https://github.com/developit/mitt/ */ import mitt from 'mitt' const emitter = mitt() // listen to an event emitter.on('foo', e => console.log('foo', e) ) // listen to all events emitter.on('*', (type, e) => console.log(type, e) ) // fire an event emitter.emit('foo', { a: 'b' }) // clearing all events emitter.all.clear() // working with handler references: function onFoo() {} emitter.on('foo', onFoo) // listen emitter.off('foo', onFoo) // unlisten
2020-11-11 - 如何在短视频分享商品信息
一、如何在短视频分享商品?第一步:添加商品至商品橱窗方式一:从选品中心添加商品微信发现页-视频号-创作者中心-带货中心-去选品-在选品中心添加商品至橱窗。 [图片] 方式二:从小商店添加商品具有小商店相关权限的店铺成员(店长、店员、推广员、导购),可以在微信发现页-视频号-创作者中心-带货中心-橱窗管理-添加商品-选择通过小商店添加商品。 [图片] 方式三:从以下渠道添加商品信息可通过多种渠道添加商品信息至视频号橱窗 渠道 1-小商店助手—我要带货:微信搜索「小商店助手」小程序-开通小商店-点击我要带货-选择商品-添加商品信息至商品橱窗。(仅限非个人主体的小商店且需要绑定小程序联盟的PID) [图片] 渠道 2-小程序联盟:微信搜索「小程序联盟」小程序-选择商品-点击「带货」-点击「上架到视频号橱窗」 [图片] 也可以通过:视频号-创作者中心-带货中心-去选品-选品中心-筛选-小程序联盟,选择将小程序联盟的商品加入商品橱窗。 渠道 3-成为商家推广员:需要商家先将创作者添加为“视频号推广员”。(操作步骤:小程序商家在微信公众平台添加创作者视频号为推广员,小商店商家在小商店管理后台添加创作者为推广员),添加成功后,创作者可以在其视频号商品橱窗内添加该小程序/小商店的商品。(注:平台将不对「推广员」进行分佣结算,需要商家自行与创作者结算) [图片] (小程序商家在微信公众平台添加推广员) [图片] (小商店商家在小商店管理后台添加) 第二步:发表视频时添加商品信息发表视频-选择添加链接-添加商品-选择已添加至橱窗中的商品,或者点击「从商店选择」添加商品-发表视频即可 注:视频内容需与商品具有相关性 [图片] 二、如何获得短视频商品分享权限满足条件的创作者可在:创作者中心-带货中心-攻略-如何开通商品分享权限,自主申请商品信息分享功能,经平台审核通过即可获得在短视频中分享商品信息的权限。 [图片] 如果你是商家,希望将自己的商品提供给视频号创作者进行分享,可以参考流程:如何让视频号创作者带自己的商品 三、佣金结算说明不同添加商品方式的佣金结算不同: 通过方式一在视频号【选品中心】选品带货,结算规则请查看:视频号带货中心结算说明 | 微信开放社区通过方式三的渠道1在【小商店助手】选品带货,结算规则请查看:小程序联盟佣金计算规则 | 微信开放文档通过方式三的渠道2在【小程序联盟】选品带货,结算规则请查看:小程序商品的佣金结算规则 | 微信开放文档通过方式三的渠道3作成为小商店导购分销员带货,结算规则请查看:导购分销 - 用销售激励,让人一起帮你卖货 | 微信开放社区通过方式三的渠道3「推广员」能力帮助小商店/小程序商家分享商品,由商家自行与创作者进行佣金结算,平台不负责计佣结算。
2022-03-29 - 如何突破一次只能获取20条记录的limit限制?只需要一行代码。
笔者刚遇到需要一次性拉取超过100条(云函数里超过1000条)记录的这种需求。 一般情况下,会有下面两种处理方式: 1、先获取总数,再for循环,每次拉取limit条记录;(可结合Promise.all并发) 2、递归拉取,每次拉取limit条记录,直到拉取的记录数量小于limit。 以上两种方式都比较麻烦,于是动了一脑筋,以最简单的方式实现上面的需求。 极简代码如下: db.collection('order').aggregate() .match({ status:'已付费' }) .addFields({ tempTag:1 //增加一个临时标签;也可以不要addFields这个阶段; }) .group({ _id:'$tempTag', orders:$.push('$$ROOT') //一次性拉取超过100条或者1000条记录 }) .end() .then(res=>{ let orders = res.list[0].orders console.log(orders) }) 一个临时标签,搞定。 小心数据量太大搞崩了,崩溃的极限是多少,需要各位自行摸索了。 需要注意的是,如果是云函数里执行以上代码(比如lookup),返回小程序端的数据量不要超过1M。
2021-03-15 - 数据库原子操作和事务讲解
使用更新指令(如 inc、mul、addToSet)可以对云数据库的一条记录和记录内的子文档(结合反范式化设计)进行原子操作,但是如果要跨多个记录或跨多个集合的原子操作时,就需要使用云数据库的事务能力。 12.6.1 更新指令的原子操作关系型数据库是很难做到通过一个语句对数据强制一致性的需求来表示的,只能依赖事务。但是云开发数据库由于可以反范式化设计内嵌子文档,以及更新指定可以对单个记录或同一个记录内的子文档进行原子操作,所以通常情况下,云开发数据库不必使用事务。 比如调整某个订单项目的数量之后,应该同时更新该订单的总费用,我们可以设计采用如下方式设计该集合,比如订单的集合为 order: { "_id": "2020030922100983", "userID": "124785", "total":117, "orders": [{ "item":"苹果", "price":15, "number":3 },{ "item":"火龙果", "price":18, "number":4 }] } 客户在下单的时候经常会调整订单内某个商品比如苹果的购买数量,而下单的总价又必须同步更新,不能购买数量减少了,但是总价不变,这两个操作必须同时进行,如果是使用关系型数据库,则需要先通过两次查询,更新完数据之后,再存储进数据库,这个很容易出现有的成功,有的没有成功的情况。但是云开发的数据库则可以借助于更新指令做到一条更新来实现两个数据同时成功或失败: db.collection("order") .doc("2020030922100983") .update({ data: { "orders.0.number": _.inc(1), total: _.inc(15), }, }); 这个操作只是在单个记录里进行,那要实现跨记录要进行原子操作呢?更新指令其实是可以做到事务仿真的,但是比较麻烦,这时就建议用事务了。 12.6.2 事务与 ACID事务就是一段数据库语句的批处理,但是这个批处理是一个 atom(原子),多个增删改的操作是绑定在一起的,不可分割,要么都执行,要么回滚(rollback)都不执行。比如银行转账,需要做到一个账户的钱汇出去了,那另外一个账户就一定会收到钱,不能钱汇出去了,但是钱没有到另外一个的账上;也就是要执行转账这个事务,会对 A 用户的账户数据和 B 用户的账户数据做增删改的处理,这两个处理必须一起成功一起失败。 1、ACID一般来说,事务是必须满足 4 个条件(ACID): Atomicity(原子性)、Consistency(稳定性)、Isolation(隔离性)、Durability(可靠性): 原子性:整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中一部分操作,一致性:事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行前后,数据库都必须处于一致性状态。换句话说,事务的执行结果必须是使数据库从一个一致性状态转变到另一个一致性状态。比如在执行事务前,A 用户账户有 50 元,B 用户账户有 150 元;执行 B 转给 A 50 元事务后,两个用户账户总和还是 200 元。隔离性:事务的隔离性是指在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间事务之间,互不干扰。比如在线银行,同时转账的人虽然很多,但是不会出现影响 A 与 B 之间的转账;可靠性:即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态,已提交事务的更新不会丢失。 2、云函数事务注意事项01不支持批量操作,只支持单记录操作 在事务中不支持批量操作(where 语句),只支持单记录操作(collection.doc, collection.add),这可以避免大量锁冲突、保证运行效率,并且大多数情况下,单记录操作足够满足需求,因为在事务中是可以对多个单个记录进行操作的,也就是可以比如说在一个事务中同时对集合 A 的记录 x 和 y 两个记录操作、又对集合 B 的记录 z 操作。 02云数据库采用的是快照隔离 对于两个并发执行的事务来说,如果涉及到操作同一条记录的时候,可能会发生问题。因为并发操作会带来数据的不一致性,包括脏读、不可重复读、幻读等。 脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据;不可重复读:在一个事务内两次读到的数据是不一样的,受到另一个事务修改后提交的影响,因此称为是不可重复读幻读:第一个事务对表进行读取,当第二个事务对表进行增加或删除操作事务提交后,第一个事务再次读取,会出现增加或减少行数的情况云开发的数据库系统的事务过程采用的是快照隔离(Snapshot isolation),可以避免并发操作带来数据不一致的问题。 事务期间,读操作返回的是对象的快照,而非实际数据事务期间,写操作会:1. 改变快照,保证接下来的读的一致性;2. 给对象加上事务锁事务锁:如果对象上存在事务锁,那么:1. 其它事务的写入会直接失败;2. 普通的更新操作会被阻塞,直到事务锁释放或者超时事务提交后,操作完毕的快照会被原子性地写入数据库中 12.6.3 事务操作的两套 API云开发数据库的事务提供两种操作风格的接口,一个是简易的、带有冲突自动重试的 runTransaction 接口,一个是流程自定义控制的 startTransaction 接口。通过 runTransaction 回调中获得的参数 transaction 或通过 startTransaction 获得的返回值 transaction,我们将其类比为 db 对象,只是在其上进行的操作将在事务内的快照完成,保证原子性。transaction 上提供的接口树形图一览: transaction |-- collection 获取集合引用 | |-- doc 获取记录引用 | | |-- get 获取记录内容 | | |-- update 更新记录内容 | | |-- set 替换记录内容 | | |-- remove 删除记录 | |-- add 新增记录 |-- rollback 终止事务并回滚 |-- commit 提交事务(仅在使用 startTransaction 时需调用) 1、通过 runTransaction 回调获得 transaction以下提供一个使用 runTransaction 接口的,两个账户之间进行转账的简易示例。事务执行函数由开发者传入,函数接收一个参数 transaction,其上提供 collection 方法和 rollback 方法。collection 方法用于取数据库集合记录引用进行操作,rollback 方法用于在不想继续执行事务时终止并回滚事务。 const cloud = require("wx-server-sdk"); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV, }); const _ = db.command; exports.main = async (event) => { try { const result = await db.runTransaction(async (transaction) => { const aaaRes = await transaction.collection("account").doc("aaa").get(); const bbbRes = await transaction.collection("account").doc("bbb").get(); if (aaaRes.data && bbbRes.data) { const updateAAARes = await transaction .collection("account") .doc("aaa") .update({ data: { amount: _.inc(-10), }, }); const updateBBBRes = await transaction .collection("account") .doc("bbb") .update({ data: { amount: _.inc(10), }, }); console.log(`transaction succeeded`, result); return { aaaAccount: aaaRes.data.amount - 10, }; } else { await transaction.rollback(-100); } }); return { success: true, aaaAccount: result.aaaAccount, }; } catch (e) { console.error(`事务报错`, e); return { success: false, error: e, }; } }; 事务执行函数必须为 async 异步函数或返回 Promise 的函数,当事务执行函数返回时,SDK 会认为用户逻辑已完成,自动提交(commit)事务,因此务必确保用户事务逻辑完成后才在 async 异步函数中返回或 resolve Promise。 2、通过 startTransaction 获得 transactiondb.startTransaction(),开启一个新的事务,之后即可进行 CRUD 操作;db.startTransaction().transaction.commit(),提交事务保存数据,在提交之前事务中的变更的数据对外是不可见的;db.startTransaction().rollback(),事务终止并回滚事务,例如,一部分数据更新失败,对已修改过的数据也进行回滚。const cloud = require("wx-server-sdk"); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV, }); const db = cloud.database({ throwOnNotFound: false, }); const _ = db.command; exports.main = async (event) => { try { const transaction = await db.startTransaction(); const aaaRes = await transaction.collection("account").doc("aaa").get(); const bbbRes = await transaction.collection("account").doc("bbb").get(); if (aaaRes.data && bbbRes.data) { const updateAAARes = await transaction .collection("account") .doc("aaa") .update({ data: { amount: _.inc(-10), }, }); const updateBBBRes = await transaction .collection("account") .doc("bbb") .update({ data: { amount: _.inc(10), }, }); await transaction.commit(); return { success: true, aaaAccount: aaaRes.data.amount - 10, }; } else { await transaction.rollback(); return { success: false, error: `rollback`, rollbackCode: -100, }; } } catch (e) { console.error(`事务报错`, e); } }; 也就是说对于多用户同时操作(主要是写)数据库的并发处理问题,我们不仅可以使用原子更新,还可以使用事务。其中原子更新主要用户操作单个记录内的字段或单个记录里内嵌的数组对象里的字段,而事务则主要是用于跨记录和跨集合的处理。
2021-09-10