- 新增 bindRefereeRelation 接口替换原有的 createInviteRelation 接口 - 优化邀请参数解析逻辑,支持 uid_xxx 格式的邀请码 - 重构 handleInviteRelation 函数,使用新的绑定推荐关系接口 - 新增 checkAndHandleInviteRelation 和 manualHandleInviteRelation 函数 - 优化首页和订单列表的相关逻辑,以支持新的邀请关系建立流程 - 更新文档中的相关描述,如将"下级成员"改为"团队成员"
385 lines
9.6 KiB
TypeScript
385 lines
9.6 KiB
TypeScript
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 {
|
||
// 从 scene 参数中解析邀请信息
|
||
if (options.scene) {
|
||
// 确保 scene 是字符串类型
|
||
const sceneStr = typeof options.scene === 'string' ? options.scene : String(options.scene)
|
||
console.log('解析scene参数:', sceneStr)
|
||
|
||
// 处理 uid_xxx 格式的邀请码
|
||
if (sceneStr.startsWith('uid_')) {
|
||
const inviterId = sceneStr.replace('uid_', '')
|
||
if (inviterId && !isNaN(parseInt(inviterId))) {
|
||
console.log('检测到uid格式邀请码:', inviterId)
|
||
return {
|
||
inviter: inviterId,
|
||
source: 'qrcode',
|
||
t: Date.now().toString()
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理传统的 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
|
||
}
|
||
}
|
||
|
||
// 从 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 {
|
||
Taro.setStorageSync('invite_params', {
|
||
...params,
|
||
timestamp: Date.now()
|
||
})
|
||
console.log('邀请参数已保存:', params)
|
||
} catch (error) {
|
||
console.error('保存邀请参数失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取本地存储的邀请信息
|
||
*/
|
||
export function getStoredInviteParams(): InviteParams | null {
|
||
try {
|
||
const stored = Taro.getStorageSync('invite_params')
|
||
if (stored && stored.inviter) {
|
||
// 检查是否过期(24小时)
|
||
const now = Date.now()
|
||
const expireTime = 24 * 60 * 60 * 1000 // 24小时
|
||
|
||
if (now - stored.timestamp < expireTime) {
|
||
return {
|
||
inviter: stored.inviter,
|
||
source: stored.source || 'unknown',
|
||
t: stored.t
|
||
}
|
||
} else {
|
||
// 过期则清除
|
||
clearInviteParams()
|
||
}
|
||
}
|
||
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({
|
||
refereeId: 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 async function checkAndHandleInviteRelation(): Promise<boolean> {
|
||
try {
|
||
// 获取当前用户信息
|
||
const userInfo = Taro.getStorageSync('userInfo')
|
||
if (!userInfo || !userInfo.userId) {
|
||
console.log('用户未登录,无法处理邀请关系')
|
||
return false
|
||
}
|
||
|
||
return await handleInviteRelation(userInfo.userId)
|
||
} 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({
|
||
refereeId: 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
|
||
}
|
||
}
|