feat(invite): 优化邀请参数解析和处理逻辑

- 重构 invite 工具函数,增强参数解析能力
- 添加参数调试工具,便于排查问题
- 优化用户登录后的邀请关系处理流程- 调整 API调用,统一使用 dealerId替代 refereeId
- 移除未使用的导入和冗余代码
This commit is contained in:
2025-08-26 18:53:14 +08:00
parent 0a6f21d182
commit 24c6e6c0fd
13 changed files with 358 additions and 64 deletions

View File

@@ -2,7 +2,7 @@
export const ENV_CONFIG = { export const ENV_CONFIG = {
// 开发环境 // 开发环境
development: { development: {
API_BASE_URL: 'http://127.0.0.1:9200/api', API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '开发环境', APP_NAME: '开发环境',
DEBUG: 'true', DEBUG: 'true',
}, },

View File

@@ -8,6 +8,7 @@ import navTo from "@/utils/common";
import {TenantId} from "@/config/app"; import {TenantId} from "@/config/app";
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon"; import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
import {useUser} from "@/hooks/useUser"; import {useUser} from "@/hooks/useUser";
import {getStoredInviteParams} from "@/utils/invite";
function UserCard() { function UserCard() {
const {getDisplayName, getRoleName} = useUser(); const {getDisplayName, getRoleName} = useUser();
@@ -134,6 +135,14 @@ function UserCard() {
/* 获取用户手机号 */ /* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => { const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
const {code, encryptedData, iv} = detail const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
console.log('Admin UserCard组件登录时检测到的邀请参数:', inviteParams)
console.log('Admin UserCard组件推荐人ID:', refereeId)
Taro.login({ Taro.login({
success: function () { success: function () {
if (code) { if (code) {
@@ -145,7 +154,7 @@ function UserCard() {
encryptedData, encryptedData,
iv, iv,
notVerifyPhone: true, notVerifyPhone: true,
refereeId: 0, refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee', sceneType: 'save_referee',
tenantId: TenantId tenantId: TenantId
}, },

View File

@@ -1,5 +1,6 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api'; import type { ApiResult, PageResult } from '@/api';
import { BaseUrl } from '@/config/app';
/** /**
* 小程序码生成参数 * 小程序码生成参数
@@ -38,7 +39,7 @@ export interface InviteRelationParam {
*/ */
export interface BindRefereeParam { export interface BindRefereeParam {
// 推荐人ID // 推荐人ID
refereeId: number; dealerId: number;
// 被推荐人ID (可选,如果不传则使用当前登录用户) // 被推荐人ID (可选,如果不传则使用当前登录用户)
userId?: number; userId?: number;
// 推荐来源 // 推荐来源
@@ -112,7 +113,7 @@ export async function generateMiniProgramCode(data: MiniProgramCodeParam) {
try { try {
const url = '/wx-login/getOrderQRCodeUnlimited/' + data.scene; const url = '/wx-login/getOrderQRCodeUnlimited/' + data.scene;
// 由于接口直接返回图片buffer我们直接构建完整的URL // 由于接口直接返回图片buffer我们直接构建完整的URL
return `${API_BASE_URL}${url}`; return `${BaseUrl}${url}`;
} catch (error: any) { } catch (error: any) {
throw new Error(error.message || '生成小程序码失败'); throw new Error(error.message || '生成小程序码失败');
} }
@@ -155,7 +156,7 @@ export async function bindRefereeRelation(data: BindRefereeParam) {
const res = await request.post<ApiResult<unknown>>( const res = await request.post<ApiResult<unknown>>(
'/shop/shop-dealer-referee', '/shop/shop-dealer-referee',
{ {
refereeId: data.refereeId, dealerId: data.dealerId,
userId: data.userId, userId: data.userId,
source: data.source || 'qrcode', source: data.source || 'qrcode',
scene: data.scene scene: data.scene

View File

@@ -6,7 +6,7 @@ import './app.scss'
import {loginByOpenId} from "@/api/layout"; import {loginByOpenId} from "@/api/layout";
import {TenantId} from "@/config/app"; import {TenantId} from "@/config/app";
import {saveStorageByLoginUser} from "@/utils/server"; import {saveStorageByLoginUser} from "@/utils/server";
import {parseInviteParams, saveInviteParams, trackInviteSource, handleInviteRelation} from "@/utils/invite"; import {parseInviteParams, saveInviteParams, trackInviteSource, handleInviteRelation, debugInviteInfo} from "@/utils/invite";
function App(props: { children: any; }) { function App(props: { children: any; }) {
const reload = () => { const reload = () => {
@@ -57,12 +57,13 @@ function App(props: { children: any; }) {
// 处理启动参数 // 处理启动参数
const handleLaunchOptions = (options: any) => { const handleLaunchOptions = (options: any) => {
try { try {
console.log('小程序启动参数:', options) console.log('=== 小程序启动参数处理开始 ===')
console.log('完整启动参数:', JSON.stringify(options, null, 2))
// 解析邀请参数 // 解析邀请参数
const inviteParams = parseInviteParams(options) const inviteParams = parseInviteParams(options)
if (inviteParams) { if (inviteParams) {
console.log('检测到邀请参数:', inviteParams) console.log('✅ 成功检测到邀请参数:', inviteParams)
// 保存邀请参数到本地存储 // 保存邀请参数到本地存储
saveInviteParams(inviteParams) saveInviteParams(inviteParams)
@@ -73,12 +74,21 @@ function App(props: { children: any; }) {
// 显示邀请提示 // 显示邀请提示
setTimeout(() => { setTimeout(() => {
Taro.showToast({ Taro.showToast({
title: '检测到邀请信息', title: `检测到邀请信息 ID:${inviteParams.inviter}`,
icon: 'success', icon: 'success',
duration: 2000 duration: 3000
}) })
}, 1000) }, 1000)
// 打印调试信息
setTimeout(() => {
debugInviteInfo()
}, 2000)
} else {
console.log('❌ 未检测到邀请参数')
} }
console.log('=== 小程序启动参数处理结束 ===')
} catch (error) { } catch (error) {
console.error('处理启动参数失败:', error) console.error('处理启动参数失败:', error)
} }

View File

@@ -39,6 +39,10 @@ export const useUserData = (): UseUserDataReturn => {
setLoading(true) setLoading(true)
setError(null) setError(null)
if(!Taro.getStorageSync('UserId')){
return;
}
// 并发请求所有数据 // 并发请求所有数据
const [userDataRes, couponsRes, giftCardsRes] = await Promise.all([ const [userDataRes, couponsRes, giftCardsRes] = await Promise.all([
getUserInfo(), getUserInfo(),

View File

@@ -8,7 +8,7 @@ import {TenantId} from "@/config/app";
import {getOrganization} from "@/api/system/organization"; import {getOrganization} from "@/api/system/organization";
import {myUserVerify} from "@/api/system/userVerify"; import {myUserVerify} from "@/api/system/userVerify";
import { useShopInfo } from '@/hooks/useShopInfo'; import { useShopInfo } from '@/hooks/useShopInfo';
import {handleInviteRelation} from "@/utils/invite"; import {handleInviteRelation, getStoredInviteParams} from "@/utils/invite";
import {View,Text} from '@tarojs/components' import {View,Text} from '@tarojs/components'
import MySearch from "./MySearch"; import MySearch from "./MySearch";
import './Header.scss'; import './Header.scss';
@@ -88,6 +88,14 @@ const Header = (props: any) => {
/* 获取用户手机号 */ /* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => { const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
const {code, encryptedData, iv} = detail const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
console.log('登录时检测到的邀请参数:', inviteParams)
console.log('推荐人ID:', refereeId)
Taro.login({ Taro.login({
success: function () { success: function () {
if (code) { if (code) {
@@ -99,7 +107,7 @@ const Header = (props: any) => {
encryptedData, encryptedData,
iv, iv,
notVerifyPhone: true, notVerifyPhone: true,
refereeId: 0, refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee', sceneType: 'save_referee',
tenantId: TenantId tenantId: TenantId
}, },

View File

@@ -10,7 +10,7 @@ import {myUserVerify} from "@/api/system/userVerify";
import {User} from "@/api/system/user/model"; import {User} from "@/api/system/user/model";
import { useShopInfo } from '@/hooks/useShopInfo'; import { useShopInfo } from '@/hooks/useShopInfo';
import { useUser } from '@/hooks/useUser'; import { useUser } from '@/hooks/useUser';
import {handleInviteRelation} from "@/utils/invite"; import {handleInviteRelation, getStoredInviteParams} from "@/utils/invite";
import MySearch from "./MySearch"; import MySearch from "./MySearch";
import './Header.scss'; import './Header.scss';
@@ -74,6 +74,14 @@ const Header = (props: any) => {
// 获取手机号授权 // 获取手机号授权
const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => { const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
const {code, encryptedData, iv} = detail const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
console.log('HeaderWithHook组件登录时检测到的邀请参数:', inviteParams)
console.log('HeaderWithHook组件推荐人ID:', refereeId)
Taro.login({ Taro.login({
success: function () { success: function () {
if (code) { if (code) {
@@ -85,7 +93,7 @@ const Header = (props: any) => {
encryptedData, encryptedData,
iv, iv,
notVerifyPhone: true, notVerifyPhone: true,
refereeId: 0, refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee', sceneType: 'save_referee',
tenantId: TenantId tenantId: TenantId
}, },

View File

@@ -4,7 +4,7 @@ import {Input, Radio, Button} from '@nutui/nutui-react-taro'
import {TenantId} from "@/config/app"; import {TenantId} from "@/config/app";
import './login.scss'; import './login.scss';
import {saveStorageByLoginUser} from "@/utils/server"; import {saveStorageByLoginUser} from "@/utils/server";
import {handleInviteRelation} from "@/utils/invite"; import {handleInviteRelation, getStoredInviteParams} from "@/utils/invite";
// 微信获取手机号回调参数类型 // 微信获取手机号回调参数类型
interface GetPhoneNumberDetail { interface GetPhoneNumberDetail {
@@ -40,6 +40,14 @@ const Login = (props: LoginProps) => {
/* 获取用户手机号 */ /* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: GetPhoneNumberEvent) => { const handleGetPhoneNumber = ({detail}: GetPhoneNumberEvent) => {
const {code, encryptedData, iv} = detail const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
console.log('Login组件登录时检测到的邀请参数:', inviteParams)
console.log('Login组件推荐人ID:', refereeId)
Taro.login({ Taro.login({
success: function () { success: function () {
if (code) { if (code) {
@@ -51,7 +59,7 @@ const Login = (props: LoginProps) => {
encryptedData, encryptedData,
iv, iv,
notVerifyPhone: true, notVerifyPhone: true,
refereeId: 0, refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee', sceneType: 'save_referee',
tenantId: TenantId tenantId: TenantId
}, },

View File

@@ -11,6 +11,7 @@ import {TenantId} from "@/config/app";
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon"; import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
import {useUser} from "@/hooks/useUser"; import {useUser} from "@/hooks/useUser";
import {useUserData} from "@/hooks/useUserData"; import {useUserData} from "@/hooks/useUserData";
import {getStoredInviteParams} from "@/utils/invite";
function UserCard() { function UserCard() {
const { const {
@@ -151,6 +152,14 @@ function UserCard() {
/* 获取用户手机号 */ /* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => { const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
const {code, encryptedData, iv} = detail const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
console.log('UserCard组件登录时检测到的邀请参数:', inviteParams)
console.log('UserCard组件推荐人ID:', refereeId)
Taro.login({ Taro.login({
success: function () { success: function () {
if (code) { if (code) {
@@ -162,7 +171,7 @@ function UserCard() {
encryptedData, encryptedData,
iv, iv,
notVerifyPhone: true, notVerifyPhone: true,
refereeId: 0, refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee', sceneType: 'save_referee',
tenantId: TenantId tenantId: TenantId
}, },

View File

@@ -738,20 +738,20 @@ function OrderList(props: OrderListProps) {
)} )}
{/* 待发货状态:显示申请退款 */} {/* 待发货状态:显示申请退款 */}
{item.payStatus && item.deliveryStatus === 10 && item.orderStatus !== 2 && item.orderStatus !== 4 && ( {/*{item.payStatus && item.deliveryStatus === 10 && item.orderStatus !== 2 && item.orderStatus !== 4 && (*/}
<Button size={'small'} onClick={(e) => { {/* <Button size={'small'} onClick={(e) => {*/}
e.stopPropagation(); {/* e.stopPropagation();*/}
applyRefund(item); {/* applyRefund(item);*/}
}}>退</Button> {/* }}>申请退款</Button>*/}
)} {/*)}*/}
{/* 待收货状态:显示查看物流和确认收货 */} {/* 待收货状态:显示查看物流和确认收货 */}
{item.deliveryStatus === 20 && item.orderStatus !== 2 && ( {item.deliveryStatus === 20 && item.orderStatus !== 2 && (
<Space> <Space>
<Button size={'small'} onClick={(e) => { {/*<Button size={'small'} onClick={(e) => {*/}
e.stopPropagation(); {/* e.stopPropagation();*/}
viewLogistics(item); {/* viewLogistics(item);*/}
}}></Button> {/*}}>查看物流</Button>*/}
<Button size={'small'} type="primary" onClick={(e) => { <Button size={'small'} type="primary" onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
confirmReceive(item); confirmReceive(item);
@@ -766,24 +766,24 @@ function OrderList(props: OrderListProps) {
e.stopPropagation(); e.stopPropagation();
buyAgain(item); buyAgain(item);
}}></Button> }}></Button>
<Button size={'small'} onClick={(e) => { {/*<Button size={'small'} onClick={(e) => {*/}
e.stopPropagation(); {/* e.stopPropagation();*/}
evaluateGoods(item); {/* evaluateGoods(item);*/}
}}></Button> {/*}}>评价商品</Button>*/}
<Button size={'small'} onClick={(e) => { {/*<Button size={'small'} onClick={(e) => {*/}
e.stopPropagation(); {/* e.stopPropagation();*/}
applyRefund(item); {/* applyRefund(item);*/}
}}>退</Button> {/*}}>申请退款</Button>*/}
</Space> </Space>
)} )}
{/* 退款/售后状态:显示查看进度和撤销申请 */} {/* 退款/售后状态:显示查看进度和撤销申请 */}
{(item.orderStatus === 4 || item.orderStatus === 7) && ( {(item.orderStatus === 4 || item.orderStatus === 7) && (
<Space> <Space>
<Button size={'small'} onClick={(e) => { {/*<Button size={'small'} onClick={(e) => {*/}
e.stopPropagation(); {/* e.stopPropagation();*/}
viewProgress(item); {/* viewProgress(item);*/}
}}></Button> {/*}}>查看进度</Button>*/}
</Space> </Space>
)} )}

View File

@@ -15,22 +15,41 @@ export interface InviteParams {
*/ */
export function parseInviteParams(options: any): InviteParams | null { export function parseInviteParams(options: any): InviteParams | null {
try { 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 参数中解析邀请信息 // 从 scene 参数中解析邀请信息
if (options.scene) { if (sceneStr) {
// 确保 scene 是字符串类型
const sceneStr = typeof options.scene === 'string' ? options.scene : String(options.scene)
console.log('解析scene参数:', sceneStr)
// 处理 uid_xxx 格式的邀请码 // 处理 uid_xxx 格式的邀请码
if (sceneStr.startsWith('uid_')) { if (sceneStr.startsWith('uid_')) {
const inviterId = sceneStr.replace('uid_', '') const inviterId = sceneStr.replace('uid_', '')
console.log('提取的邀请人ID:', inviterId)
if (inviterId && !isNaN(parseInt(inviterId))) { if (inviterId && !isNaN(parseInt(inviterId))) {
console.log('检测到uid格式邀请码:', inviterId) const parsedInviterId = parseInt(inviterId)
return { console.log('检测到uid格式邀请码邀请人ID:', parsedInviterId)
const inviteParams = {
inviter: inviterId, inviter: inviterId,
source: 'qrcode', source: 'qrcode',
t: Date.now().toString() t: Date.now().toString()
} }
console.log('返回的邀请参数:', inviteParams)
return inviteParams
} else {
console.log('邀请人ID无效:', inviterId)
} }
} }
@@ -59,6 +78,8 @@ export function parseInviteParams(options: any): InviteParams | null {
console.log('检测到传统格式邀请码:', params) console.log('检测到传统格式邀请码:', params)
return params return params
} }
} else {
console.log('options中没有scene参数或query.scene参数')
} }
// 从 query 参数中解析邀请信息(兼容旧版本) // 从 query 参数中解析邀请信息(兼容旧版本)
@@ -81,11 +102,17 @@ export function parseInviteParams(options: any): InviteParams | null {
*/ */
export function saveInviteParams(params: InviteParams) { export function saveInviteParams(params: InviteParams) {
try { try {
Taro.setStorageSync('invite_params', { const saveData = {
...params, ...params,
timestamp: Date.now() timestamp: Date.now()
}) }
console.log('邀请参数已保存:', params)
Taro.setStorageSync('invite_params', saveData)
console.log('邀请参数已保存到本地存储:', saveData)
// 验证保存是否成功
const saved = Taro.getStorageSync('invite_params')
console.log('验证保存结果:', saved)
} catch (error) { } catch (error) {
console.error('保存邀请参数失败:', error) console.error('保存邀请参数失败:', error)
} }
@@ -97,21 +124,28 @@ export function saveInviteParams(params: InviteParams) {
export function getStoredInviteParams(): InviteParams | null { export function getStoredInviteParams(): InviteParams | null {
try { try {
const stored = Taro.getStorageSync('invite_params') const stored = Taro.getStorageSync('invite_params')
console.log('从本地存储获取的邀请参数:', stored)
if (stored && stored.inviter) { if (stored && stored.inviter) {
// 检查是否过期24小时 // 检查是否过期24小时
const now = Date.now() const now = Date.now()
const expireTime = 24 * 60 * 60 * 1000 // 24小时 const expireTime = 24 * 60 * 60 * 1000 // 24小时
if (now - stored.timestamp < expireTime) { if (now - stored.timestamp < expireTime) {
return { const result = {
inviter: stored.inviter, inviter: stored.inviter,
source: stored.source || 'unknown', source: stored.source || 'unknown',
t: stored.t t: stored.t
} }
console.log('返回有效的邀请参数:', result)
return result
} else { } else {
console.log('邀请参数已过期,清除本地存储')
// 过期则清除 // 过期则清除
clearInviteParams() clearInviteParams()
} }
} else {
console.log('本地存储中没有有效的邀请参数')
} }
return null return null
} catch (error) { } catch (error) {
@@ -159,7 +193,7 @@ export async function handleInviteRelation(userId: number): Promise<boolean> {
// 使用新的绑定推荐关系接口 // 使用新的绑定推荐关系接口
await bindRefereeRelation({ await bindRefereeRelation({
refereeId: inviterId, dealerId: inviterId,
userId: userId, userId: userId,
source: inviteParams.source || 'qrcode', source: inviteParams.source || 'qrcode',
scene: inviteParams.source === 'qrcode' ? `uid_${inviterId}` : `inviter=${inviterId}&source=${inviteParams.source}&t=${inviteParams.t}` scene: inviteParams.source === 'qrcode' ? `uid_${inviterId}` : `inviter=${inviterId}&source=${inviteParams.source}&t=${inviteParams.t}`
@@ -275,20 +309,68 @@ export function trackInviteSource(source: string, inviterId?: number) {
} }
} }
/**
* 调试工具:打印所有邀请相关的存储信息
*/
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> { export async function checkAndHandleInviteRelation(): Promise<boolean> {
try { try {
// 打印调试信息
debugInviteInfo()
// 获取当前用户信息 // 获取当前用户信息
const userInfo = Taro.getStorageSync('userInfo') const userInfo = Taro.getStorageSync('userInfo')
if (!userInfo || !userInfo.userId) { const userId = Taro.getStorageSync('UserId')
const finalUserId = userId || userInfo?.userId
if (!finalUserId) {
console.log('用户未登录,无法处理邀请关系') console.log('用户未登录,无法处理邀请关系')
return false return false
} }
return await handleInviteRelation(userInfo.userId) console.log('使用用户ID处理邀请关系:', finalUserId)
return await handleInviteRelation(parseInt(finalUserId))
} catch (error) { } catch (error) {
console.error('检查邀请关系失败:', error) console.error('检查邀请关系失败:', error)
return false return false
@@ -353,7 +435,7 @@ export async function bindReferee(refereeId: number, userId?: number, source: st
} }
await bindRefereeRelation({ await bindRefereeRelation({
refereeId: refereeId, dealerId: refereeId,
userId: targetUserId, userId: targetUserId,
source: source, source: source,
scene: source === 'qrcode' ? `uid_${refereeId}` : undefined scene: source === 'qrcode' ? `uid_${refereeId}` : undefined

166
src/utils/test-invite.ts Normal file
View File

@@ -0,0 +1,166 @@
/**
* 邀请参数解析测试工具
*/
import { parseInviteParams } from './invite'
/**
* 测试不同格式的邀请参数解析
*/
export function testInviteParamsParsing() {
console.log('=== 开始测试邀请参数解析 ===')
// 测试用例1: uid_格式
const testCase1 = {
scene: 'uid_33103',
path: 'pages/index/index'
}
console.log('测试用例1 - uid格式:')
console.log('输入:', testCase1)
const result1 = parseInviteParams(testCase1)
console.log('输出:', result1)
console.log('预期: { inviter: "33103", source: "qrcode", t: "..." }')
console.log('结果:', result1?.inviter === '33103' && result1?.source === 'qrcode' ? '✅ 通过' : '❌ 失败')
console.log('')
// 测试用例2: 传统格式
const testCase2 = {
scene: 'inviter=12345&source=share&t=1640995200000',
path: 'pages/index/index'
}
console.log('测试用例2 - 传统格式:')
console.log('输入:', testCase2)
const result2 = parseInviteParams(testCase2)
console.log('输出:', result2)
console.log('预期: { inviter: "12345", source: "share", t: "1640995200000" }')
console.log('结果:', result2?.inviter === '12345' && result2?.source === 'share' ? '✅ 通过' : '❌ 失败')
console.log('')
// 测试用例3: 数字类型的scene
const testCase3 = {
scene: 1047, // 数字类型
path: 'pages/index/index'
}
console.log('测试用例3 - 数字类型scene:')
console.log('输入:', testCase3)
const result3 = parseInviteParams(testCase3)
console.log('输出:', result3)
console.log('预期: null (因为不是uid_格式)')
console.log('结果:', result3 === null ? '✅ 通过' : '❌ 失败')
console.log('')
// 测试用例4: 空参数
const testCase4 = {}
console.log('测试用例4 - 空参数:')
console.log('输入:', testCase4)
const result4 = parseInviteParams(testCase4)
console.log('输出:', result4)
console.log('预期: null')
console.log('结果:', result4 === null ? '✅ 通过' : '❌ 失败')
console.log('')
// 测试用例5: 无效的uid格式
const testCase5 = {
scene: 'uid_abc',
path: 'pages/index/index'
}
console.log('测试用例5 - 无效uid格式:')
console.log('输入:', testCase5)
const result5 = parseInviteParams(testCase5)
console.log('输出:', result5)
console.log('预期: null (因为abc不是数字)')
console.log('结果:', result5 === null ? '✅ 通过' : '❌ 失败')
console.log('')
// 测试用例6: referrer参数
const testCase6 = {
referrer: '99999',
path: 'pages/index/index'
}
console.log('测试用例6 - referrer参数:')
console.log('输入:', testCase6)
const result6 = parseInviteParams(testCase6)
console.log('输出:', result6)
console.log('预期: { inviter: "99999", source: "link" }')
console.log('结果:', result6?.inviter === '99999' && result6?.source === 'link' ? '✅ 通过' : '❌ 失败')
console.log('')
console.log('=== 邀请参数解析测试完成 ===')
}
/**
* 模拟小程序启动场景测试
*/
export function simulateMiniProgramLaunch() {
console.log('=== 模拟小程序启动场景 ===')
// 模拟通过小程序码启动
const qrcodeOptions = {
path: 'pages/index/index',
scene: 'uid_33103',
shareTicket: undefined,
referrerInfo: {}
}
console.log('模拟小程序码启动:')
console.log('启动参数:', qrcodeOptions)
const qrcodeResult = parseInviteParams(qrcodeOptions)
console.log('解析结果:', qrcodeResult)
if (qrcodeResult && qrcodeResult.inviter === '33103') {
console.log('✅ 小程序码邀请解析成功')
return qrcodeResult
} else {
console.log('❌ 小程序码邀请解析失败')
return null
}
}
/**
* 验证邀请参数格式
*/
export function validateInviteParams(params: any) {
console.log('=== 验证邀请参数格式 ===')
console.log('参数:', params)
if (!params) {
console.log('❌ 参数为空')
return false
}
if (!params.inviter) {
console.log('❌ 缺少inviter字段')
return false
}
if (isNaN(parseInt(params.inviter))) {
console.log('❌ inviter不是有效数字')
return false
}
if (!params.source) {
console.log('❌ 缺少source字段')
return false
}
console.log('✅ 邀请参数格式验证通过')
return true
}
/**
* 运行所有测试
*/
export function runAllTests() {
console.log('🚀 开始运行所有邀请参数测试')
testInviteParamsParsing()
const simulationResult = simulateMiniProgramLaunch()
if (simulationResult) {
validateInviteParams(simulationResult)
}
console.log('🎉 所有测试完成')
}

View File

@@ -1,11 +0,0 @@
#!/bin/bash
# 设置代理(根据你的 Clash Verge 配置)
export http_proxy=http://127.0.0.1:7897
export https_proxy=http://127.0.0.1:7897
# 启动 Claude Code
echo "🚀 启动 Claude Code..."
echo "📡 使用代理: $http_proxy"
npx @anthropic-ai/claude-code