chore: 以本地工作区为准全量快照同步 GitHub

包含小程序、管理端、soul-api、脚本与静态资源等当前本地全部已跟踪与新增文件(排除 .DS_Store 与 .obsidian)。

Made-with: Cursor
This commit is contained in:
卡若
2026-04-13 11:49:38 +08:00
parent 83375cc388
commit b5f5180654
142 changed files with 10684 additions and 2670 deletions

View File

@@ -412,6 +412,90 @@ function checkRule_Withdraw(rules) {
}
}
/**
* 浏览 N 章后弹出 365 读书会支付引导
* 规则匹配triggerConditions 包含 view_n_chapters_pay365 或 browse_n_chapters
* 阈值来自 actionConfig.chapterThreshold默认 3
* actionConfig.revealModemodal 进入章节即延时弹窗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,
}