From 395501e961091f87e472dab0f6d117085afbcaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=8B=A5?= Date: Thu, 29 Jan 2026 11:11:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=86=E9=94=80?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8D=E6=98=BE=E7=A4=BA=E5=92=8C=E7=94=A8?= =?UTF-8?q?=E6=88=B7ID=E5=94=AF=E4=B8=80=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 分销数据修复: - 添加访问记录API,即使未登录也记录访问 - 登录成功后自动绑定推荐码 - 在链接访问时立即记录访问统计 2. 支付商品名称修复: - 支付时显示完整章节名称而非仅ID - 格式:章节9.12-美业整合:一个人的公司如何月入十万 3. 用户ID唯一性修复: - 移除mockLogin,必须使用真实微信登录 - 用户ID使用openId作为唯一标识 - 同一微信用户每次登录都是同一ID --- app/api/referral/visit/route.ts | 100 ++++++++++++++++++++++++++++ miniprogram/app.js | 112 ++++++++++++++++++-------------- miniprogram/pages/read/read.js | 8 ++- 3 files changed, 172 insertions(+), 48 deletions(-) create mode 100644 app/api/referral/visit/route.ts diff --git a/app/api/referral/visit/route.ts b/app/api/referral/visit/route.ts new file mode 100644 index 0000000..2f56790 --- /dev/null +++ b/app/api/referral/visit/route.ts @@ -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: '已处理' + }) + } +} diff --git a/miniprogram/app.js b/miniprogram/app.js index 358818b..e0c3901 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -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 }, // 退出登录 diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index 8de44cf..13f9b1b 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -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 || '' } })