import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/auth/auth_provider.dart'; /// 会员 provider:会员状态/套餐/下单/轮询/领奖 class MemberInfo { final bool isVip; final String expireAt; final String planName; final List benefits; const MemberInfo({ required this.isVip, required this.expireAt, required this.planName, required this.benefits, }); factory MemberInfo.fromJson(Map e) => MemberInfo( isVip: e['is_vip'] as bool? ?? false, expireAt: e['expire_at'] as String? ?? '', planName: e['plan_name'] as String? ?? '', benefits: (e['benefits'] as List? ?? []).cast(), ); } /// 权益 key → 文案 const benefitLabels = { 'effect_unlimited': '无限效果图', 'ai_priority': '优先 AI 方案', 'cps_commission_x15': '返现加成 1.5x', 'store_discount': '门店折扣', }; List benefitTexts(MemberInfo m) => m.benefits.map((b) => benefitLabels[b] ?? b).toList(); class MemberNotifier extends AsyncNotifier { @override Future build() async { final api = ref.read(apiClientProvider); final data = await api.get>('/member/status'); return MemberInfo.fromJson(data); } Future refresh() async { state = await AsyncValue.guard(build); } /// 领取广告激励(服务端限频);adType: effect_extra | vip_trial /// 返回当日剩余次数;超出限频抛 ApiException Future claimReward(String adType) async { final api = ref.read(apiClientProvider); final data = await api.post>('/ad/reward/claim', {'ad_type': adType}); await refresh(); // vip_trial 可能开通体验会员 return (data['reward']?['remaining_today'] as num?)?.toInt() ?? 0; } } final memberProvider = AsyncNotifierProvider(MemberNotifier.new); class MemberPlan { final int id; final String name; final int priceFen; final int durationDays; final List features; const MemberPlan({ required this.id, required this.name, required this.priceFen, required this.durationDays, required this.features, }); factory MemberPlan.fromJson(Map e) => MemberPlan( id: (e['id'] as num).toInt(), name: e['name'] as String? ?? '', priceFen: (e['price_fen'] as num?)?.toInt() ?? 0, durationDays: (e['duration_days'] as num?)?.toInt() ?? 30, features: _parseFeatures(e['features'] as String? ?? ''), ); static List _parseFeatures(String s) { try { return (jsonDecode(s) as List).cast(); } catch (_) { return const []; } } String get priceText => '¥${(priceFen / 100).toStringAsFixed(priceFen % 100 == 0 ? 0 : 1)}'; } final memberPlanProvider = FutureProvider>((ref) async { final api = ref.read(apiClientProvider); final data = await api.get>('/member/plan/list'); final list = data['list'] as List? ?? []; return list .map((e) => MemberPlan.fromJson(e as Map)) .toList(); }); class OrderResult { final String orderNo; final String payUrl; const OrderResult({required this.orderNo, required this.payUrl}); } /// 创建支付订单(后端调虎皮棋下单,返回收银台 URL) Future createMemberOrder(WidgetRef ref, int planId) async { final api = ref.read(apiClientProvider); final data = await api.post>('/member/order/create', {'plan_id': planId}); return OrderResult( orderNo: data['order_no'] as String? ?? '', payUrl: data['pay_url'] as String? ?? '', ); } /// 订单状态(支付页 2s 轮询):pending | paid | closed Future fetchOrderStatus(WidgetRef ref, String orderNo) async { final api = ref.read(apiClientProvider); final data = await api.get>('/member/order/status', query: {'order_no': orderNo}); return data['status'] as String? ?? ''; }