- 一眼告诉你什么是订阅消息了,看完就懂订阅消息。
消息通知有两种: 一、A的动作后,发消息给A自己,这种容易解决,不多说明; 二、A动作后,发消息给B(比如管理员、店家、楼主),如何保证B收到消息?这种是本方案要解决的问题。 一张图片一眼告诉你什么是订阅消息,产品经理的设计UI居然让人一眼就知道订阅消息是什么玩意。 [图片] 用户 B (管理员、商家、组长、楼主)在知道订阅数不足后,打开小程序来续订阅数,否则没法收到订阅消息。 [图片] 补充一: 关于勾选按钮,请注意话述是:“总是保持以上选择,不再询问”,而不是:“总是同意接收订阅消息”,不要幻想就成了永久性订阅消息; 相当于你打电话订外卖,对店家说“老样子”,店家只会马上送一次外卖,而不是会以后每天自动给你送外卖了。 勾选和不勾选的区别是什么呢? 区别仅仅是:不勾选时,必须点击订阅10次,弹窗10次;勾选后,仍然必须点击订阅10次,但是不弹窗。无论如何“订阅”这个点击n次的动作少不了。 补充二: 一旦勾选后,就不可逆了,没有任何办法恢复或取消勾选了,除非你小程序MP后台换一次消息模板号(删除模板,重新添加一次)。 补充三: 关于如何保存订阅数。 保存在数据库中,笔者用的是云开发,数据库表user结构如下: { _id:'openid1', nickName:'老张', msg:{ "tempId1":5, "tempId2":7, } } 补充四: 关于如何获取订阅数。两种方式: 一、wx.requestSubscribeMessage的回调success里获取; 二、消息推送机制获取;https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html
2022-09-21 - #小程序能力 小程序内如何关注公众号思路
方式一:官方组件->official-account 方式二:官方组件->webview打开公众号文章引导用户关注 方式三:官方组件->button open-type='contact' 打开客服窗口给用户推送关注公众号引导 方式四、小程序内嵌公众号名称或者图片 方式一:官方组件->official-account https://developers.weixin.qq.com/miniprogram/dev/component/official-account.html 前提:当用户扫小程序码打开小程序时,开发者可在小程序内配置公众号关注组件,方便用户快捷关注公众号,可嵌套在原生组件内。 Tips 使用组件前,需前往小程序后台,在“设置”->“关注公众号”中设置要展示的公众号。注:设置的公众号需与小程序主体一致。在一个小程序的生命周期内,只有从以下场景进入小程序,才具有展示引导关注公众号组件的能力:当小程序从扫小程序码场景(场景值1047,场景值1124)打开时当小程序从聊天顶部场景(场景值1089)中的「最近使用」内打开时,若小程序之前未被销毁,则该组件保持上一次打开小程序时的状态当从其他小程序返回小程序(场景值1038)时,若小程序之前未被销毁,则该组件保持上一次打开小程序时的状态为便于开发者调试,基础库 2.7.3 版本起开发版小程序增加以下场景展示公众号组件:开发版小程序从扫二维码(场景值 1011)打开 — 体验版小程序打开组件限定最小宽度为300px,高度为定值84px。每个页面只能配置一个该组件。 方式二:官方组件->webview打开公众号文章引导用户关注 小程序中使用webview组件打开一篇要关注公众号的文章,引导用户点击公众号名称关注; 需设置:在公众号中关联小程序,否则不能打开公众号文章 [图片] 方式三:官方组件->button open-type='contact' 打开客服窗口给用户推送关注公众号引导 [图片] 技能get: 1.如果获取公众号历史连接:PC微信客户端获取 [图片] 2.快捷获取公众号二维码:https://open.weixin.qq.com/qr/code?username=dyh_mirsh 方式四、小程序内嵌公众号名称或者图片 图片:引导用户保存图片在微信扫一扫识别 名字:提供复制能力,引导用户复制到搜索框搜索; [图片]
2020-10-14 - 如何自己在小程序内做埋点数据统计
如何在自己小程序内做数据埋点 小程序后台已经有了较为完善的数据统计和基础的分析,但是功能还是比较基础的,通常我们对数据分析有较高的要求时,就需要自己做数据收集了。那小程序内如何做自动的数据埋点和手动埋点呢。 自动埋点 启动时间 (onLaunch) 系统信息 (systemInfo) 停留时长 (Page onHide - Page onShow) 来源 (query 参数) PV (Page onShow) UV (结合用户筛选PV) 预设点击数据收集 (onTap 等) 手动埋点 自定义点击收集数据 改造小程序生命周期 自动埋点是需要集成到底层内,不能对业务进行侵入,所以,我们需要改造小程序生命周期,在不同的生命周期内进行预设收集数据的功能。 对 [代码]App[代码] 进行重写 [代码]const oldApp = App; // 我们需要重写的方法 const appFn = ['onLaunch', 'onShow', 'onHide']; App = function (options) { let oldFuncs = {}; appFn.forEach((item) => { oldFuncs[item] = options[item] }) appFn.forEach((item) => { options[item] = function (options) { // todo 做各类数据收集 oldFuncs[item].apply(this, arguments) } }) oldApp.apply(this, arguments); }; [代码] 对 [代码]Page[代码] 重写 [代码]const oldPage = Page; const pageFn = ['onLoad', 'onShow', 'onHide', 'onUnload', 'onShareAppMessage', 'onAddToFavorites'] Page = function (options) { let oldFuncs = {}; pageFn.forEach((item) => { if (options[item]) { oldFuncs[item] = options[item] } }) pageFn.forEach((item) => { if (options[item]) { options[item] = function () { console.log('Page', item, ); // 收集各类数据 oldFuncs[item].apply(this, arguments) } } }) // 以下代码则是对除生命周期类的方法进行重写,做预设点击事件收集数据 const methods = getMethods(options); if (!!methods) { for (var i = 0, len = methods.length; i < len; i++) { clickProxy(options, methods[i]); } } oldPage.apply(this, arguments); } [代码] 对 [代码]Component[代码] 重写 [代码]const oldComponent = Component; Component = function (options) { // 对组建内 methods 进行重写预设点击事件埋点收集 Object.keys(options.methods).forEach((method) => { clickProxy(options.methods, method) }) oldComponent.apply(this, arguments); } [代码] 以上对 [代码]App[代码] [代码]Page[代码] [代码]Component[代码] 进行重写之后,在必要的地方,加入自己的上报代码. 以下完整代码 [代码] const mpHook = { data: 1, onLoad: 1, onShow: 1, onReady: 1, onPullDownRefresh: 1, onReachBottom: 1, onShareAppMessage: 1, onPageScroll: 1, onResize: 1, onTabItemTap: 1, onHide: 1, onUnload: 1, }; const oldApp = App; const oldPage = Page; const oldComponent = Component; const appFn = ['onLaunch', 'onShow', 'onHide'] App = function (options) { let oldFuncs = {}; appFn.forEach((item) => { oldFuncs[item] = options[item] }) appFn.forEach((item) => { options[item] = function (options) { console.log('App', item); // 收集各类数据 oldFuncs[item].apply(this, arguments) } }) oldApp.apply(this, arguments); }; const pageFn = ['onLoad', 'onShow', 'onHide', 'onUnload', 'onShareAppMessage', 'onAddToFavorites'] Page = function (options) { let oldFuncs = {}; pageFn.forEach((item) => { if (options[item]) { oldFuncs[item] = options[item] } }) pageFn.forEach((item) => { if (options[item]) { options[item] = function () { console.log('Page', item, ); // 收集各类数据 oldFuncs[item].apply(this, arguments) } } }) const methods = getMethods(options); if (!!methods) { for (var i = 0, len = methods.length; i < len; i++) { clickProxy(options, methods[i]); } } oldPage.apply(this, arguments); } Component = function (options) { Object.keys(options.methods).forEach((method) => { clickProxy(options.methods, method) }) oldComponent.apply(this, arguments); } function clickProxy(options, method) { const oldFunc = options[method]; options[method] = function () { const pages = getCurrentPages(); const currentPage = pages[pages.length - 1]; const pageQuery = currentPage.options || {}; const pagePath = currentPage.route; const res = oldFunc.apply(this, arguments); let prop = {}, type = ""; if (isObject(arguments[0])) { const current_target = arguments[0].currentTarget || {}; const dataset = current_target.dataset || {}; type = arguments[0]["type"]; prop["$event_type"] = type; prop["$event_timestamp"] = Date.now(); prop["$element_id"] = current_target.id; prop["$element_type"] = dataset["type"]; prop["$element_content"] = dataset["content"]; prop["$element_name"] = dataset["name"]; prop["$page_path"] = pagePath; prop["$page_quey"] = pageQuery; if (isObject(arguments[0].event_prop)) { prop = Object.assign(prop, arguments[0].event_prop); } } console.log('type', type) if (type) { // 可以对不同事件类型进行筛选是否需要收集 post(prop) } console.log(res); return res; }; } const getMethods = function (options) { let methods = []; for (let m in options) { if (typeof options[m] === "function" && !mpHook[m]) { methods.push(m); } } return methods; }; const isObject = function (obj) { if (obj === undefined || obj === null) { return false; } else { return toString.call(obj) == "[object Object]"; } }; const post = function (data) { console.log('data', data) // 提交数据时,可以在组合下 systeminfo 用户信息等相关信息 wx.request({ url: 'https://www.example.php', method: 'post', data }) } // 手动埋点的部分,自己在需要收集的地方调用相关方法收集数据 [代码]
2021-06-24 - 如何实现公众号被关注自动回复小程序卡片?
疑问:「公众号推送小程序卡片」的功能还未开放,为什么有部分公众号关注后可以直接向用户推送小程序卡片呢? 原理:用户关注微信公众号后,系统会向用户发送与该公众号相关联的小程序卡片。用户点击小程序卡片可以直接打开小程序页面。 猜想,这个功能到底有什么用? 提升用户体验:比其他入口(文章插入等),更快,更直观增强粉丝粘性:除了公众号内容,还有小程序丰富的形式展示,服务流量再次利用:打通公众号小程序之间的跳转,留存降低教育成本:无需铺垫,第一次关注就弹出丰富客服能力:通过客服消息自动回复小程序(关注后回复,关键词回复,收到消息回复等) 如何实现这个效果? 前提条件:公众号需认证,具体客服能力,以及关联过小程序 关注后自动回复——选择小程序卡片(填写卡片标题,小程序路径,小程序appid),保存,开启关注后自动回复按钮 [图片] 拓展阅读:更多公众号自动回复玩法
2020-04-14 - pc端生成二维码功能问题?
场景描述:一个pc端功能web系统,其中有一个功能是活动报名。需要生成一个让用户扫描的二维码参加活动。 问题:用户能直接扫码(第三方生成的二维码图片)图片,而不能引导用户关注公众号。 要解决的问题:通过扫码微信生成的二维码,而判断用户是否注册,未注册用户,跳转到公众号关注页面。 当前情况:注册了微信开放平台,拿到开放平台的Appid和Secret,在电脑端获得了access_token。 调取二维码生成接口。 文档页面:"https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html" “公众号->帐号管理->生成带参数的二维码”. 需要功能“如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。” 但返回请求,是{"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest rid: 5fXXXX"} 问题文档:“40001->获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口”. 问题来了:是不是我理解错了。这个生成二维码只可以微信端使用,而不是给pc端使用的? 那我PC端想生成一个带判断用户是否关注的二维码,应该怎么做? 请指教?
2020-09-16