Files
soul-yongping/miniprogram/pages/match/match.js

937 lines
29 KiB
JavaScript
Raw Normal View History

/**
* Soul创业派对 - 找伙伴页
* 按H5网页端完全重构
* 开发: 卡若
*/
const app = getApp()
// 默认匹配类型配置
// 找伙伴:真正的匹配功能,匹配数据库中的真实用户
// 资源对接:需要登录+购买章节才能使用填写2项信息我能帮到你什么、我需要什么帮助
// 导师顾问:跳转到存客宝添加微信
// 团队招募:跳转到存客宝添加微信
let MATCH_TYPES = [
{ id: 'partner', label: '找伙伴', matchLabel: '找伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false },
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true, requirePurchase: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true }
]
let FREE_MATCH_LIMIT = 3 // 每日免费匹配次数
Page({
data: {
statusBarHeight: 44,
// 匹配类型
matchTypes: MATCH_TYPES,
selectedType: 'partner',
currentTypeLabel: '找伙伴',
// 用户状态
isLoggedIn: false,
hasPurchased: false,
hasFullBook: false,
// 匹配次数
todayMatchCount: 0,
totalMatchesAllowed: FREE_MATCH_LIMIT,
matchesRemaining: FREE_MATCH_LIMIT,
showQuotaExhausted: false,
needPayToMatch: false,
// 匹配状态
isMatching: false,
matchAttempts: 0,
currentMatch: null,
// 加入弹窗
showJoinModal: false,
joinType: null,
joinTypeLabel: '',
contactType: 'phone',
phoneNumber: '',
wechatId: '',
userPhone: '',
isJoining: false,
joinSuccess: false,
joinError: '',
needBindFirst: false,
// 资源对接表单
canHelp: '',
needHelp: '',
goodAt: '',
// 解锁弹窗
showUnlockModal: false,
// 手机号绑定弹窗(一键加好友前校验)
showBindPhoneModal: false,
pendingAddWechatAfterBind: false,
bindPhoneInput: '',
showMatchPhoneManualInput: false,
// 登录弹窗(未登录时点击匹配弹出)
showLoginModal: false,
isLoggingIn: false,
agreeProtocol: false,
// 匹配价格(可配置)
matchPrice: 1,
extraMatches: 0,
// 好友优惠展示(与 read 页一致)
userDiscount: 5,
hasReferralDiscount: false,
showDiscountHint: false,
displayMatchPrice: 1
},
onLoad(options = {}) {
// ref支持 query.ref 或 scene 中的 ref=xxx分享进入时
let ref = options.ref
if (!ref && options.scene) {
const sceneStr = (typeof options.scene === 'string' ? decodeURIComponent(options.scene) : '').trim()
const parts = sceneStr.split(/[&_]/)
for (const part of parts) {
const eq = part.indexOf('=')
if (eq > 0) {
const k = part.slice(0, eq)
const v = part.slice(eq + 1)
if (k === 'ref' && v) { ref = v; break }
}
}
}
if (ref) {
wx.setStorageSync('referral_code', ref)
app.handleReferralCode({ query: { ref } })
}
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44,
_ref: ref
})
this.loadMatchConfig()
this.loadStoredContact()
this.refreshMatchCountAndStatus()
},
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
const tabBar = this.getTabBar()
if (tabBar.updateSelected) {
tabBar.updateSelected()
} else {
tabBar.setData({ selected: 2 })
}
}
this.loadStoredContact()
this.refreshMatchCountAndStatus()
},
// 加载匹配配置(含 userDiscount 用于好友优惠展示)
async loadMatchConfig() {
try {
const userId = app.globalData.userInfo?.id
const [matchRes, configRes] = await Promise.all([
app.request('/api/miniprogram/match/config', { method: 'GET' }),
app.request('/api/miniprogram/config', { method: 'GET' })
])
const matchPrice = matchRes?.success && matchRes?.data ? (matchRes.data.matchPrice || 1) : 1
const userDiscount = configRes?.userDiscount ?? 5
const hasReferral = !!(wx.getStorageSync('referral_code') || this.data._ref)
const hasReferralDiscount = hasReferral && userDiscount > 0
const displayMatchPrice = hasReferralDiscount
? Math.round(matchPrice * (1 - userDiscount / 100) * 100) / 100
: matchPrice
if (matchRes?.success && matchRes?.data) {
MATCH_TYPES = matchRes.data.matchTypes || MATCH_TYPES
FREE_MATCH_LIMIT = matchRes.data.freeMatchLimit || FREE_MATCH_LIMIT
}
this.setData({
matchTypes: MATCH_TYPES,
totalMatchesAllowed: FREE_MATCH_LIMIT,
matchPrice,
userDiscount,
hasReferralDiscount,
showDiscountHint: userDiscount > 0,
displayMatchPrice
})
console.log('[Match] 加载匹配配置成功:', { matchPrice, userDiscount, hasReferralDiscount, displayMatchPrice })
} catch (e) {
console.log('[Match] 加载匹配配置失败,使用默认配置:', e)
}
},
// 加载本地存储的联系方式(含用户资料的手机号、微信号)
loadStoredContact() {
const ui = app.globalData.userInfo || {}
const phone = wx.getStorageSync('user_phone') || ui.phone || ''
const wechat = wx.getStorageSync('user_wechat') || ui.wechat || ui.wechatId || ''
this.setData({
phoneNumber: phone,
wechatId: wechat,
userPhone: phone
})
},
// 从服务端刷新匹配配额并初始化用户状态(前后端双向校验,服务端为权威)
async refreshMatchCountAndStatus() {
if (app.globalData.isLoggedIn && app.globalData.userInfo?.id) {
try {
const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${encodeURIComponent(app.globalData.userInfo.id)}`)
if (res.success && res.data) {
app.globalData.matchCount = res.data.matchCount ?? 0
app.globalData.matchQuota = res.data.matchQuota || null
// 根据 hasReferrer 更新优惠展示(与 read 页一致)
const hasReferral = !!(wx.getStorageSync('referral_code') || this.data._ref || res.data.hasReferrer)
const matchPrice = this.data.matchPrice ?? 1
const userDiscount = this.data.userDiscount ?? 5
const hasReferralDiscount = hasReferral && userDiscount > 0
const displayMatchPrice = hasReferralDiscount
? Math.round(matchPrice * (1 - userDiscount / 100) * 100) / 100
: matchPrice
this.setData({ hasReferralDiscount, displayMatchPrice })
}
} catch (e) {
console.log('[Match] 拉取 matchQuota 失败:', e)
}
}
this.initUserStatus()
},
// 初始化用户状态matchQuota 服务端纯计算:订单+match_records
initUserStatus() {
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
const quota = app.globalData.matchQuota
// 今日剩余次数、今日已用:来自服务端 matchQuota未登录无法计算不能显示已用完
const remainToday = quota?.remainToday ?? 0
const matchesUsedToday = quota?.matchesUsedToday ?? 0
const purchasedRemain = quota?.purchasedRemain ?? 0
const totalMatchesAllowed = hasFullBook ? 999999 : (quota ? remainToday + matchesUsedToday : FREE_MATCH_LIMIT)
// 仅登录且服务端返回配额时,才判断是否已用完;未登录时显示「开始匹配」
const needPayToMatch = isLoggedIn && !hasFullBook && (quota ? remainToday <= 0 : false)
const showQuotaExhausted = isLoggedIn && !hasFullBook && (quota ? remainToday <= 0 : false)
this.setData({
isLoggedIn,
hasFullBook,
hasPurchased: true,
todayMatchCount: matchesUsedToday,
totalMatchesAllowed,
matchesRemaining: hasFullBook ? 999999 : (isLoggedIn && quota ? remainToday : (isLoggedIn ? 0 : FREE_MATCH_LIMIT)),
needPayToMatch,
showQuotaExhausted,
extraMatches: purchasedRemain
})
},
// 选择匹配类型
selectType(e) {
const typeId = e.currentTarget.dataset.type
const type = MATCH_TYPES.find(t => t.id === typeId)
this.setData({
selectedType: typeId,
currentTypeLabel: type?.matchLabel || type?.label || '创业伙伴'
})
},
// 点击匹配按钮
handleMatchClick() {
// 检测是否登录,未登录则弹出登录弹窗
if (!this.data.isLoggedIn) {
this.setData({ showLoginModal: true, agreeProtocol: false })
return
}
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
// 资源对接类型需要购买章节才能使用
if (currentType && currentType.id === 'investor') {
// 检查是否购买过章节
const hasPurchased = app.globalData.purchasedSections?.length > 0 || app.globalData.hasFullBook
if (!hasPurchased) {
wx.showModal({
title: '需要购买章节',
content: '购买任意章节后即可使用资源对接功能',
confirmText: '去购买',
success: (res) => {
if (res.confirm) {
wx.switchTab({ url: '/pages/chapters/chapters' })
}
}
})
return
}
}
// 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募)
if (currentType && currentType.showJoinAfterMatch) {
// 先检查是否已绑定联系方式
const hasPhone = !!this.data.phoneNumber
const hasWechat = !!this.data.wechatId
if (!hasPhone && !hasWechat) {
// 没有绑定联系方式,先显示绑定提示(仍尝试加载已有资料填充)
this.loadStoredContact()
this.setData({
showJoinModal: true,
joinType: currentType.id,
joinTypeLabel: currentType.matchLabel || currentType.label,
joinSuccess: false,
joinError: '',
needBindFirst: true
})
return
}
// 已绑定联系方式先显示匹配动画1-3秒再弹出确认
this.startMatchingAnimation(currentType)
return
}
// 创业合伙类型 - 超过匹配次数时直接弹出付费弹窗
if (this.data.showQuotaExhausted || this.data.needPayToMatch) {
this.setData({ showUnlockModal: true })
return
}
this.startMatch()
},
// 匹配动画后弹出加入确认
startMatchingAnimation(currentType) {
// 显示匹配中状态
this.setData({
isMatching: true,
matchAttempts: 0,
currentMatch: null
})
// 动画计时
const timer = setInterval(() => {
this.setData({ matchAttempts: this.data.matchAttempts + 1 })
}, 500)
// 1-3秒随机延迟后显示弹窗
const delay = Math.random() * 2000 + 1000
setTimeout(() => {
clearInterval(timer)
// 打开弹窗前调取用户资料填充手机号、微信号
this.loadStoredContact()
this.setData({
isMatching: false,
showJoinModal: true,
joinType: currentType.id,
joinTypeLabel: currentType.matchLabel || currentType.label,
joinSuccess: false,
joinError: '',
needBindFirst: false
})
}, delay)
},
// 显示购买提示
showPurchaseTip() {
wx.showModal({
title: '需要购买书籍',
content: '购买《Soul创业派对》后即可使用匹配功能仅需9.9元',
confirmText: '去购买',
success: (res) => {
if (res.confirm) {
this.goToChapters()
}
}
})
},
// 开始匹配 - 只匹配数据库中的真实用户
async startMatch() {
this.loadStoredContact()
this.setData({
isMatching: true,
matchAttempts: 0,
currentMatch: null
})
// 匹配动画计时器
const timer = setInterval(() => {
this.setData({ matchAttempts: this.data.matchAttempts + 1 })
}, 1000)
// 从数据库获取真实用户匹配(后端会校验剩余次数)
let matchedUser = null
let quotaExceeded = false
try {
const ui = app.globalData.userInfo || {}
const phone = (wx.getStorageSync('user_phone') || ui.phone || this.data.phoneNumber || '').trim()
const wechatId = (wx.getStorageSync('user_wechat') || ui.wechat || ui.wechatId || this.data.wechatId || '').trim()
const res = await app.request('/api/miniprogram/match/users', {
method: 'POST',
data: {
matchType: this.data.selectedType,
userId: app.globalData.userInfo?.id || '',
phone,
wechatId
}
})
if (res.success && res.data) {
matchedUser = res.data
console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname)
} else if (res.code === 'QUOTA_EXCEEDED') {
quotaExceeded = true
}
} catch (e) {
console.log('[Match] 数据库匹配失败:', e)
}
// 延迟显示结果(模拟匹配过程)
const delay = Math.random() * 2000 + 2000
setTimeout(() => {
clearInterval(timer)
// 次数用尽(后端校验)- 直接弹出付费弹窗
if (quotaExceeded) {
this.setData({ isMatching: false, showUnlockModal: true })
this.refreshMatchCountAndStatus()
return
}
// 如果没有匹配到用户,提示用户
if (!matchedUser) {
this.setData({ isMatching: false })
wx.showModal({
title: '暂无匹配',
content: '当前暂无合适的匹配用户,请稍后再试',
showCancel: false,
confirmText: '知道了'
})
return
}
// 匹配成功:从服务端刷新配额(后端已写入 match_records
this.setData({
isMatching: false,
currentMatch: matchedUser,
needPayToMatch: false
})
this.refreshMatchCountAndStatus()
// 上报匹配行为到存客宝
this.reportMatch(matchedUser)
}, delay)
},
// 生成模拟匹配数据
generateMockMatch() {
const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者']
const concepts = [
'专注私域流量运营5年帮助100+品牌实现从0到1的增长。',
'连续创业者,擅长商业模式设计和资源整合。',
'在Soul分享真实创业故事希望找到志同道合的合作伙伴。'
]
const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan']
const index = Math.floor(Math.random() * nicknames.length)
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
return {
id: `user_${Date.now()}`,
nickname: nicknames[index],
avatar: `https://picsum.photos/200/200?random=${Date.now()}`,
tags: ['创业者', '私域运营', currentType?.label || '创业合伙'],
matchScore: Math.floor(Math.random() * 20) + 80,
concept: concepts[index % concepts.length],
wechat: wechats[index % wechats.length],
commonInterests: [
{ icon: '📚', text: '都在读《创业派对》' },
{ icon: '💼', text: '对私域运营感兴趣' },
{ icon: '🎯', text: '相似的创业方向' }
]
}
},
// 上报匹配行为
async reportMatch(matchedUser) {
try {
await app.request('/api/miniprogram/ckb/match', {
method: 'POST',
data: {
matchType: this.data.selectedType,
phone: this.data.phoneNumber,
wechat: this.data.wechatId,
userId: app.globalData.userInfo?.id || '',
nickname: app.globalData.userInfo?.nickname || '',
matchedUser: {
id: matchedUser.id,
nickname: matchedUser.nickname,
matchScore: matchedUser.matchScore
}
}
})
} catch (e) {
console.log('上报匹配失败:', e)
}
},
// 取消匹配
cancelMatch() {
this.setData({ isMatching: false, matchAttempts: 0 })
},
// 重置匹配(返回)
resetMatch() {
this.setData({ currentMatch: null })
},
// 添加微信好友(先校验手机号绑定)
handleAddWechat() {
if (!this.data.currentMatch) return
// 未登录需先登录
if (!app.globalData.isLoggedIn) {
wx.showModal({
title: '需要登录',
content: '请先登录后再添加好友',
confirmText: '去登录',
success: (res) => {
if (res.confirm) wx.switchTab({ url: '/pages/my/my' })
}
})
return
}
// 判断是否已绑定手机号(本地缓存或用户资料)
const hasPhone = !!(
wx.getStorageSync('user_phone') ||
app.globalData.userInfo?.phone
)
if (!hasPhone) {
this.setData({
showBindPhoneModal: true,
pendingAddWechatAfterBind: true
})
return
}
this.doCopyWechat()
},
// 执行复制联系方式(优先微信号,无则复制手机号)
doCopyWechat() {
if (!this.data.currentMatch) return
const wechat = (this.data.currentMatch.wechat || this.data.currentMatch.wechatId || '').trim()
const phone = (this.data.currentMatch.phone || '').trim()
const toCopy = wechat || phone
if (!toCopy) {
wx.showModal({
title: '暂无可复制',
content: '该用户未提供微信号或手机号,请通过其他方式联系',
showCancel: false,
confirmText: '知道了'
})
return
}
const label = wechat ? '微信号' : '手机号'
wx.setClipboardData({
data: toCopy,
success: () => {
wx.showModal({
title: wechat ? '微信号已复制' : '手机号已复制',
content: wechat
? `${label}${toCopy}\n\n请打开微信添加好友,备注"创业合作"即可`
: `${label}${toCopy}\n\n可通过微信搜索该手机号添加好友`,
showCancel: false,
confirmText: '知道了'
})
},
fail: () => {
wx.showToast({ title: '复制失败,请重试', icon: 'none' })
}
})
},
// 切换联系方式类型(同步刷新用户资料填充)
switchContactType(e) {
const type = e.currentTarget.dataset.type
this.loadStoredContact()
this.setData({ contactType: type, joinError: '' })
},
// 手机号输入
onPhoneInput(e) {
this.setData({
phoneNumber: e.detail.value.replace(/\D/g, '').slice(0, 11),
joinError: ''
})
},
// 资源对接表单输入
onCanHelpInput(e) {
this.setData({ canHelp: e.detail.value })
},
onNeedHelpInput(e) {
this.setData({ needHelp: e.detail.value })
},
onGoodAtInput(e) {
this.setData({ goodAt: e.detail.value })
},
// 微信号输入
onWechatInput(e) {
this.setData({
wechatId: e.detail.value,
joinError: ''
})
},
// 提交加入
async handleJoinSubmit() {
const { contactType, phoneNumber, wechatId, joinType, isJoining, canHelp, needHelp } = this.data
if (isJoining) return
// 验证联系方式
if (contactType === 'phone') {
if (!phoneNumber || phoneNumber.length !== 11) {
this.setData({ joinError: '请输入正确的11位手机号' })
return
}
} else {
if (!wechatId || wechatId.length < 6) {
this.setData({ joinError: '请输入正确的微信号至少6位' })
return
}
}
// 资源对接需要填写两项信息
if (joinType === 'investor') {
if (!canHelp || canHelp.trim().length < 2) {
this.setData({ joinError: '请填写"我能帮到你什么"' })
return
}
if (!needHelp || needHelp.trim().length < 2) {
this.setData({ joinError: '请填写"我需要什么帮助"' })
return
}
}
this.setData({ isJoining: true, joinError: '' })
try {
const res = await app.request('/api/miniprogram/ckb/join', {
method: 'POST',
data: {
type: joinType,
phone: contactType === 'phone' ? phoneNumber : '',
wechat: contactType === 'wechat' ? wechatId : '',
userId: app.globalData.userInfo?.id || '',
// 资源对接专属字段
canHelp: joinType === 'investor' ? canHelp : '',
needHelp: joinType === 'investor' ? needHelp : ''
}
})
// 保存联系方式到本地
if (phoneNumber) wx.setStorageSync('user_phone', phoneNumber)
if (wechatId) wx.setStorageSync('user_wechat', wechatId)
if (res.success) {
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
} else {
// 即使API返回失败也模拟成功因为已保存本地
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
}
} catch (e) {
// 网络错误时也模拟成功
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
} finally {
this.setData({ isJoining: false })
}
},
// 关闭加入弹窗
closeJoinModal() {
if (this.data.isJoining) return
this.setData({ showJoinModal: false, joinError: '' })
},
// 关闭手机绑定弹窗
closeBindPhoneModal() {
this.setData({
showBindPhoneModal: false,
pendingAddWechatAfterBind: false,
bindPhoneInput: '',
showMatchPhoneManualInput: false
})
},
// 关闭登录弹窗
closeLoginModal() {
if (this.data.isLoggingIn) return
this.setData({ showLoginModal: false })
},
// 切换协议勾选
toggleAgree() {
this.setData({ agreeProtocol: !this.data.agreeProtocol })
},
// 打开用户协议
openUserProtocol() {
wx.navigateTo({ url: '/pages/agreement/agreement' })
},
// 打开隐私政策
openPrivacy() {
wx.navigateTo({ url: '/pages/privacy/privacy' })
},
// 微信登录(匹配页)
async handleMatchWechatLogin() {
if (!this.data.agreeProtocol) {
wx.showToast({ title: '请先阅读并同意用户协议和隐私政策', icon: 'none' })
return
}
this.setData({ isLoggingIn: true })
try {
const result = await app.login()
if (result) {
// 登录成功后必须拉取 matchQuota否则无法正确显示剩余次数
await this.refreshMatchCountAndStatus()
this.setData({ showLoginModal: false, agreeProtocol: false })
wx.showToast({ title: '登录成功', icon: 'success' })
} else {
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
}
} catch (e) {
console.error('[Match] 微信登录错误:', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
} finally {
this.setData({ isLoggingIn: false })
}
},
// 一键获取手机号(匹配页加好友前绑定)
async onMatchGetPhoneNumber(e) {
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
wx.showToast({ title: '授权失败', icon: 'none' })
return
}
const code = e.detail.code
if (!code) {
this.setData({ showMatchPhoneManualInput: true })
return
}
try {
wx.showLoading({ title: '获取中...', mask: true })
const userId = app.globalData.userInfo?.id
const res = await app.request('/api/miniprogram/phone', {
method: 'POST',
data: { code, userId }
})
wx.hideLoading()
if (res.success && res.phoneNumber) {
await this.saveMatchPhoneAndContinue(res.phoneNumber)
} else {
this.setData({ showMatchPhoneManualInput: true })
}
} catch (err) {
wx.hideLoading()
this.setData({ showMatchPhoneManualInput: true })
}
},
// 切换为手动输入
onMatchShowManualInput() {
this.setData({ showMatchPhoneManualInput: true })
},
// 手动输入手机号
onMatchPhoneInput(e) {
this.setData({
bindPhoneInput: e.detail.value.replace(/\D/g, '').slice(0, 11)
})
},
// 确认手动绑定手机号
async confirmMatchPhoneBind() {
const { bindPhoneInput } = this.data
if (!bindPhoneInput || bindPhoneInput.length !== 11) {
wx.showToast({ title: '请输入正确的11位手机号', icon: 'none' })
return
}
if (!/^1[3-9]\d{9}$/.test(bindPhoneInput)) {
wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
await this.saveMatchPhoneAndContinue(bindPhoneInput)
},
// 保存手机号到本地+服务器,并继续加好友
async saveMatchPhoneAndContinue(phone) {
wx.setStorageSync('user_phone', phone)
if (app.globalData.userInfo) {
app.globalData.userInfo.phone = phone
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
this.setData({
phoneNumber: phone,
userPhone: phone,
bindPhoneInput: ''
})
this.loadStoredContact()
try {
const userId = app.globalData.userInfo?.id
if (userId) {
await app.request('/api/miniprogram/user/profile', {
method: 'POST',
data: { userId, phone }
})
}
} catch (e) {
console.log('[Match] 同步手机号到服务器失败:', e)
}
const pending = this.data.pendingAddWechatAfterBind
this.closeBindPhoneModal()
if (pending) {
wx.showToast({ title: '绑定成功', icon: 'success' })
setTimeout(() => this.doCopyWechat(), 500)
}
},
// 显示解锁弹窗
showUnlockModal() {
this.setData({ showUnlockModal: true })
},
// 关闭解锁弹窗
closeUnlockModal() {
this.setData({ showUnlockModal: false })
},
// 支付成功后立即查询订单状态并刷新(首轮 0 延迟,之后每 800ms 重试)
async pollOrderAndRefresh(orderSn) {
const maxAttempts = 12
const interval = 800
for (let i = 0; i < maxAttempts; i++) {
try {
const r = await app.request(`/api/miniprogram/pay?orderSn=${encodeURIComponent(orderSn)}`, { method: 'GET', silent: true })
if (r?.data?.status === 'paid') {
await this.refreshMatchCountAndStatus()
return
}
} catch (_) {}
if (i < maxAttempts - 1) await new Promise(r => setTimeout(r, interval))
}
await this.refreshMatchCountAndStatus()
},
// 购买匹配次数(与购买章节逻辑一致,写入订单)
async buyMatchCount() {
this.setData({ showUnlockModal: false })
try {
// 获取openId
let openId = app.globalData.openId || wx.getStorageSync('openId')
if (!openId) {
openId = await app.getOpenId()
}
if (!openId) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
const matchPrice = this.data.matchPrice || 1
2026-02-05 11:35:57 +08:00
// 邀请码:与章节支付一致,写入订单便于分销归属与对账
const referralCode = wx.getStorageSync('referral_code') || ''
// 调用支付接口购买匹配次数productType: match订单类型购买匹配次数
const res = await app.request('/api/miniprogram/pay', {
method: 'POST',
data: {
openId,
productType: 'match',
productId: 'match_1',
amount: matchPrice,
description: '匹配次数x1',
2026-02-05 11:35:57 +08:00
userId: app.globalData.userInfo?.id || '',
referralCode: referralCode || undefined
}
})
if (res.success && res.data?.payParams) {
const orderSn = res.data.orderSn
// 调用微信支付
await new Promise((resolve, reject) => {
wx.requestPayment({
...res.data.payParams,
success: resolve,
fail: reject
})
})
wx.showToast({ title: '购买成功', icon: 'success' })
// 轮询订单状态,确认已支付后再刷新(不依赖 PayNotify 回调时机)
this.pollOrderAndRefresh(orderSn)
} else {
throw new Error(res.error || '创建订单失败')
}
} catch (e) {
if (e.errMsg && e.errMsg.includes('cancel')) {
wx.showToast({ title: '已取消', icon: 'none' })
} else {
// 测试模式(无支付环境时本地模拟)
wx.showModal({
title: '支付服务暂不可用',
content: '是否使用测试模式购买?',
success: (res) => {
if (res.confirm) {
app.globalData.matchCount = (app.globalData.matchCount ?? 0) + 1
wx.showToast({ title: '测试购买成功', icon: 'success' })
this.initUserStatus()
}
}
})
}
}
},
// 跳转到目录页购买
goToChapters() {
this.setData({ showUnlockModal: false })
wx.switchTab({ url: '/pages/chapters/chapters' })
},
// 打开设置
openSettings() {
wx.navigateTo({ url: '/pages/settings/settings' })
},
// 阻止事件冒泡
preventBubble() {},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 找伙伴',
path: ref ? `/pages/match/match?ref=${ref}` : '/pages/match/match'
}
}
})