- miniprogram: reading-records、imageUrl/mpNavigate、多页资料与 VIP 展示调整 - soul-admin: Users/Settings/UserDetailModal、dist 构建产物更新 - soul-api: user/vip/referral/ckb/db、MBTI 头像管理、user_rule_completion、迁移 SQL - .cursor: karuo-party 与飞书文档;.gitignore 忽略 .tmp_skill_bundle Made-with: Cursor
222 lines
7.6 KiB
JavaScript
222 lines
7.6 KiB
JavaScript
const accessManager = require('../../utils/chapterAccessManager')
|
||
const app = getApp()
|
||
const { trackClick } = require('../../utils/trackClick')
|
||
|
||
Page({
|
||
data: {
|
||
statusBarHeight: 44,
|
||
isVip: false,
|
||
daysRemaining: 0,
|
||
expireDateStr: '',
|
||
price: 1980,
|
||
originalPrice: 6980,
|
||
/* 按 premium_membership_landing_v1 设计稿 */
|
||
contentRights: [
|
||
{ key: 'match', title: '匹配伙伴', desc: '精准匹配创业伙伴', icon: 'users' },
|
||
{ key: 'party', title: '派对专属', desc: '创业派对房专享', icon: 'star' },
|
||
{ key: 'rank', title: '老板排行', desc: '创业老板排行榜', icon: 'bar-chart' },
|
||
{ key: 'top', title: '轮流置顶', desc: '首页获客曝光位', icon: 'chevron-up' }
|
||
],
|
||
socialRights: [
|
||
{ key: 'cases', title: '案例宝库', desc: '100+赚钱案例库', icon: 'book-open' },
|
||
{ key: 'fullbook', title: '全书解锁', desc: '365天全案精读', icon: 'folder' },
|
||
{ key: 'daily', title: '每日总结', desc: 'AI每日精华推送', icon: 'lightbulb' },
|
||
{ key: 'leads', title: '获取客资', desc: '文章@你即可获客', icon: 'link' }
|
||
],
|
||
purchasing: false
|
||
},
|
||
|
||
onLoad() {
|
||
wx.showShareMenu({ withShareTimeline: true })
|
||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||
this.loadVipInfo()
|
||
},
|
||
|
||
async loadVipInfo() {
|
||
const userId = app.globalData.userInfo?.id
|
||
if (!userId) return
|
||
try {
|
||
const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true })
|
||
if (res?.success) {
|
||
const d = res.data
|
||
let expStr = ''
|
||
if (d.expireDate) {
|
||
const dt = new Date(d.expireDate)
|
||
expStr = `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`
|
||
}
|
||
// 同步 VIP 状态到全局(与「我的」页保持一致)
|
||
const isVip = !!d.isVip
|
||
app.globalData.isVip = isVip
|
||
app.globalData.vipExpireDate = d.expireDate || expStr || ''
|
||
const userInfo = app.globalData.userInfo || {}
|
||
userInfo.isVip = isVip
|
||
userInfo.vipExpireDate = app.globalData.vipExpireDate
|
||
app.globalData.userInfo = userInfo
|
||
wx.setStorageSync('userInfo', userInfo)
|
||
|
||
this.setData({
|
||
isVip,
|
||
daysRemaining: d.daysRemaining,
|
||
expireDateStr: expStr,
|
||
price: d.price || 1980
|
||
})
|
||
}
|
||
} catch (e) { console.log('[VIP] 加载失败', e) }
|
||
},
|
||
|
||
async handlePurchase() {
|
||
trackClick('vip', 'btn_click', '开通VIP')
|
||
let userId = app.globalData.userInfo?.id
|
||
let openId = app.globalData.openId || app.globalData.userInfo?.open_id
|
||
if (!userId || !openId) {
|
||
wx.showLoading({ title: '登录中...', mask: true })
|
||
try {
|
||
await app.login()
|
||
userId = app.globalData.userInfo?.id
|
||
openId = app.globalData.openId || app.globalData.userInfo?.open_id
|
||
wx.hideLoading()
|
||
if (!userId || !openId) {
|
||
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||
return
|
||
}
|
||
} catch (e) {
|
||
wx.hideLoading()
|
||
wx.showToast({ title: '登录失败', icon: 'none' })
|
||
return
|
||
}
|
||
}
|
||
// VIP 购买成功后再跳转资料页:购买前不拦截
|
||
this.setData({ purchasing: true })
|
||
const amount = this.data.price
|
||
try {
|
||
// 0. 尝试余额支付(若余额足够)
|
||
const referralCode = wx.getStorageSync('referral_code') || ''
|
||
try {
|
||
const balanceRes = await app.request({ url: `/api/miniprogram/balance?userId=${userId}`, silent: true })
|
||
const balance = balanceRes?.data?.balance || 0
|
||
if (balance >= amount) {
|
||
const consumeRes = await app.request({
|
||
url: '/api/miniprogram/balance/consume',
|
||
method: 'POST',
|
||
data: {
|
||
userId,
|
||
productType: 'vip',
|
||
productId: 'vip_annual',
|
||
amount,
|
||
referralCode: referralCode || undefined
|
||
}
|
||
})
|
||
if (consumeRes?.success) {
|
||
this.setData({ purchasing: false })
|
||
wx.showToast({ title: 'VIP开通成功', icon: 'success' })
|
||
await this._onVipPaymentSuccess()
|
||
return
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.warn('[VIP] 余额支付失败,改用微信支付:', e)
|
||
}
|
||
|
||
// 1. 微信支付
|
||
const payRes = await app.request('/api/miniprogram/pay', {
|
||
method: 'POST',
|
||
data: {
|
||
openId,
|
||
userId,
|
||
productType: 'vip',
|
||
productId: 'vip_annual',
|
||
amount,
|
||
description: '卡若创业派对VIP年度会员(365天)'
|
||
}
|
||
})
|
||
if (payRes?.success && payRes.data?.payParams) {
|
||
wx.requestPayment({
|
||
...payRes.data.payParams,
|
||
success: async () => {
|
||
wx.showToast({ title: 'VIP开通成功', icon: 'success' })
|
||
await this._onVipPaymentSuccess()
|
||
},
|
||
fail: () => wx.showToast({ title: '支付取消', icon: 'none' })
|
||
})
|
||
} else {
|
||
wx.showToast({ title: payRes?.error || '支付参数获取失败', icon: 'none' })
|
||
}
|
||
} catch (e) {
|
||
console.error('[VIP] 购买失败', e)
|
||
wx.showToast({ title: '购买失败,请稍后重试', icon: 'none' })
|
||
} finally { this.setData({ purchasing: false }) }
|
||
},
|
||
|
||
async _onVipPaymentSuccess() {
|
||
wx.showLoading({ title: '同步权益中...', mask: true })
|
||
try {
|
||
await new Promise(r => setTimeout(r, 1500))
|
||
await accessManager.refreshUserPurchaseStatus()
|
||
// 重新拉取 VIP 状态并同步到全局
|
||
await this.loadVipInfo()
|
||
const pages = getCurrentPages()
|
||
pages.forEach(p => {
|
||
if (typeof p.initUserStatus === 'function') p.initUserStatus()
|
||
else if (typeof p.updateUserStatus === 'function') p.updateUserStatus()
|
||
})
|
||
|
||
// 超级个体购买后:弹窗提示,强制跳转资料编辑页
|
||
wx.hideLoading()
|
||
wx.showModal({
|
||
title: '补全 VIP 资料',
|
||
content: '补全资料后,找伙伴、提现与 VIP 群对接会更顺畅;手机号等为必填项。',
|
||
confirmText: '去填写',
|
||
showCancel: false,
|
||
success: () => {
|
||
wx.redirectTo({ url: '/pages/profile-edit/profile-edit?from=vip' })
|
||
}
|
||
})
|
||
} catch (e) {
|
||
console.error('[VIP] 支付后同步失败:', e)
|
||
wx.hideLoading()
|
||
}
|
||
},
|
||
|
||
goBack() { getApp().goBackOrToHome() },
|
||
|
||
/**
|
||
* 权益卡片跳转:会员权利 / 派对权利 统一点击进对应能力页
|
||
*/
|
||
onBenefitTap(e) {
|
||
const key = e.currentTarget.dataset.key
|
||
if (!key) return
|
||
trackClick('vip', 'benefit_tap', key)
|
||
const tab = (path) => {
|
||
wx.switchTab({ url: path })
|
||
}
|
||
const nav = (path) => {
|
||
wx.navigateTo({ url: path })
|
||
}
|
||
const routes = {
|
||
match: () => tab('/pages/match/match'),
|
||
party: () => nav('/pages/mentors/mentors'),
|
||
rank: () => tab('/pages/index/index'),
|
||
top: () => tab('/pages/index/index'),
|
||
cases: () => tab('/pages/chapters/chapters'),
|
||
fullbook: () => tab('/pages/chapters/chapters'),
|
||
daily: () => tab('/pages/chapters/chapters'),
|
||
leads: () => nav('/pages/profile-edit/profile-edit')
|
||
}
|
||
const fn = routes[key]
|
||
if (typeof fn === 'function') fn()
|
||
},
|
||
|
||
onShareAppMessage() {
|
||
const ref = app.getMyReferralCode()
|
||
return {
|
||
title: '卡若创业派对 - VIP会员',
|
||
path: ref ? `/pages/vip/vip?ref=${ref}` : '/pages/vip/vip'
|
||
}
|
||
},
|
||
|
||
onShareTimeline() {
|
||
const ref = app.getMyReferralCode()
|
||
return { title: '卡若创业派对 - VIP会员', query: ref ? `ref=${ref}` : '' }
|
||
}
|
||
})
|