feat(vip): 实现VIP服务权限控制和食物识别功能限制
- 添加VIP服务权限检查hook,支持免费使用次数限制 - 为食物识别功能添加登录验证和VIP权限检查 - 优化RevenueCat用户标识同步逻辑 - 修复会员购买状态检查的类型安全问题 - 为营养成分分析添加登录验证
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
import { HeaderBar } from '@/components/ui/HeaderBar';
|
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
|
import { useMembershipModal } from '@/contexts/MembershipModalContext';
|
||||||
import { useAppDispatch } from '@/hooks/redux';
|
import { useAppDispatch } from '@/hooks/redux';
|
||||||
|
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||||
import { useCosUpload } from '@/hooks/useCosUpload';
|
import { useCosUpload } from '@/hooks/useCosUpload';
|
||||||
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
||||||
|
import { useVipService } from '@/hooks/useVipService';
|
||||||
import { recognizeFood } from '@/services/foodRecognition';
|
import { recognizeFood } from '@/services/foodRecognition';
|
||||||
import { saveRecognitionResult, setError, setLoading } from '@/store/foodRecognitionSlice';
|
import { saveRecognitionResult, setError, setLoading } from '@/store/foodRecognitionSlice';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
@@ -37,6 +40,11 @@ export default function FoodRecognitionScreen() {
|
|||||||
const [currentStep, setCurrentStep] = useState<'idle' | 'uploading' | 'recognizing' | 'completed' | 'failed'>('idle');
|
const [currentStep, setCurrentStep] = useState<'idle' | 'uploading' | 'recognizing' | 'completed' | 'failed'>('idle');
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
// 添加认证和VIP服务相关hooks
|
||||||
|
const { ensureLoggedIn } = useAuthGuard();
|
||||||
|
const { handleServiceAccess } = useVipService();
|
||||||
|
const { openMembershipModal } = useMembershipModal();
|
||||||
|
|
||||||
// 动画引用
|
// 动画引用
|
||||||
const scaleAnim = useRef(new Animated.Value(1)).current;
|
const scaleAnim = useRef(new Animated.Value(1)).current;
|
||||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||||
@@ -125,6 +133,28 @@ export default function FoodRecognitionScreen() {
|
|||||||
})
|
})
|
||||||
]).start();
|
]).start();
|
||||||
|
|
||||||
|
// 先验证登录状态
|
||||||
|
const isLoggedIn = await ensureLoggedIn();
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户是否可以使用 AI 食物识别功能
|
||||||
|
const canAccess = handleServiceAccess(
|
||||||
|
() => {
|
||||||
|
// 允许使用,继续执行识别流程
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// 不允许使用,显示会员付费弹窗
|
||||||
|
openMembershipModal();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果用户没有权限,直接返回
|
||||||
|
if (!canAccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setShowRecognitionProcess(true);
|
setShowRecognitionProcess(true);
|
||||||
setRecognitionLogs([]);
|
setRecognitionLogs([]);
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ export function NutritionRadarCard({
|
|||||||
style={styles.foodOptionItem}
|
style={styles.foodOptionItem}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
triggerLightHaptic();
|
triggerLightHaptic();
|
||||||
router.push(`${ROUTES.NUTRITION_LABEL_ANALYSIS}?mealType=${currentMealType}`);
|
pushIfAuthedElseLogin(`${ROUTES.NUTRITION_LABEL_ANALYSIS}?mealType=${currentMealType}`);
|
||||||
}}
|
}}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -72,6 +72,23 @@ const DEFAULT_PLANS: MembershipPlan[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// RevenueCat 在 JS SDK 中以 string[] 返回 activeSubscriptions,但保持健壮性以防类型变化
|
||||||
|
const getActiveSubscriptionIds = (customerInfo: CustomerInfo): string[] => {
|
||||||
|
const activeSubscriptions = customerInfo.activeSubscriptions as unknown;
|
||||||
|
|
||||||
|
if (Array.isArray(activeSubscriptions)) {
|
||||||
|
return activeSubscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSubscriptions && typeof activeSubscriptions === 'object') {
|
||||||
|
return Object.values(activeSubscriptions as Record<string, string>).filter(
|
||||||
|
(value): value is string => typeof value === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
// 权限类型枚举
|
// 权限类型枚举
|
||||||
type PermissionType = 'exclusive' | 'limited' | 'unlimited';
|
type PermissionType = 'exclusive' | 'limited' | 'unlimited';
|
||||||
|
|
||||||
@@ -356,10 +373,11 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
const listener = (customerInfo: CustomerInfo) => {
|
const listener = (customerInfo: CustomerInfo) => {
|
||||||
log.info('购买状态变化监听器触发', { customerInfo, visible });
|
log.info('购买状态变化监听器触发', { customerInfo, visible });
|
||||||
|
|
||||||
|
const activeSubscriptionIds = getActiveSubscriptionIds(customerInfo);
|
||||||
// 检查是否有有效的购买记录
|
// 检查是否有有效的购买记录
|
||||||
const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0;
|
const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0;
|
||||||
const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0;
|
const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0;
|
||||||
const hasActiveSubscriptions = Object.keys(customerInfo.activeSubscriptions).length > 0;
|
const hasActiveSubscriptions = activeSubscriptionIds.length > 0;
|
||||||
|
|
||||||
if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) {
|
if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) {
|
||||||
capturePurchaseEvent('success', '监听到购买状态变化', {
|
capturePurchaseEvent('success', '监听到购买状态变化', {
|
||||||
@@ -368,7 +386,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
hasActiveSubscriptions,
|
hasActiveSubscriptions,
|
||||||
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
||||||
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
||||||
activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length,
|
activeSubscriptionsCount: activeSubscriptionIds.length,
|
||||||
modalVisible: visible
|
modalVisible: visible
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -424,10 +442,12 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
log.info('获取用户购买信息', { customerInfo });
|
log.info('获取用户购买信息', { customerInfo });
|
||||||
|
|
||||||
// 记录详细的购买状态日志
|
// 记录详细的购买状态日志
|
||||||
|
const activeSubscriptionIds = getActiveSubscriptionIds(customerInfo);
|
||||||
|
|
||||||
captureMessageWithContext('获取用户购买信息成功', {
|
captureMessageWithContext('获取用户购买信息成功', {
|
||||||
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
||||||
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
||||||
activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length,
|
activeSubscriptionsCount: activeSubscriptionIds.length,
|
||||||
originalAppUserId: customerInfo.originalAppUserId,
|
originalAppUserId: customerInfo.originalAppUserId,
|
||||||
firstSeen: customerInfo.firstSeen,
|
firstSeen: customerInfo.firstSeen,
|
||||||
originalPurchaseDate: customerInfo.originalPurchaseDate,
|
originalPurchaseDate: customerInfo.originalPurchaseDate,
|
||||||
@@ -451,7 +471,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 检查订阅
|
// 检查订阅
|
||||||
Object.keys(customerInfo.activeSubscriptions).forEach(productId => {
|
activeSubscriptionIds.forEach(productId => {
|
||||||
activePurchasedProductIds.push(productId);
|
activePurchasedProductIds.push(productId);
|
||||||
log.debug(`激活的订阅: ${productId}`);
|
log.debug(`激活的订阅: ${productId}`);
|
||||||
});
|
});
|
||||||
@@ -517,7 +537,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
captureMessageWithContext('用户没有激活的购买记录', {
|
captureMessageWithContext('用户没有激活的购买记录', {
|
||||||
hasEntitlements: Object.keys(customerInfo.entitlements.active).length > 0,
|
hasEntitlements: Object.keys(customerInfo.entitlements.active).length > 0,
|
||||||
hasNonSubscriptions: customerInfo.nonSubscriptionTransactions.length > 0,
|
hasNonSubscriptions: customerInfo.nonSubscriptionTransactions.length > 0,
|
||||||
hasActiveSubscriptions: Object.keys(customerInfo.activeSubscriptions).length > 0
|
hasActiveSubscriptions: activeSubscriptionIds.length > 0
|
||||||
});
|
});
|
||||||
log.info('用户没有激活的购买记录,使用默认选择逻辑');
|
log.info('用户没有激活的购买记录,使用默认选择逻辑');
|
||||||
setSelectedProduct(availableProducts[0] ?? null);
|
setSelectedProduct(availableProducts[0] ?? null);
|
||||||
@@ -611,13 +631,14 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
|
|
||||||
log.info('购买成功', { customerInfo, productIdentifier });
|
log.info('购买成功', { customerInfo, productIdentifier });
|
||||||
|
|
||||||
|
const activeSubscriptionIds = getActiveSubscriptionIds(customerInfo);
|
||||||
// 记录购买成功事件
|
// 记录购买成功事件
|
||||||
capturePurchaseEvent('success', `购买成功: ${productIdentifier}`, {
|
capturePurchaseEvent('success', `购买成功: ${productIdentifier}`, {
|
||||||
productIdentifier,
|
productIdentifier,
|
||||||
hasActiveEntitlements: Object.keys(customerInfo.entitlements.active).length > 0,
|
hasActiveEntitlements: Object.keys(customerInfo.entitlements.active).length > 0,
|
||||||
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
||||||
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
||||||
activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length
|
activeSubscriptionsCount: activeSubscriptionIds.length
|
||||||
});
|
});
|
||||||
|
|
||||||
// 购买成功后,监听器会自动处理后续逻辑(刷新用户信息、关闭弹窗等)
|
// 购买成功后,监听器会自动处理后续逻辑(刷新用户信息、关闭弹窗等)
|
||||||
@@ -687,10 +708,11 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
const customerInfo = await Purchases.restorePurchases();
|
const customerInfo = await Purchases.restorePurchases();
|
||||||
|
|
||||||
log.info('恢复购买结果', { customerInfo });
|
log.info('恢复购买结果', { customerInfo });
|
||||||
|
const activeSubscriptionIds = getActiveSubscriptionIds(customerInfo);
|
||||||
captureMessageWithContext('恢复购买结果', {
|
captureMessageWithContext('恢复购买结果', {
|
||||||
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
||||||
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
||||||
activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length,
|
activeSubscriptionsCount: activeSubscriptionIds.length,
|
||||||
managementUrl: customerInfo.managementURL,
|
managementUrl: customerInfo.managementURL,
|
||||||
originalAppUserId: customerInfo.originalAppUserId
|
originalAppUserId: customerInfo.originalAppUserId
|
||||||
});
|
});
|
||||||
@@ -698,7 +720,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
// 检查是否有有效的购买记录
|
// 检查是否有有效的购买记录
|
||||||
const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0;
|
const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0;
|
||||||
const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0;
|
const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0;
|
||||||
const hasActiveSubscriptions = Object.keys(customerInfo.activeSubscriptions).length > 0;
|
const hasActiveSubscriptions = activeSubscriptionIds.length > 0;
|
||||||
|
|
||||||
if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) {
|
if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) {
|
||||||
// 检查具体的购买内容
|
// 检查具体的购买内容
|
||||||
@@ -716,7 +738,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 检查订阅
|
// 检查订阅
|
||||||
Object.keys(customerInfo.activeSubscriptions).forEach(productId => {
|
activeSubscriptionIds.forEach(productId => {
|
||||||
restoredProducts.push(productId);
|
restoredProducts.push(productId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -787,7 +809,7 @@ export function MembershipModal({ visible, onClose, onPurchaseSuccess }: Members
|
|||||||
hasActiveSubscriptions,
|
hasActiveSubscriptions,
|
||||||
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length,
|
||||||
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length,
|
||||||
activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length
|
activeSubscriptionsCount: activeSubscriptionIds.length
|
||||||
});
|
});
|
||||||
GlobalToast.show({
|
GlobalToast.show({
|
||||||
message: '没有找到购买记录',
|
message: '没有找到购买记录',
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useS
|
|||||||
import Purchases from 'react-native-purchases';
|
import Purchases from 'react-native-purchases';
|
||||||
|
|
||||||
import { MembershipModal } from '@/components/model/MembershipModal';
|
import { MembershipModal } from '@/components/model/MembershipModal';
|
||||||
|
import { useAppSelector } from '@/hooks/redux';
|
||||||
|
import { selectUserProfile } from '@/store/userSlice';
|
||||||
import { logger } from '@/utils/logger';
|
import { logger } from '@/utils/logger';
|
||||||
|
|
||||||
type MembershipModalOptions = {
|
type MembershipModalOptions = {
|
||||||
@@ -20,6 +22,9 @@ export function MembershipModalProvider({ children }: { children: React.ReactNod
|
|||||||
const [pendingSuccessCallback, setPendingSuccessCallback] = useState<(() => void) | undefined>();
|
const [pendingSuccessCallback, setPendingSuccessCallback] = useState<(() => void) | undefined>();
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
|
||||||
|
// 获取用户信息,用于RevenueCat用户标识
|
||||||
|
const userProfile = useAppSelector(selectUserProfile);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 直接使用生产环境的 API Key,避免环境变量问题
|
// 直接使用生产环境的 API Key,避免环境变量问题
|
||||||
const iosApiKey = 'appl_lmVvuLWFlXlrEsnvxMzTnKapqcc';
|
const iosApiKey = 'appl_lmVvuLWFlXlrEsnvxMzTnKapqcc';
|
||||||
@@ -28,18 +33,51 @@ export function MembershipModalProvider({ children }: { children: React.ReactNod
|
|||||||
try {
|
try {
|
||||||
// 检查是否已经配置过,避免重复配置
|
// 检查是否已经配置过,避免重复配置
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
await Purchases.configure({ apiKey: iosApiKey });
|
// 如果有用户ID,在配置时传入用户标识
|
||||||
|
const configOptions: any = { apiKey: iosApiKey };
|
||||||
|
|
||||||
|
if (userProfile?.id) {
|
||||||
|
configOptions.appUserID = userProfile.id;
|
||||||
|
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化,使用用户ID:', userProfile.id);
|
||||||
|
} else {
|
||||||
|
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化,未设置用户ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
await Purchases.configure(configOptions);
|
||||||
setIsInitialized(true);
|
setIsInitialized(true);
|
||||||
console.log('[MembershipModalProvider] RevenueCat SDK 初始化成功');
|
logger.info('[MembershipModalProvider] RevenueCat SDK 初始化成功');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[MembershipModalProvider] RevenueCat SDK 初始化失败:', error);
|
logger.error('[MembershipModalProvider] RevenueCat SDK 初始化失败:', error);
|
||||||
// 初始化失败时不阻止应用正常运行
|
// 初始化失败时不阻止应用正常运行
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
initializeRevenueCat();
|
initializeRevenueCat();
|
||||||
}, [isInitialized]);
|
}, [isInitialized, userProfile?.id]);
|
||||||
|
|
||||||
|
// 监听用户登录状态变化,在用户登录后更新RevenueCat的用户标识
|
||||||
|
useEffect(() => {
|
||||||
|
const updateRevenueCatUser = async () => {
|
||||||
|
if (isInitialized && userProfile?.id) {
|
||||||
|
try {
|
||||||
|
// 检查当前RevenueCat的用户标识
|
||||||
|
const customerInfo = await Purchases.getCustomerInfo();
|
||||||
|
const currentAppUserID = customerInfo.originalAppUserId;
|
||||||
|
|
||||||
|
// 如果当前用户ID与RevenueCat中的用户ID不同,则更新
|
||||||
|
if (currentAppUserID !== userProfile.id) {
|
||||||
|
console.log('[MembershipModalProvider] 更新RevenueCat用户标识:', userProfile.id);
|
||||||
|
await Purchases.logIn(userProfile.id);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[MembershipModalProvider] 更新RevenueCat用户标识失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateRevenueCatUser();
|
||||||
|
}, [userProfile?.id, isInitialized]);
|
||||||
|
|
||||||
const openMembershipModal = useCallback((options?: MembershipModalOptions) => {
|
const openMembershipModal = useCallback((options?: MembershipModalOptions) => {
|
||||||
setPendingSuccessCallback(() => options?.onPurchaseSuccess);
|
setPendingSuccessCallback(() => options?.onPurchaseSuccess);
|
||||||
|
|||||||
91
hooks/useVipService.ts
Normal file
91
hooks/useVipService.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { useAppSelector } from '@/hooks/redux';
|
||||||
|
import { selectUserProfile } from '@/store/userSlice';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增值服务检查结果
|
||||||
|
*/
|
||||||
|
export interface VipServiceResult {
|
||||||
|
/** 是否可以使用服务 */
|
||||||
|
canUseService: boolean;
|
||||||
|
/** 是否是 VIP 用户 */
|
||||||
|
isVip: boolean;
|
||||||
|
/** 剩余免费使用次数 */
|
||||||
|
remainingFreeUsage: number;
|
||||||
|
/** 最大免费使用次数 */
|
||||||
|
maxFreeUsage: number;
|
||||||
|
/** 是否需要显示会员付费弹窗 */
|
||||||
|
shouldShowMembershipModal: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VIP 增值服务 Hook
|
||||||
|
* 用于检查用户是否可以使用增值服务
|
||||||
|
*/
|
||||||
|
export function useVipService() {
|
||||||
|
const userProfile = useAppSelector(selectUserProfile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户是否可以使用增值服务
|
||||||
|
* @returns 检查结果
|
||||||
|
*/
|
||||||
|
const checkServiceAccess = useCallback((): VipServiceResult => {
|
||||||
|
const isVip = userProfile?.isVip ?? false;
|
||||||
|
const freeUsageCount = userProfile?.freeUsageCount ?? 0;
|
||||||
|
const maxUsageCount = userProfile?.maxUsageCount ?? 5;
|
||||||
|
|
||||||
|
console.log('userProfile', userProfile);
|
||||||
|
|
||||||
|
|
||||||
|
// VIP 用户可以使用所有服务
|
||||||
|
if (isVip) {
|
||||||
|
return {
|
||||||
|
canUseService: true,
|
||||||
|
isVip: true,
|
||||||
|
remainingFreeUsage: 0, // VIP 用户不关心免费次数
|
||||||
|
maxFreeUsage: 0,
|
||||||
|
shouldShowMembershipModal: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算剩余免费使用次数
|
||||||
|
const canUseService = freeUsageCount > 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
canUseService,
|
||||||
|
isVip: false,
|
||||||
|
remainingFreeUsage: freeUsageCount,
|
||||||
|
maxFreeUsage: maxUsageCount,
|
||||||
|
shouldShowMembershipModal: !canUseService,
|
||||||
|
};
|
||||||
|
}, [userProfile]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并处理服务访问
|
||||||
|
* @param onAllowed 当允许使用时的回调函数
|
||||||
|
* @param onBlocked 当阻止使用时的回调函数(可选)
|
||||||
|
* @returns 是否可以继续使用服务
|
||||||
|
*/
|
||||||
|
const handleServiceAccess = useCallback((
|
||||||
|
onAllowed: () => void,
|
||||||
|
onBlocked?: () => void
|
||||||
|
): boolean => {
|
||||||
|
const result = checkServiceAccess();
|
||||||
|
|
||||||
|
if (result.canUseService) {
|
||||||
|
onAllowed();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
onBlocked?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [checkServiceAccess]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkServiceAccess,
|
||||||
|
handleServiceAccess,
|
||||||
|
isVip: userProfile?.isVip ?? false,
|
||||||
|
freeUsageCount: userProfile?.freeUsageCount ?? 0,
|
||||||
|
maxUsageCount: userProfile?.maxUsageCount ?? 5,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user