chore: 以本地工作区为准全量快照同步 GitHub
包含小程序、管理端、soul-api、脚本与静态资源等当前本地全部已跟踪与新增文件(排除 .DS_Store 与 .obsidian)。 Made-with: Cursor
This commit is contained in:
@@ -412,6 +412,90 @@ function checkRule_Withdraw(rules) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览 N 章后弹出 365 读书会支付引导
|
||||
* 规则匹配:triggerConditions 包含 view_n_chapters_pay365 或 browse_n_chapters
|
||||
* 阈值来自 actionConfig.chapterThreshold(默认 3)
|
||||
* actionConfig.revealMode:modal 进入章节即延时弹窗;anchor 仅展示锚点条,用户点击后再弹窗/支付
|
||||
* actionConfig.anchorLabel:锚点条文案(可选)
|
||||
*/
|
||||
function findRuleByTriggerCondition(rules, conditionKey) {
|
||||
return rules.find(r => {
|
||||
if (r.completed) return false
|
||||
const tc = r.triggerConditions
|
||||
if (Array.isArray(tc)) return tc.includes(conditionKey)
|
||||
if (typeof tc === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(tc)
|
||||
if (Array.isArray(parsed)) return parsed.includes(conditionKey)
|
||||
} catch (_) {}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function parseActionConfig(rule) {
|
||||
if (!rule) return {}
|
||||
const ac = rule.actionConfig
|
||||
if (ac && typeof ac === 'object' && !Array.isArray(ac)) return ac
|
||||
if (typeof ac === 'string' && ac.trim()) {
|
||||
try {
|
||||
const p = JSON.parse(ac)
|
||||
if (p && typeof p === 'object') return p
|
||||
} catch (_) {}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
function checkRule_ViewNChaptersPay365(rules) {
|
||||
const rule = findRuleByTriggerCondition(rules, 'view_n_chapters_pay365')
|
||||
|| findRuleByTriggerCondition(rules, 'browse_n_chapters')
|
||||
if (!rule) return null
|
||||
|
||||
const user = getUserInfo()
|
||||
if (!user.id) return null
|
||||
|
||||
const app = getAppInstance()
|
||||
if (!app) return null
|
||||
if (app.globalData.hasFullBook || app.globalData.isVip) return null
|
||||
|
||||
const ac = parseActionConfig(rule)
|
||||
const threshold = (Number(ac.chapterThreshold) > 0) ? Number(ac.chapterThreshold) : 3
|
||||
const fullRead =
|
||||
typeof app.getReadCount === 'function' ? app.getReadCount() : app.globalData.readCount || 0
|
||||
const browseDistinct =
|
||||
typeof app.getBrowseDistinctChapterCount === 'function' ? app.getBrowseDistinctChapterCount() : 0
|
||||
const readCount = Math.max(Number(fullRead) || 0, Number(browseDistinct) || 0)
|
||||
if (readCount < threshold) return null
|
||||
|
||||
if (isInCooldown('view_n_pay365')) return null
|
||||
setCooldown('view_n_pay365')
|
||||
|
||||
const ctaKind = String(ac.ctaKind || 'pay365')
|
||||
const amount = ctaKind === 'pay365' ? 365 : null
|
||||
|
||||
const rm = ac.revealMode
|
||||
let revealMode = rm === 'modal' || rm === 'anchor' ? rm : null
|
||||
if (!revealMode) {
|
||||
revealMode = 'modal'
|
||||
}
|
||||
const anchorLabel = trimStr(ac.anchorLabel)
|
||||
|
||||
return {
|
||||
ruleId: 'view_n_pay365',
|
||||
serverRuleId: rule.id,
|
||||
title: rule.title || '加入 365 读书会',
|
||||
message: rule.description || `你已阅读 ${readCount} 个章节,加入 365 读书会可解锁全部内容,一年内不限次阅读。`,
|
||||
confirmText: amount ? `¥${amount} 立即加入` : '去了解',
|
||||
cancelText: '再看看',
|
||||
action: ctaKind === 'pay365' ? 'pay365' : 'navigate',
|
||||
target: null,
|
||||
amount,
|
||||
revealMode,
|
||||
anchorLabel,
|
||||
}
|
||||
}
|
||||
|
||||
function checkRulesSync(scene, rules) {
|
||||
const user = getUserInfo()
|
||||
if (!user.id) return null
|
||||
@@ -431,6 +515,8 @@ function checkRulesSync(scene, rules) {
|
||||
return checkAvatarNicknameGuides(rules) || checkRule_ShareAfter5Chapters(rules) || checkRule_BindWechat(rules) || checkRule_Withdraw(rules)
|
||||
case 'before_join_party':
|
||||
return checkRule_JoinParty(rules)
|
||||
case 'chapter_read':
|
||||
return checkRule_ViewNChaptersPay365(rules)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@@ -452,6 +538,12 @@ function executeRule(rule, pageInstance) {
|
||||
if (typeof pageInstance.showPhoneBinding === 'function') {
|
||||
pageInstance.showPhoneBinding()
|
||||
}
|
||||
} else if (rule.action === 'pay365' && pageInstance) {
|
||||
if (typeof pageInstance.handlePurchaseFullBook === 'function') {
|
||||
pageInstance.handlePurchaseFullBook()
|
||||
} else if (typeof pageInstance.processPayment === 'function') {
|
||||
pageInstance.processPayment('fullbook', null, rule.amount || 365)
|
||||
}
|
||||
}
|
||||
if (rule.serverRuleId) {
|
||||
markRuleCompleted(rule.serverRuleId)
|
||||
@@ -485,8 +577,34 @@ async function checkAndExecute(scene, pageInstance) {
|
||||
rule = checkRulesSync(scene, rules)
|
||||
}
|
||||
if (rule) {
|
||||
if (
|
||||
scene === 'chapter_read' &&
|
||||
rule.ruleId === 'view_n_pay365' &&
|
||||
rule.revealMode === 'anchor' &&
|
||||
rule.action === 'pay365' &&
|
||||
pageInstance &&
|
||||
typeof pageInstance.onPay365MarketingAnchor === 'function'
|
||||
) {
|
||||
setTimeout(() => pageInstance.onPay365MarketingAnchor(rule), 800)
|
||||
return
|
||||
}
|
||||
setTimeout(() => executeRule(rule, pageInstance), 800)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkRules: checkRulesSync, executeRule, checkAndExecute, loadRules, markRuleCompleted }
|
||||
/** 阅读页同步营销态:与 chapter_read 同一套条件,不自动弹窗 */
|
||||
async function getChapterReadMarketingRule() {
|
||||
const rules = await loadRules()
|
||||
const user = getUserInfo()
|
||||
if (!user.id) return null
|
||||
return checkRulesSync('chapter_read', rules)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkRules: checkRulesSync,
|
||||
executeRule,
|
||||
checkAndExecute,
|
||||
loadRules,
|
||||
markRuleCompleted,
|
||||
getChapterReadMarketingRule,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user