/** * 章节权限管理器 * 统一管理章节权限判断、状态流转、异常处理 */ const app = getApp() class ChapterAccessManager { constructor() { this.accessStates = { UNKNOWN: 'unknown', FREE: 'free', LOCKED_NOT_LOGIN: 'locked_not_login', LOCKED_NOT_PURCHASED: 'locked_not_purchased', UNLOCKED_PURCHASED: 'unlocked_purchased', ERROR: 'error' } } /** * 拉取最新配置(价格等,免费以章节数据为准) */ async fetchLatestConfig() { try { const res = await app.request({ url: '/api/miniprogram/config', silent: true, timeout: 3000 }) if (res.success && res.prices) { return { prices: res.prices || { section: 1, fullbook: 9.9 } } } } catch (e) { console.warn('[AccessManager] 获取配置失败,使用默认配置:', e) } return { prices: { section: 1, fullbook: 9.9 } } } /** * 判断章节是否免费(统一:章节 isFree 或 price===0) */ isFreeFromChapterData(chapterData) { if (!chapterData) return false if (chapterData.isFree === true) return true if (chapterData.price !== undefined && chapterData.price === 0) return true return false } /** * 【核心方法】确定章节权限状态(统一以章节数据 isFree/price 为准) * @param {string} sectionId - 章节ID * @param {object} chapterData - 章节接口返回 { isFree, price, ... },免费 = isFree 或 price===0 * @returns {Promise} accessState */ async determineAccessState(sectionId, chapterData) { try { // 1. 检查是否免费(统一:章节 isFree 或 price===0) if (this.isFreeFromChapterData(chapterData)) { console.log('[AccessManager] 免费章节:', sectionId) return this.accessStates.FREE } // 2. 检查是否登录 const userId = app.globalData.userInfo?.id if (!userId) { console.log('[AccessManager] 未登录,需要登录:', sectionId) return this.accessStates.LOCKED_NOT_LOGIN } // 3. 请求服务端校验是否已购买(带重试) const res = await this.requestWithRetry( `/api/miniprogram/user/check-purchased?userId=${encodeURIComponent(userId)}&type=section&productId=${encodeURIComponent(sectionId)}`, { timeout: 5000 }, 2 // 最多重试2次 ) if (res.success && res.data?.isPurchased) { console.log('[AccessManager] 已购买:', sectionId, res.data.reason) // 同步更新本地缓存(仅用于展示,不作权限依据) this.syncLocalCache(sectionId, res.data) return this.accessStates.UNLOCKED_PURCHASED } console.log('[AccessManager] 未购买:', sectionId) return this.accessStates.LOCKED_NOT_PURCHASED } catch (error) { console.error('[AccessManager] 权限判断失败:', error) // 网络/服务端错误 → 保守策略:返回错误状态 return this.accessStates.ERROR } } /** * 带重试的请求 */ async requestWithRetry(url, options = {}, maxRetries = 3) { let lastError = null for (let i = 0; i < maxRetries; i++) { try { const res = await app.request(url, options) return res } catch (e) { lastError = e console.warn(`[AccessManager] 第 ${i+1} 次请求失败:`, url, e.message) // 如果不是最后一次,等待后重试(指数退避) if (i < maxRetries - 1) { await this.sleep(1000 * (i + 1)) } } } throw lastError } /** * 同步更新本地购买缓存(仅用于展示,不作权限依据) */ syncLocalCache(sectionId, purchaseData) { if (purchaseData.reason === 'has_full_book') { app.globalData.hasFullBook = true } if (!app.globalData.purchasedSections.includes(sectionId)) { app.globalData.purchasedSections = [...app.globalData.purchasedSections, sectionId] } // 更新 storage const userInfo = app.globalData.userInfo || {} userInfo.hasFullBook = app.globalData.hasFullBook userInfo.purchasedSections = app.globalData.purchasedSections wx.setStorageSync('userInfo', userInfo) } /** * 刷新用户购买状态(从 orders 表拉取最新) */ async refreshUserPurchaseStatus() { const userId = app.globalData.userInfo?.id if (!userId) return try { const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${encodeURIComponent(userId)}`) if (res.success && res.data) { app.globalData.hasFullBook = res.data.hasFullBook || false app.globalData.purchasedSections = res.data.purchasedSections || [] const userInfo = app.globalData.userInfo || {} userInfo.hasFullBook = res.data.hasFullBook userInfo.purchasedSections = res.data.purchasedSections wx.setStorageSync('userInfo', userInfo) console.log('[AccessManager] 购买状态已刷新:', { hasFullBook: res.data.hasFullBook, purchasedCount: res.data.purchasedSections.length }) } } catch (e) { console.error('[AccessManager] 刷新购买状态失败:', e) } } /** * 获取状态对应的用户提示文案 */ getStateMessage(accessState) { const messages = { [this.accessStates.UNKNOWN]: '加载中...', [this.accessStates.FREE]: '免费阅读', [this.accessStates.LOCKED_NOT_LOGIN]: '登录后继续阅读', [this.accessStates.LOCKED_NOT_PURCHASED]: '购买后继续阅读', [this.accessStates.UNLOCKED_PURCHASED]: '已解锁', [this.accessStates.ERROR]: '网络异常,请重试' } return messages[accessState] || '未知状态' } /** * 判断是否可访问全文 */ canAccessFullContent(accessState) { return [this.accessStates.FREE, this.accessStates.UNLOCKED_PURCHASED].includes(accessState) } /** * 工具:延迟 */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } } // 导出单例 const accessManager = new ChapterAccessManager() export default accessManager