diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index ea5933f3..8a8359b1 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -39,6 +39,14 @@ function getContentParseConfig() { } /** 补全 mentionDisplay,避免旧数据无字段;昵称去空白防「@ 名」 */ +/** 付费墙「已阅读 x%」:与章节接口 previewPercent 一致(未全文解锁时),缺省 20 */ +function normalizePreviewPercent(res) { + const p = res && res.previewPercent + const n = typeof p === 'number' ? Math.round(p) : parseInt(String(p), 10) + if (!isNaN(n) && n >= 1 && n <= 100) return n + return 20 +} + function normalizeMentionSegments(segments) { if (!Array.isArray(segments)) return [] return segments.map((row) => { @@ -85,6 +93,8 @@ Page({ // 阅读进度 readingProgress: 0, + /** 未解锁时付费墙展示:与 /api/miniprogram/book/chapter/* 的 previewPercent 对齐 */ + previewPercent: 20, showPaywall: false, // 上一篇/下一篇 @@ -425,7 +435,8 @@ Page({ contentSegments: normalizeMentionSegments(segments), previewParagraphs: lines.slice(0, previewCount), partTitle: res.partTitle || '', - chapterTitle: res.chapterTitle || '' + chapterTitle: res.chapterTitle || '', + previewPercent: normalizePreviewPercent(res), } if (res.mid) updates.sectionMid = res.mid this.setData(updates) @@ -451,7 +462,8 @@ Page({ contentSegments: normalizeMentionSegments(segments), previewParagraphs: lines.slice(0, previewCount), partTitle: cached.partTitle || '', - chapterTitle: cached.chapterTitle || '' + chapterTitle: cached.chapterTitle || '', + previewPercent: normalizePreviewPercent(cached), }) app.touchRecentSection(id) console.log('[Read] 从本地缓存加载成功') @@ -571,7 +583,8 @@ Page({ previewParagraphs: lines.slice(0, previewCount), partTitle: res.partTitle || '', // 导航栏、分享等使用的文章标题,同样统一为 sectionTitle - chapterTitle: sectionTitle + chapterTitle: sectionTitle, + previewPercent: normalizePreviewPercent(res), }) }, diff --git a/miniprogram/pages/read/read.wxml b/miniprogram/pages/read/read.wxml index 638654dd..2fcb929c 100644 --- a/miniprogram/pages/read/read.wxml +++ b/miniprogram/pages/read/read.wxml @@ -140,7 +140,7 @@ 解锁完整内容 - 已预览部分内容,登录并支付 ¥1 后阅读全文 + 已阅读{{previewPercent}}%,登录并支付 ¥{{section && section.price != null ? section.price : sectionPrice}} 后阅读全文 购买本章 @@ -212,7 +212,7 @@ 解锁完整内容 - 已阅读50%,购买后继续阅读 + 已阅读{{previewPercent}}%,购买后继续阅读 购买本章 diff --git a/soul-api/internal/handler/book.go b/soul-api/internal/handler/book.go index 705dfbc8..11bd0d40 100644 --- a/soul-api/internal/handler/book.go +++ b/soul-api/internal/handler/book.go @@ -710,14 +710,14 @@ func findChapterAndRespond(c *gin.Context, whereFn func(*gorm.DB) *gorm.DB) { // 确定返回的 content:免费直接返回,付费须校验购买权限 userID := c.Query("userId") isPremium := ch.EditionPremium != nil && *ch.EditionPremium + hasFullAccess := isFree || checkUserChapterAccess(db, userID, ch.ID, isPremium) var returnContent string - if isFree { - returnContent = ch.Content - } else if checkUserChapterAccess(db, userID, ch.ID, isPremium) { + var unpaidPreviewPercent int // 未解锁时试读比例(system_config.unpaid_preview_percent);已解锁时不写入响应 + if hasFullAccess { returnContent = ch.Content } else { - percent := getUnpaidPreviewPercent(db) - returnContent = previewContent(ch.Content, percent) + unpaidPreviewPercent = getUnpaidPreviewPercent(db) + returnContent = previewContent(ch.Content, unpaidPreviewPercent) } // data 中的 content 必须与外层 content 一致,避免泄露完整内容给未授权用户 @@ -728,15 +728,19 @@ func findChapterAndRespond(c *gin.Context, whereFn func(*gorm.DB) *gorm.DB) { chForResponse.SectionTitle = sanitizeChapterTitleField(ch.SectionTitle) out := gin.H{ - "success": true, - "data": chForResponse, - "content": returnContent, - "chapterTitle": chForResponse.ChapterTitle, - "partTitle": chForResponse.PartTitle, - "id": ch.ID, - "mid": ch.MID, - "sectionTitle": chForResponse.SectionTitle, - "isFree": isFree, + "success": true, + "data": chForResponse, + "content": returnContent, + "chapterTitle": chForResponse.ChapterTitle, + "partTitle": chForResponse.PartTitle, + "id": ch.ID, + "mid": ch.MID, + "sectionTitle": chForResponse.SectionTitle, + "isFree": isFree, + "hasFullAccess": hasFullAccess, + } + if !hasFullAccess { + out["previewPercent"] = unpaidPreviewPercent } // 文章详情内直接输出上一篇/下一篇,省去单独请求 if list := getOrderedChapterList(); len(list) > 0 {