diff --git a/pages/add-anniversary/add-anniversary.js b/pages/add-anniversary/add-anniversary.js index d6f7c34..ba8ce05 100644 --- a/pages/add-anniversary/add-anniversary.js +++ b/pages/add-anniversary/add-anniversary.js @@ -3,6 +3,7 @@ const storage = require('../../utils/storage') const dateUtils = require('../../utils/date') const sync = require('../../utils/sync') const lunar = require('../../utils/lunar') +const subscribe = require('../../utils/subscribe') Page({ data: { @@ -373,7 +374,7 @@ Page({ requestSubscribe() { return new Promise((resolve, reject) => { wx.requestSubscribeMessage({ - tmplIds: ['6J7Stt-lu7DKU6jblJ0nZGq_D81z5glnksf7qWfy5Yw'], + tmplIds: [subscribe.TEMPLATE_ID], success: (res) => { console.log('订阅消息授权结果:', res) resolve(res) diff --git a/utils/api.js b/utils/api.js index 72dacbc..6615afa 100644 --- a/utils/api.js +++ b/utils/api.js @@ -72,4 +72,9 @@ function person(action, data) { return request({ url: '/api/person', data: { action, data } }) } -module.exports = { request, login, anniversary, person, BASE_URL } +// 订阅额度:grant 上报授权,get 查询余额与风险 +function subscribe(action, data) { + return request({ url: '/api/subscribe', data: { action, data } }) +} + +module.exports = { request, login, anniversary, person, subscribe, BASE_URL } diff --git a/utils/subscribe.js b/utils/subscribe.js new file mode 100644 index 0000000..ef25a27 --- /dev/null +++ b/utils/subscribe.js @@ -0,0 +1,68 @@ +/** + * 订阅消息统一入口 + * + * ⚠️ 最重要的约束:wx.requestSubscribeMessage 和 wx.openSetting 必须由真实点击手势 + * 直接触发(基础库 2.8.2+),且调用前不能有 await 等异步操作,否则手势上下文丢失、 + * 调用必然失败(报 fail can only be invoked by user TAP gesture)。 + * 所以本模块的 requestAndReport 必须在 bindtap 处理函数里同步调用, + * 用户状态要提前用 getStatus 查好缓存起来,不能现查现用。 + */ + +const sync = require('./sync') + +const TEMPLATE_ID = '6J7Stt-lu7DKU6jblJ0nZGq_D81z5glnksf7qWfy5Yw' + +/** + * 查询用户当前的订阅设置状态 + * itemSettings 只包含用户勾选过「总是保持以上选择」的模板,据此可判断调用会不会弹窗。 + * @returns {Promise} + * silent — 已长期同意,调用不弹窗,可在任意点击上搭车静默补额度 + * willPrompt — 没勾过,调用会弹窗,只在用户主动点击时才调 + * rejected — 已长期拒绝或被封禁,再调也没用,需引导去设置页 + * mainSwitchOff — 通知总开关关闭,需引导去设置页 + * unknown — 查询失败,按 willPrompt 保守处理 + */ +function getStatus() { + return new Promise((resolve) => { + wx.getSetting({ + withSubscriptions: true, + success: (res) => { + const s = res.subscriptionsSetting || {} + if (s.mainSwitch === false) return resolve('mainSwitchOff') + const item = s.itemSettings && s.itemSettings[TEMPLATE_ID] + if (item === 'accept') return resolve('silent') + if (item === 'reject' || item === 'ban') return resolve('rejected') + resolve('willPrompt') + }, + fail: () => resolve('unknown') + }) + }) +} + +/** + * 请求订阅并把新增额度上报后端 + * 必须在 tap handler 中同步调用(本函数第一条语句就是 wx.requestSubscribeMessage) + * @returns {Promise} 用户是否同意 + */ +function requestAndReport() { + return new Promise((resolve) => { + wx.requestSubscribeMessage({ + tmplIds: [TEMPLATE_ID], + success: (res) => { + const accepted = res[TEMPLATE_ID] === 'accept' + if (!accepted) return resolve(false) + // 走同步队列上报:失败会自动入队,下次启动 flush。 + // 不能静默吞掉——用户已经同意、微信侧额度确实 +1 了,我们漏记会导致余额低估, + // 进而白白跳过本可以发出去的提醒。 + sync.syncOrEnqueue({ kind: 'subscribe', action: 'grant', data: { count: 1 } }) + .then(() => resolve(true)) + }, + fail: (err) => { + console.warn('[subscribe] 订阅失败', err) + resolve(false) + } + }) + }) +} + +module.exports = { TEMPLATE_ID, getStatus, requestAndReport } diff --git a/utils/sync.js b/utils/sync.js index 03cd601..c8b1e5d 100644 --- a/utils/sync.js +++ b/utils/sync.js @@ -24,6 +24,7 @@ function enqueue(item) { async function dispatch(item) { if (item.kind === 'person') return api.person(item.action, item.data) if (item.kind === 'anniversary') return api.anniversary(item.action, item.data) + if (item.kind === 'subscribe') return api.subscribe(item.action, item.data) throw new Error('unknown sync kind: ' + item.kind) }