180 lines
5.0 KiB
JavaScript
180 lines
5.0 KiB
JavaScript
|
|
/**
|
|||
|
|
* Soul创业派对 - 用户旅程规则引擎
|
|||
|
|
* 在关键页面自动检查用户状态,引导完善信息
|
|||
|
|
*
|
|||
|
|
* 规则逻辑:
|
|||
|
|
* 1. 注册后 → 引导填写头像和昵称
|
|||
|
|
* 2. 浏览付费章节 → 引导绑定手机号
|
|||
|
|
* 3. 完成匹配 → 引导填写 MBTI / 行业等
|
|||
|
|
* 4. 购买全书 → 引导填写完整信息(进 VIP 群)
|
|||
|
|
* 5. 累计浏览 5 章 → 引导分享推广
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const app = getApp()
|
|||
|
|
|
|||
|
|
const RULE_COOLDOWN_KEY = 'rule_engine_cooldown'
|
|||
|
|
const COOLDOWN_MS = 60 * 1000
|
|||
|
|
|
|||
|
|
function isInCooldown(ruleId) {
|
|||
|
|
try {
|
|||
|
|
const map = wx.getStorageSync(RULE_COOLDOWN_KEY) || {}
|
|||
|
|
const ts = map[ruleId]
|
|||
|
|
if (!ts) return false
|
|||
|
|
return Date.now() - ts < COOLDOWN_MS
|
|||
|
|
} catch { return false }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setCooldown(ruleId) {
|
|||
|
|
try {
|
|||
|
|
const map = wx.getStorageSync(RULE_COOLDOWN_KEY) || {}
|
|||
|
|
map[ruleId] = Date.now()
|
|||
|
|
wx.setStorageSync(RULE_COOLDOWN_KEY, map)
|
|||
|
|
} catch {}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getUserInfo() {
|
|||
|
|
return app.globalData.userInfo || {}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function checkRule_FillAvatar() {
|
|||
|
|
const user = getUserInfo()
|
|||
|
|
if (!user.id) return null
|
|||
|
|
const avatar = user.avatar || user.avatarUrl || ''
|
|||
|
|
const nickname = user.nickname || ''
|
|||
|
|
if (avatar && !avatar.includes('default') && nickname && nickname !== '微信用户' && !nickname.startsWith('微信用户')) {
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
if (isInCooldown('fill_avatar')) return null
|
|||
|
|
setCooldown('fill_avatar')
|
|||
|
|
return {
|
|||
|
|
ruleId: 'fill_avatar',
|
|||
|
|
title: '完善个人信息',
|
|||
|
|
message: '设置头像和昵称,让其他创业者更容易认识你',
|
|||
|
|
action: 'navigate',
|
|||
|
|
target: '/pages/profile-edit/profile-edit'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function checkRule_BindPhone() {
|
|||
|
|
const user = getUserInfo()
|
|||
|
|
if (!user.id) return null
|
|||
|
|
if (user.phone) return null
|
|||
|
|
if (isInCooldown('bind_phone')) return null
|
|||
|
|
setCooldown('bind_phone')
|
|||
|
|
return {
|
|||
|
|
ruleId: 'bind_phone',
|
|||
|
|
title: '绑定手机号',
|
|||
|
|
message: '绑定手机号解锁更多功能,保障账户安全',
|
|||
|
|
action: 'bind_phone',
|
|||
|
|
target: null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function checkRule_FillProfile() {
|
|||
|
|
const user = getUserInfo()
|
|||
|
|
if (!user.id) return null
|
|||
|
|
if (user.mbti && user.industry) return null
|
|||
|
|
if (isInCooldown('fill_profile')) return null
|
|||
|
|
setCooldown('fill_profile')
|
|||
|
|
return {
|
|||
|
|
ruleId: 'fill_profile',
|
|||
|
|
title: '完善创业档案',
|
|||
|
|
message: '填写 MBTI 和行业信息,帮你精准匹配创业伙伴',
|
|||
|
|
action: 'navigate',
|
|||
|
|
target: '/pages/profile-edit/profile-edit'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function checkRule_ShareAfter5Chapters() {
|
|||
|
|
const user = getUserInfo()
|
|||
|
|
if (!user.id) return null
|
|||
|
|
const readCount = app.globalData.readCount || 0
|
|||
|
|
if (readCount < 5) return null
|
|||
|
|
if (isInCooldown('share_after_5')) return null
|
|||
|
|
setCooldown('share_after_5')
|
|||
|
|
return {
|
|||
|
|
ruleId: 'share_after_5',
|
|||
|
|
title: '邀请好友一起看',
|
|||
|
|
message: '你已阅读 ' + readCount + ' 个章节,分享给好友可获得分销收益',
|
|||
|
|
action: 'navigate',
|
|||
|
|
target: '/pages/referral/referral'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 在指定场景下检查规则
|
|||
|
|
* @param {string} scene - 场景:after_login, before_read, after_match, after_pay, page_show
|
|||
|
|
* @returns {object|null} - 触发的规则,null 表示无需触发
|
|||
|
|
*/
|
|||
|
|
function checkRules(scene) {
|
|||
|
|
const user = getUserInfo()
|
|||
|
|
if (!user.id) return null
|
|||
|
|
|
|||
|
|
switch (scene) {
|
|||
|
|
case 'after_login':
|
|||
|
|
return checkRule_FillAvatar()
|
|||
|
|
case 'before_read':
|
|||
|
|
return checkRule_BindPhone() || checkRule_FillAvatar()
|
|||
|
|
case 'after_match':
|
|||
|
|
return checkRule_FillProfile()
|
|||
|
|
case 'after_pay':
|
|||
|
|
return checkRule_FillProfile()
|
|||
|
|
case 'page_show':
|
|||
|
|
return checkRule_FillAvatar() || checkRule_ShareAfter5Chapters()
|
|||
|
|
default:
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 执行规则动作
|
|||
|
|
* @param {object} rule - checkRules 返回的规则对象
|
|||
|
|
* @param {object} pageInstance - 页面实例(用于绑定手机号等操作)
|
|||
|
|
*/
|
|||
|
|
function executeRule(rule, pageInstance) {
|
|||
|
|
if (!rule) return
|
|||
|
|
|
|||
|
|
wx.showModal({
|
|||
|
|
title: rule.title,
|
|||
|
|
content: rule.message,
|
|||
|
|
confirmText: '去完善',
|
|||
|
|
cancelText: '稍后再说',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
if (rule.action === 'navigate' && rule.target) {
|
|||
|
|
wx.navigateTo({ url: rule.target })
|
|||
|
|
} else if (rule.action === 'bind_phone' && pageInstance) {
|
|||
|
|
if (typeof pageInstance.showPhoneBinding === 'function') {
|
|||
|
|
pageInstance.showPhoneBinding()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_trackRuleAction(rule.ruleId, res.confirm ? 'confirm' : 'cancel')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function _trackRuleAction(ruleId, action) {
|
|||
|
|
const userId = getUserInfo().id
|
|||
|
|
if (!userId) return
|
|||
|
|
app.request('/api/miniprogram/track', {
|
|||
|
|
method: 'POST',
|
|||
|
|
data: { userId, action: 'rule_trigger', target: ruleId, extraData: { result: action } },
|
|||
|
|
silent: true
|
|||
|
|
}).catch(() => {})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 在页面 onShow 中调用的便捷方法
|
|||
|
|
* @param {string} scene
|
|||
|
|
* @param {object} pageInstance
|
|||
|
|
*/
|
|||
|
|
function checkAndExecute(scene, pageInstance) {
|
|||
|
|
const rule = checkRules(scene)
|
|||
|
|
if (rule) {
|
|||
|
|
setTimeout(() => executeRule(rule, pageInstance), 800)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = { checkRules, executeRule, checkAndExecute }
|