Files
glt-taro/src/utils/invite.ts
赵忠林 24c6e6c0fd feat(invite): 优化邀请参数解析和处理逻辑
- 重构 invite 工具函数,增强参数解析能力
- 添加参数调试工具,便于排查问题
- 优化用户登录后的邀请关系处理流程- 调整 API调用,统一使用 dealerId替代 refereeId
- 移除未使用的导入和冗余代码
2025-08-26 18:53:14 +08:00

467 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Taro from '@tarojs/taro'
import { bindRefereeRelation } from '@/api/invite'
/**
* 邀请参数接口
*/
export interface InviteParams {
inviter?: string;
source?: string;
t?: string;
}
/**
* 解析小程序启动参数中的邀请信息
*/
export function parseInviteParams(options: any): InviteParams | null {
try {
console.log('开始解析邀请参数完整options:', JSON.stringify(options))
// 优先从 query.scene 参数中解析邀请信息
let sceneStr = null
if (options.query && options.query.scene) {
sceneStr = typeof options.query.scene === 'string' ? options.query.scene : String(options.query.scene)
console.log('从query.scene解析参数:', sceneStr, '类型:', typeof sceneStr)
} else if (options.scene) {
// 兼容直接从 scene 参数解析
sceneStr = typeof options.scene === 'string' ? options.scene : String(options.scene)
console.log('从options.scene解析参数:', sceneStr, '类型:', typeof sceneStr)
}
// 从 scene 参数中解析邀请信息
if (sceneStr) {
// 处理 uid_xxx 格式的邀请码
if (sceneStr.startsWith('uid_')) {
const inviterId = sceneStr.replace('uid_', '')
console.log('提取的邀请人ID:', inviterId)
if (inviterId && !isNaN(parseInt(inviterId))) {
const parsedInviterId = parseInt(inviterId)
console.log('检测到uid格式邀请码邀请人ID:', parsedInviterId)
const inviteParams = {
inviter: inviterId,
source: 'qrcode',
t: Date.now().toString()
}
console.log('返回的邀请参数:', inviteParams)
return inviteParams
} else {
console.log('邀请人ID无效:', inviterId)
}
}
// 处理传统的 key=value&key=value 格式
const params: InviteParams = {}
const pairs = sceneStr.split('&')
pairs.forEach((pair: string) => {
const [key, value] = pair.split('=')
if (key && value) {
switch (key) {
case 'inviter':
params.inviter = decodeURIComponent(value)
break
case 'source':
params.source = decodeURIComponent(value)
break
case 't':
params.t = decodeURIComponent(value)
break
}
}
})
if (params.inviter) {
console.log('检测到传统格式邀请码:', params)
return params
}
} else {
console.log('options中没有scene参数或query.scene参数')
}
// 从 query 参数中解析邀请信息(兼容旧版本)
if (options.referrer) {
return {
inviter: options.referrer,
source: 'link'
}
}
return null
} catch (error) {
console.error('解析邀请参数失败:', error)
return null
}
}
/**
* 保存邀请信息到本地存储
*/
export function saveInviteParams(params: InviteParams) {
try {
const saveData = {
...params,
timestamp: Date.now()
}
Taro.setStorageSync('invite_params', saveData)
console.log('邀请参数已保存到本地存储:', saveData)
// 验证保存是否成功
const saved = Taro.getStorageSync('invite_params')
console.log('验证保存结果:', saved)
} catch (error) {
console.error('保存邀请参数失败:', error)
}
}
/**
* 获取本地存储的邀请信息
*/
export function getStoredInviteParams(): InviteParams | null {
try {
const stored = Taro.getStorageSync('invite_params')
console.log('从本地存储获取的邀请参数:', stored)
if (stored && stored.inviter) {
// 检查是否过期24小时
const now = Date.now()
const expireTime = 24 * 60 * 60 * 1000 // 24小时
if (now - stored.timestamp < expireTime) {
const result = {
inviter: stored.inviter,
source: stored.source || 'unknown',
t: stored.t
}
console.log('返回有效的邀请参数:', result)
return result
} else {
console.log('邀请参数已过期,清除本地存储')
// 过期则清除
clearInviteParams()
}
} else {
console.log('本地存储中没有有效的邀请参数')
}
return null
} catch (error) {
console.error('获取邀请参数失败:', error)
return null
}
}
/**
* 清除本地存储的邀请信息
*/
export function clearInviteParams() {
try {
Taro.removeStorageSync('invite_params')
console.log('邀请参数已清除')
} catch (error) {
console.error('清除邀请参数失败:', error)
}
}
/**
* 处理邀请关系建立
*/
export async function handleInviteRelation(userId: number): Promise<boolean> {
try {
console.log('开始处理邀请关系当前用户ID:', userId)
const inviteParams = getStoredInviteParams()
if (!inviteParams || !inviteParams.inviter) {
console.log('没有找到邀请参数,跳过邀请关系建立')
return false
}
console.log('找到邀请参数:', inviteParams)
const inviterId = parseInt(inviteParams.inviter)
if (isNaN(inviterId) || inviterId === userId) {
// 邀请人ID无效或自己邀请自己
console.log('邀请人ID无效或自己邀请自己清除邀请参数')
clearInviteParams()
return false
}
console.log(`准备建立邀请关系: ${inviterId} -> ${userId}`)
// 使用新的绑定推荐关系接口
await bindRefereeRelation({
dealerId: inviterId,
userId: userId,
source: inviteParams.source || 'qrcode',
scene: inviteParams.source === 'qrcode' ? `uid_${inviterId}` : `inviter=${inviterId}&source=${inviteParams.source}&t=${inviteParams.t}`
})
// 清除本地存储的邀请参数
clearInviteParams()
console.log(`邀请关系建立成功: ${inviterId} -> ${userId}`)
// 显示成功提示
setTimeout(() => {
Taro.showToast({
title: '邀请关系建立成功',
icon: 'success',
duration: 2000
})
}, 500)
return true
} catch (error) {
console.error('建立邀请关系失败:', error)
// 显示错误提示
setTimeout(() => {
Taro.showToast({
title: '邀请关系建立失败',
icon: 'error',
duration: 2000
})
}, 500)
return false
}
}
/**
* 检查是否有待处理的邀请
*/
export function hasPendingInvite(): boolean {
const params = getStoredInviteParams()
return !!(params && params.inviter)
}
/**
* 获取邀请来源的显示名称
*/
export function getSourceDisplayName(source: string): string {
const sourceMap: Record<string, string> = {
'qrcode': '小程序码',
'link': '分享链接',
'share': '好友分享',
'poster': '海报分享',
'unknown': '未知来源'
}
return sourceMap[source] || source
}
/**
* 验证邀请码格式
*/
export function validateInviteCode(scene: string): boolean {
try {
if (!scene) return false
// 检查是否包含必要的参数
const hasInviter = scene.includes('inviter=')
const hasSource = scene.includes('source=')
return hasInviter && hasSource
} catch (error) {
return false
}
}
/**
* 生成邀请场景值
*/
export function generateInviteScene(inviterId: number, source: string): string {
const timestamp = Date.now()
return `inviter=${inviterId}&source=${source}&t=${timestamp}`
}
/**
* 统计邀请来源
*/
export function trackInviteSource(source: string, inviterId?: number) {
try {
// 记录邀请来源统计
const trackData = {
source,
inviterId,
timestamp: Date.now(),
userAgent: Taro.getSystemInfoSync()
}
// 可以发送到统计服务
console.log('邀请来源统计:', trackData)
// 暂存到本地,后续可批量上报
const existingTracks = Taro.getStorageSync('invite_tracks') || []
existingTracks.push(trackData)
// 只保留最近100条记录
if (existingTracks.length > 100) {
existingTracks.splice(0, existingTracks.length - 100)
}
Taro.setStorageSync('invite_tracks', existingTracks)
} catch (error) {
console.error('统计邀请来源失败:', error)
}
}
/**
* 调试工具:打印所有邀请相关的存储信息
*/
export function debugInviteInfo() {
try {
console.log('=== 邀请参数调试信息 ===')
// 获取启动参数
const launchOptions = Taro.getLaunchOptionsSync()
console.log('启动参数:', JSON.stringify(launchOptions, null, 2))
// 获取存储的邀请参数
const storedParams = Taro.getStorageSync('invite_params')
console.log('存储的邀请参数:', JSON.stringify(storedParams, null, 2))
// 获取用户信息
const userId = Taro.getStorageSync('UserId')
const userInfo = Taro.getStorageSync('userInfo')
console.log('用户ID:', userId)
console.log('用户信息:', JSON.stringify(userInfo, null, 2))
// 获取邀请统计
const inviteTracks = Taro.getStorageSync('invite_tracks')
console.log('邀请统计:', JSON.stringify(inviteTracks, null, 2))
console.log('=== 调试信息结束 ===')
return {
launchOptions,
storedParams,
userId,
userInfo,
inviteTracks
}
} catch (error) {
console.error('获取调试信息失败:', error)
return null
}
}
/**
* 检查并处理当前用户的邀请关系
* 用于在用户登录后立即检查是否需要建立邀请关系
*/
export async function checkAndHandleInviteRelation(): Promise<boolean> {
try {
// 打印调试信息
debugInviteInfo()
// 获取当前用户信息
const userInfo = Taro.getStorageSync('userInfo')
const userId = Taro.getStorageSync('UserId')
const finalUserId = userId || userInfo?.userId
if (!finalUserId) {
console.log('用户未登录,无法处理邀请关系')
return false
}
console.log('使用用户ID处理邀请关系:', finalUserId)
return await handleInviteRelation(parseInt(finalUserId))
} catch (error) {
console.error('检查邀请关系失败:', error)
return false
}
}
/**
* 手动触发邀请关系建立
* 用于在特定页面或时机手动建立邀请关系
*/
export async function manualHandleInviteRelation(userId: number): Promise<boolean> {
try {
console.log('手动触发邀请关系建立用户ID:', userId)
const inviteParams = getStoredInviteParams()
if (!inviteParams || !inviteParams.inviter) {
console.log('没有待处理的邀请参数')
return false
}
const result = await handleInviteRelation(userId)
if (result) {
// 显示成功提示
Taro.showModal({
title: '邀请成功',
content: '您已成功加入邀请人的团队!',
showCancel: false,
confirmText: '知道了'
})
}
return result
} catch (error) {
console.error('手动处理邀请关系失败:', error)
return false
}
}
/**
* 直接绑定推荐关系
* 用于直接调用绑定推荐关系接口
*/
export async function bindReferee(refereeId: number, userId?: number, source: string = 'qrcode'): Promise<boolean> {
try {
console.log('直接绑定推荐关系:', { refereeId, userId, source })
// 如果没有传入userId尝试从本地存储获取
let targetUserId = userId
if (!targetUserId) {
const userInfo = Taro.getStorageSync('userInfo')
if (userInfo && userInfo.userId) {
targetUserId = userInfo.userId
} else {
throw new Error('无法获取用户ID')
}
}
// 防止自己推荐自己
if (refereeId === targetUserId) {
throw new Error('不能推荐自己')
}
await bindRefereeRelation({
dealerId: refereeId,
userId: targetUserId,
source: source,
scene: source === 'qrcode' ? `uid_${refereeId}` : undefined
})
console.log(`推荐关系绑定成功: ${refereeId} -> ${targetUserId}`)
// 显示成功提示
Taro.showToast({
title: '推荐关系绑定成功',
icon: 'success',
duration: 2000
})
return true
} catch (error: any) {
console.error('绑定推荐关系失败:', error)
// 显示错误提示
Taro.showToast({
title: error.message || '绑定推荐关系失败',
icon: 'error',
duration: 2000
})
return false
}
}