// 订阅消息管理器 - 微信小程序专用 // 处理订阅消息的申请、管理和发送 /** * 微信小程序订阅消息管理器 * 功能: * 1. 管理订阅消息模板 * 2. 请求用户订阅权限 * 3. 检查订阅状态 * 4. 智能订阅策略 * 5. 订阅数据统计 */ class SubscribeMessageManager { constructor() { this.isInitialized = false; // 订阅消息模板配置 this.templates = { // 新消息通知 newMessage: { id: 'template_id_for_new_message', // 需要在微信公众平台配置 name: '新消息通知', description: '当您收到新消息时通知您', keywords: ['发送人', '消息内容', '发送时间'], required: false, category: 'message' }, // 好友请求通知 friendRequest: { id: 'template_id_for_friend_request', name: '好友请求通知', description: '当有人申请添加您为好友时通知您', keywords: ['申请人', '申请时间', '验证消息'], required: false, category: 'social' }, // 系统通知 systemNotice: { id: 'template_id_for_system_notice', name: '系统通知', description: '重要的系统消息和公告', keywords: ['通知类型', '通知内容', '通知时间'], required: true, category: 'system' }, // 群聊消息 groupMessage: { id: 'template_id_for_group_message', name: '群聊消息通知', description: '当群聊中有新消息时通知您', keywords: ['群名称', '发送人', '消息内容'], required: false, category: 'message' }, // 活动提醒 activityReminder: { id: 'template_id_for_activity_reminder', name: '活动提醒', description: '重要活动和事件提醒', keywords: ['活动名称', '活动时间', '活动地点'], required: false, category: 'reminder' } }; // 订阅状态缓存 this.subscriptionStatus = new Map(); // 订阅策略配置 this.subscriptionStrategy = { // 自动请求订阅的场景 autoRequestScenes: [ 'first_message_received', 'first_friend_request', 'important_system_notice' ], // 批量请求的最大数量 maxBatchRequest: 3, // 请求间隔(毫秒) requestInterval: 24 * 60 * 60 * 1000, // 24小时 // 最大请求次数 maxRequestTimes: 3 }; this.init(); } // 初始化订阅消息管理器 async init() { if (this.isInitialized) return; console.log('📝 初始化订阅消息管理器...'); try { // 加载订阅状态 await this.loadSubscriptionStatus(); // 检查模板配置 this.validateTemplateConfig(); this.isInitialized = true; console.log('✅ 订阅消息管理器初始化完成'); } catch (error) { console.error('❌ 订阅消息管理器初始化失败:', error); } } // 请求订阅消息权限 async requestSubscription(templateKeys, options = {}) { try { console.log('📝 请求订阅消息权限:', templateKeys); // 验证模板键 const validTemplateKeys = this.validateTemplateKeys(templateKeys); if (validTemplateKeys.length === 0) { throw new Error('没有有效的模板键'); } // 获取模板ID const templateIds = validTemplateKeys.map(key => this.templates[key].id); // 检查请求频率限制 if (!this.canRequestSubscription(templateKeys)) { console.log('⏰ 请求频率受限,跳过订阅请求'); return { success: false, reason: 'rate_limited' }; } // 发起订阅请求 const result = await this.makeSubscriptionRequest(templateIds, options); // 更新订阅状态 await this.updateSubscriptionStatus(validTemplateKeys, result); // 记录请求历史 this.recordSubscriptionRequest(validTemplateKeys); return result; } catch (error) { console.error('❌ 请求订阅消息权限失败:', error); return { success: false, error: error.message }; } } // 发起订阅请求 async makeSubscriptionRequest(templateIds, options = {}) { return new Promise((resolve) => { wx.requestSubscribeMessage({ tmplIds: templateIds, success: (res) => { console.log('📝 订阅请求成功:', res); resolve({ success: true, result: res, acceptedCount: this.countAcceptedSubscriptions(res) }); }, fail: (error) => { console.error('❌ 订阅请求失败:', error); resolve({ success: false, error: error.errMsg || '订阅请求失败' }); } }); }); } // 统计接受的订阅数量 countAcceptedSubscriptions(result) { let count = 0; for (const templateId in result) { if (result[templateId] === 'accept') { count++; } } return count; } // 智能订阅策略 async smartSubscriptionRequest(scene, context = {}) { try { console.log('🧠 智能订阅策略:', scene); // 检查是否需要自动请求 if (!this.subscriptionStrategy.autoRequestScenes.includes(scene)) { console.log('📝 当前场景不需要自动请求订阅'); return; } // 根据场景选择合适的模板 const templateKeys = this.getTemplatesForScene(scene); if (templateKeys.length === 0) { console.log('📝 当前场景没有对应的模板'); return; } // 过滤已订阅的模板 const unsubscribedTemplates = templateKeys.filter(key => !this.isSubscribed(key) ); if (unsubscribedTemplates.length === 0) { console.log('📝 所有相关模板都已订阅'); return; } // 限制批量请求数量 const templatesToRequest = unsubscribedTemplates.slice( 0, this.subscriptionStrategy.maxBatchRequest ); // 显示友好的订阅引导 const shouldRequest = await this.showSubscriptionGuide(scene, templatesToRequest); if (!shouldRequest) { console.log('📝 用户取消订阅请求'); return; } // 发起订阅请求 return await this.requestSubscription(templatesToRequest, { scene }); } catch (error) { console.error('❌ 智能订阅策略执行失败:', error); } } // 根据场景获取模板 getTemplatesForScene(scene) { switch (scene) { case 'first_message_received': return ['newMessage', 'groupMessage']; case 'first_friend_request': return ['friendRequest']; case 'important_system_notice': return ['systemNotice']; default: return []; } } // 显示订阅引导 async showSubscriptionGuide(scene, templateKeys) { const templateNames = templateKeys.map(key => this.templates[key].name); return new Promise((resolve) => { wx.showModal({ title: '消息通知', content: `为了及时通知您重要消息,建议开启以下通知:\n${templateNames.join('、')}`, confirmText: '开启通知', cancelText: '暂不开启', success: (res) => { resolve(res.confirm); }, fail: () => { resolve(false); } }); }); } // 检查是否可以请求订阅 canRequestSubscription(templateKeys) { const now = Date.now(); for (const key of templateKeys) { const lastRequest = this.getLastRequestTime(key); const requestCount = this.getRequestCount(key); // 检查请求间隔 if (lastRequest && (now - lastRequest) < this.subscriptionStrategy.requestInterval) { return false; } // 检查请求次数 if (requestCount >= this.subscriptionStrategy.maxRequestTimes) { return false; } } return true; } // 验证模板键 validateTemplateKeys(templateKeys) { const keys = Array.isArray(templateKeys) ? templateKeys : [templateKeys]; return keys.filter(key => this.templates[key]); } // 验证模板配置 validateTemplateConfig() { for (const [key, template] of Object.entries(this.templates)) { if (!template.id || template.id.startsWith('template_id_')) { console.warn(`⚠️ 模板 ${key} 的ID未配置或为占位符`); } } } // 检查订阅状态 isSubscribed(templateKey) { return this.subscriptionStatus.get(templateKey) === 'accept'; } // 获取所有订阅状态 getAllSubscriptionStatus() { const status = {}; for (const key in this.templates) { status[key] = { template: this.templates[key], subscribed: this.isSubscribed(key), lastRequest: this.getLastRequestTime(key), requestCount: this.getRequestCount(key) }; } return status; } // 更新订阅状态 async updateSubscriptionStatus(templateKeys, result) { if (!result.success || !result.result) return; for (const key of templateKeys) { const templateId = this.templates[key].id; const status = result.result[templateId]; if (status) { this.subscriptionStatus.set(key, status); } } await this.saveSubscriptionStatus(); } // 记录请求历史 recordSubscriptionRequest(templateKeys) { const now = Date.now(); const requestHistory = this.getRequestHistory(); for (const key of templateKeys) { if (!requestHistory[key]) { requestHistory[key] = { times: [], count: 0 }; } requestHistory[key].times.push(now); requestHistory[key].count++; } this.saveRequestHistory(requestHistory); } // 获取请求历史 getRequestHistory() { try { return wx.getStorageSync('subscriptionRequestHistory') || {}; } catch (error) { console.error('❌ 获取请求历史失败:', error); return {}; } } // 保存请求历史 saveRequestHistory(history) { try { wx.setStorageSync('subscriptionRequestHistory', history); } catch (error) { console.error('❌ 保存请求历史失败:', error); } } // 获取最后请求时间 getLastRequestTime(templateKey) { const history = this.getRequestHistory(); const keyHistory = history[templateKey]; if (keyHistory && keyHistory.times.length > 0) { return Math.max(...keyHistory.times); } return null; } // 获取请求次数 getRequestCount(templateKey) { const history = this.getRequestHistory(); return history[templateKey]?.count || 0; } // 加载订阅状态 async loadSubscriptionStatus() { try { const status = wx.getStorageSync('subscriptionStatus') || {}; this.subscriptionStatus = new Map(Object.entries(status)); } catch (error) { console.error('❌ 加载订阅状态失败:', error); } } // 保存订阅状态 async saveSubscriptionStatus() { try { const status = Object.fromEntries(this.subscriptionStatus); wx.setStorageSync('subscriptionStatus', status); } catch (error) { console.error('❌ 保存订阅状态失败:', error); } } // 获取管理器状态 getStatus() { return { isInitialized: this.isInitialized, templates: this.templates, subscriptionStatus: Object.fromEntries(this.subscriptionStatus), strategy: this.subscriptionStrategy }; } // 重置订阅状态 reset() { this.subscriptionStatus.clear(); this.saveSubscriptionStatus(); // 清除请求历史 try { wx.removeStorageSync('subscriptionRequestHistory'); } catch (error) { console.error('❌ 清除请求历史失败:', error); } } } // 创建全局实例 const subscribeMessageManager = new SubscribeMessageManager(); module.exports = subscribeMessageManager;