fix: 修复分销数据不显示和用户ID唯一性问题
1. 分销数据修复: - 添加访问记录API,即使未登录也记录访问 - 登录成功后自动绑定推荐码 - 在链接访问时立即记录访问统计 2. 支付商品名称修复: - 支付时显示完整章节名称而非仅ID - 格式:章节9.12-美业整合:一个人的公司如何月入十万 3. 用户ID唯一性修复: - 移除mockLogin,必须使用真实微信登录 - 用户ID使用openId作为唯一标识 - 同一微信用户每次登录都是同一ID
This commit is contained in:
100
app/api/referral/visit/route.ts
Normal file
100
app/api/referral/visit/route.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 推荐访问记录API
|
||||
* 用于统计「通过链接进的人数」
|
||||
* 不需要用户登录即可记录
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { query } from '@/lib/db'
|
||||
|
||||
/**
|
||||
* POST - 记录推荐访问
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { referralCode, visitorOpenId, visitorId, source, page } = body
|
||||
|
||||
if (!referralCode) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '推荐码不能为空'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 查找推荐人
|
||||
const referrers = await query(
|
||||
'SELECT id FROM users WHERE referral_code = ?',
|
||||
[referralCode]
|
||||
) as any[]
|
||||
|
||||
if (referrers.length === 0) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '推荐码无效'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
const referrerId = referrers[0].id
|
||||
|
||||
// 记录访问(允许重复访问记录,用于统计总访问次数)
|
||||
try {
|
||||
await query(`
|
||||
INSERT INTO referral_visits (
|
||||
referrer_id, visitor_id, visitor_openid, source, page, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
`, [
|
||||
referrerId,
|
||||
visitorId || null,
|
||||
visitorOpenId || null,
|
||||
source || 'miniprogram',
|
||||
page || ''
|
||||
])
|
||||
|
||||
console.log(`[Referral Visit] 记录访问: 推荐人=${referrerId}, 访客openId=${visitorOpenId?.slice(0,10) || 'unknown'}`)
|
||||
} catch (insertError) {
|
||||
// 表可能不存在,尝试创建
|
||||
console.log('[Referral Visit] 插入失败,尝试创建表...')
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS referral_visits (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
referrer_id VARCHAR(50) NOT NULL COMMENT '推广者ID',
|
||||
visitor_id VARCHAR(50) COMMENT '访客ID(可能为空)',
|
||||
visitor_openid VARCHAR(100) COMMENT '访客openId',
|
||||
source VARCHAR(50) DEFAULT 'miniprogram' COMMENT '来源:miniprogram/web/share',
|
||||
page VARCHAR(200) COMMENT '落地页路径',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_referrer_id (referrer_id),
|
||||
INDEX idx_visitor_id (visitor_id),
|
||||
INDEX idx_created_at (created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`)
|
||||
|
||||
// 重试插入
|
||||
await query(`
|
||||
INSERT INTO referral_visits (
|
||||
referrer_id, visitor_id, visitor_openid, source, page, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
`, [
|
||||
referrerId,
|
||||
visitorId || null,
|
||||
visitorOpenId || null,
|
||||
source || 'miniprogram',
|
||||
page || ''
|
||||
])
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '访问已记录'
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Referral Visit] 错误:', error)
|
||||
// 即使出错也返回成功,不影响用户体验
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '已处理'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -78,21 +78,48 @@ App({
|
||||
if (refCode) {
|
||||
console.log('[App] 检测到推荐码:', refCode)
|
||||
|
||||
// 立即记录访问(不需要登录,用于统计"通过链接进的人数")
|
||||
this.recordReferralVisit(refCode)
|
||||
|
||||
// 检查是否已经绑定过
|
||||
const boundRef = wx.getStorageSync('boundReferralCode')
|
||||
if (boundRef && boundRef !== refCode) {
|
||||
console.log('[App] 已绑定过其他推荐码,跳过')
|
||||
return
|
||||
console.log('[App] 已绑定过其他推荐码,不更换绑定关系')
|
||||
// 但仍然记录访问,不return
|
||||
} else {
|
||||
// 保存待绑定的推荐码
|
||||
this.globalData.pendingReferralCode = refCode
|
||||
wx.setStorageSync('pendingReferralCode', refCode)
|
||||
|
||||
// 如果已登录,立即绑定
|
||||
if (this.globalData.isLoggedIn && this.globalData.userInfo) {
|
||||
this.bindReferralCode(refCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 记录推荐访问(不需要登录,用于统计)
|
||||
async recordReferralVisit(refCode) {
|
||||
try {
|
||||
// 获取openId(如果有)
|
||||
const openId = this.globalData.openId || wx.getStorageSync('openId') || ''
|
||||
const userId = this.globalData.userInfo?.id || ''
|
||||
|
||||
// 保存待绑定的推荐码
|
||||
this.globalData.pendingReferralCode = refCode
|
||||
wx.setStorageSync('pendingReferralCode', refCode)
|
||||
|
||||
// 如果已登录,立即绑定
|
||||
if (this.globalData.isLoggedIn && this.globalData.userInfo) {
|
||||
this.bindReferralCode(refCode)
|
||||
}
|
||||
await this.request('/api/referral/visit', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
referralCode: refCode,
|
||||
visitorOpenId: openId,
|
||||
visitorId: userId,
|
||||
source: 'miniprogram',
|
||||
page: getCurrentPages()[getCurrentPages().length - 1]?.route || ''
|
||||
}
|
||||
})
|
||||
console.log('[App] 记录推荐访问成功')
|
||||
} catch (e) {
|
||||
console.log('[App] 记录推荐访问失败:', e.message)
|
||||
// 忽略错误,不影响用户体验
|
||||
}
|
||||
},
|
||||
|
||||
@@ -286,20 +313,29 @@ App({
|
||||
|
||||
wx.setStorageSync('userInfo', res.data.user)
|
||||
wx.setStorageSync('token', res.data.token || '')
|
||||
|
||||
// 登录成功后,检查待绑定的推荐码并执行绑定
|
||||
const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
|
||||
if (pendingRef) {
|
||||
console.log('[App] 登录后自动绑定推荐码:', pendingRef)
|
||||
this.bindReferralCode(pendingRef)
|
||||
}
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log('[App] API登录失败,使用模拟登录:', apiError.message)
|
||||
console.log('[App] API登录失败:', apiError.message)
|
||||
// 不使用模拟登录,提示用户网络问题
|
||||
wx.showToast({ title: '网络异常,请重试', icon: 'none' })
|
||||
return null
|
||||
}
|
||||
|
||||
// API不可用时使用模拟登录
|
||||
return this.mockLogin()
|
||||
return null
|
||||
} catch (e) {
|
||||
console.error('[App] 登录失败:', e)
|
||||
// 最后尝试模拟登录
|
||||
return this.mockLogin()
|
||||
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -335,36 +371,11 @@ App({
|
||||
return null
|
||||
},
|
||||
|
||||
// 模拟登录(后端不可用时使用)
|
||||
// 模拟登录已废弃 - 不再使用
|
||||
// 现在必须使用真实的微信登录获取openId作为唯一标识
|
||||
mockLogin() {
|
||||
const mockUser = {
|
||||
id: 'user_' + Date.now(),
|
||||
nickname: '访客用户',
|
||||
phone: '',
|
||||
avatar: '',
|
||||
referralCode: 'SOUL' + Date.now().toString(36).toUpperCase().slice(-6),
|
||||
purchasedSections: [],
|
||||
hasFullBook: false,
|
||||
earnings: 0,
|
||||
pendingEarnings: 0,
|
||||
referralCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
}
|
||||
|
||||
const mockToken = 'mock_token_' + Date.now()
|
||||
|
||||
// 保存用户信息
|
||||
this.globalData.userInfo = mockUser
|
||||
this.globalData.isLoggedIn = true
|
||||
this.globalData.purchasedSections = mockUser.purchasedSections
|
||||
this.globalData.hasFullBook = mockUser.hasFullBook
|
||||
|
||||
wx.setStorageSync('userInfo', mockUser)
|
||||
wx.setStorageSync('token', mockToken)
|
||||
|
||||
console.log('模拟登录成功:', mockUser)
|
||||
|
||||
return { user: mockUser, token: mockToken }
|
||||
console.warn('[App] mockLogin已废弃,请使用真实登录')
|
||||
return null
|
||||
},
|
||||
|
||||
// 手机号登录
|
||||
@@ -385,14 +396,21 @@ App({
|
||||
wx.setStorageSync('userInfo', res.data.user)
|
||||
wx.setStorageSync('token', res.data.token)
|
||||
|
||||
// 登录成功后绑定推荐码
|
||||
const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
|
||||
if (pendingRef) {
|
||||
console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef)
|
||||
this.bindReferralCode(pendingRef)
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('手机号API登录失败,使用模拟登录:', e)
|
||||
console.log('[App] 手机号登录失败:', e)
|
||||
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||||
}
|
||||
|
||||
// 回退到模拟登录
|
||||
return this.mockLogin()
|
||||
return null
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
|
||||
@@ -550,6 +550,12 @@ Page({
|
||||
let paymentData = null
|
||||
|
||||
try {
|
||||
// 获取章节完整名称用于支付描述
|
||||
const sectionTitle = this.data.section?.title || sectionId
|
||||
const description = type === 'fullbook'
|
||||
? '《一场Soul的创业实验》全书'
|
||||
: `章节${sectionId}-${sectionTitle.length > 20 ? sectionTitle.slice(0, 20) + '...' : sectionTitle}`
|
||||
|
||||
const res = await app.request('/api/miniprogram/pay', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
@@ -557,7 +563,7 @@ Page({
|
||||
productType: type,
|
||||
productId: sectionId,
|
||||
amount,
|
||||
description: type === 'fullbook' ? '《一场Soul的创业实验》全书' : `章节-${sectionId}`,
|
||||
description,
|
||||
userId: app.globalData.userInfo?.id || ''
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user