# 微信小程序订阅消息状态检查解决方案 ## 问题背景 在微信小程序开发中,订阅消息是一种重要的用户触达方式。然而,开发者经常面临一个问题:如何准确地检测用户是否订阅了特定的模板消息?当用户在设置中手动关闭订阅权限时,如何在前端及时反映这一状态变化? 传统的做法是依赖本地存储来记录用户订阅状态,但这存在明显的缺陷:当用户在微信设置中手动关闭订阅权限时,应用无法感知这一变化,导致界面上显示的状态与实际状态不一致。 ## 解决方案 ### 核心API:uni.getSetting withSubscriptions参数 微信小程序提供了检查订阅消息状态的官方API,关键在于使用 `withSubscriptions: true` 参数: ```javascript uni.getSetting({ withSubscriptions: true, // 关键参数 success(res) { console.log(res.subscriptionsSetting); // 包含 mainSwitch 和 itemSettings 字段 } }); ``` ### subscriptionsSetting结构说明 返回的 [subscriptionsSetting](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L150-L150) 对象包含以下重要字段: 1. **[mainSwitch](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L151-L151)**: Boolean类型,表示订阅消息的总开关 2. **[itemSettings](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L152-L152)**: 对象类型,包含每个模板ID的具体状态 - 模板ID作为键名 - 状态值包括:'accept'(接受)、'reject'(拒绝)、'ban'(封禁) ### 实现步骤 1. 在页面加载时调用检查函数 2. 使用 `withSubscriptions: true` 参数调用 [getSetting](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L4363-L4363) 3. 解析 [subscriptionsSetting](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L150-L150) 中的 [mainSwitch](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L151-L151) 和 [itemSettings](file:///D:/慢病APP/uniapp-ts/node_modules/@dcloudio/types/uni-app.d.ts#L152-L152) 4. 根据状态更新本地存储和界面显示 ### 代码示例 ```javascript const checkSubscriptionStatus = () => { if (typeof uni.getSetting === 'function') { uni.getSetting({ withSubscriptions: true, success: (res) => { if (res.subscriptionsSetting) { // 检查主开关 const mainSwitch = res.subscriptionsSetting.mainSwitch; // 检查特定模板状态 const itemSettings = res.subscriptionsSetting.itemSettings || {}; const templateStatus = itemSettings[TEMPLATE_ID]; // 根据状态更新本地存储和UI // 将 templateStatus !== 'accept' 视为未授权(例如 'reject'、'ban' 或 undefined) if (mainSwitch === false || templateStatus !== 'accept') { // 更新为未订阅状态(将状态保存在 patientReminders.notificationsEnabled) notificationsEnabled.value = false; try { const reminders = uni.getStorageSync('patientReminders') || { bloodPressureReminder: { enabled: false, times: [] }, bloodSugarReminder: { enabled: false, times: [] }, heartRateReminder: { enabled: false, times: [] }, medicationReminder: { enabled: false, times: [] }, notificationsEnabled: false, subscriptionAvailable: true }; reminders.notificationsEnabled = false; uni.setStorageSync('patientReminders', reminders); } catch (e) {} } else if (mainSwitch === true && templateStatus === 'accept') { // 更新为已订阅状态(保存到 patientReminders) notificationsEnabled.value = true; try { const reminders = uni.getStorageSync('patientReminders') || { bloodPressureReminder: { enabled: false, times: [] }, bloodSugarReminder: { enabled: false, times: [] }, heartRateReminder: { enabled: false, times: [] }, medicationReminder: { enabled: false, times: [] }, notificationsEnabled: false, subscriptionAvailable: true }; reminders.notificationsEnabled = true; uni.setStorageSync('patientReminders', reminders); } catch (e) {} } } }, fail: (err) => { console.error('Failed to get user settings:', err); } }); } }; ``` ## 注意事项 1. **API兼容性**:确保目标平台支持 `withSubscriptions` 参数 2. **状态同步**:定期检查状态以确保与用户设置同步 3. **用户体验**:当检测到订阅状态变更时,应给予用户适当提示 4. **错误处理**:妥善处理API调用失败的情况 ## 总结 通过使用 `uni.getSetting` 配合 `withSubscriptions: true` 参数,我们可以准确地检测用户对订阅消息的设置,实现前端独立的状态管理,无需依赖后端服务。这种方法能够实时反映用户在微信设置中的操作,提升用户体验和应用的准确性。 ## 本项目中已做的同步变更 在本仓库的 `src/pages/patient/health/reminder.vue` 中,我们把原来分散的订阅检查逻辑合并为一个统一的函数 `checkSubscriptionStatus`,并为其新增了一个 `passive` 参数,用来区分“被动检查”和“主动检查”的行为: - `checkSubscriptionStatus(passive = false)` - passive = false(默认,主动检查):用于用户交互触发的场景(例如用户在设置页返回后、或用户明确请求刷新订阅状态时)。在此模式下: - 如果 `mainSwitch === false` 或模板状态非 `accept`(例如 `reject`、`ban` 或未返回模板状态 `undefined`),将把 `notificationsEnabled` 设为 `false`、显示权限引导(`showPermissionGuide = true`,当主开关关闭时),并保存本地状态。 - 如果 `mainSwitch === true` 且模板状态为 `accept`,将把 `notificationsEnabled` 设为 `true`、关闭权限引导,并保存本地状态。 - passive = true(被动检查):用于生命周期或自动触发的场景(例如页面 `onShow` 时自动检测)。在此模式下: - 仅在检测到 `mainSwitch === false` 或模板状态非 `accept`(例如 `reject`、`ban` 或未返回模板状态 `undefined`)时,才会把 `notificationsEnabled` 设为 `false` 并保存本地状态;不会在检测到 `accept` 时把本地开关置为 `true`,也不会显示权限引导(遵循“被动不打扰用户”的原则)。 修改要点: - 将 `onShow` 中原本重复的 `uni.getSetting({...})` 代码替换为 `checkSubscriptionStatus(true)`,实现代码复用和行为统一(onShow 执行被动检查)。 - `openSettings` 中在用户从系统设置返回时仍然调用 `checkSubscriptionStatus()`(默认主动模式),以便在用户在系统设置中开启权限后,将本地状态同步为已开启并关闭权限引导。 示例调用: ```javascript // 页面返回前台时(被动检查) checkSubscriptionStatus(true) // 用户在设置页返回时(主动检查) checkSubscriptionStatus() ``` 为什么这样做: - 被动检查(如 onShow)不应在检测到接受时主动修改用户在界面上的显式选择,避免悖论或意外更改;但当检测到权限被明确关闭时,应被动同步并关闭本地功能,防止应用继续假定有推送能力。 - 主动检查(如用户明确进入设置或手动刷新)则可以安全地将本地状态与系统设置对齐,并在必要时显示权限引导帮助用户恢复权限。 已在代码中完成该改动,且相关逻辑已保存到 `reminder.vue` 中的注释与实现里。