import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/auth/auth_provider.dart'; /// CPS 联盟商品(后端 /cps/* 模型) class CpsProductInfo { final int id; final String source; final String outerId; final String categoryCode; final String name; final String coverUrl; final int priceFen; final String shopName; final int commissionRate; // 万分比 final String city; const CpsProductInfo({ required this.id, required this.source, required this.outerId, required this.categoryCode, required this.name, required this.coverUrl, required this.priceFen, required this.shopName, required this.commissionRate, required this.city, }); String get priceText => (priceFen / 100).toStringAsFixed(0); String get commissionText { final pct = commissionRate / 100; return pct > 0 ? '佣金 ${pct.toStringAsFixed(1)}%' : ''; } } CpsProductInfo _parseProduct(Map e) => CpsProductInfo( id: (e['id'] as num).toInt(), source: e['source'] as String? ?? '', outerId: e['outer_id'] as String? ?? '', categoryCode: e['category_code'] as String? ?? '', name: e['name'] as String? ?? '', coverUrl: e['cover_url'] as String? ?? '', priceFen: (e['price_fen'] as num?)?.toInt() ?? 0, shopName: e['shop_name'] as String? ?? '', commissionRate: (e['commission_rate'] as num?)?.toInt() ?? 0, city: e['city'] as String? ?? '', ); /// 联盟分类(chips) class CpsCategoryInfo { final String code; final String name; final String source; const CpsCategoryInfo({ required this.code, required this.name, required this.source, }); } final cpsCategoryProvider = FutureProvider>((ref) async { final api = ref.read(apiClientProvider); final data = await api.get>('/cps/category/list'); final list = data['list'] as List? ?? []; return list .map((e) => CpsCategoryInfo( code: e['code'] as String? ?? '', name: e['name'] as String? ?? '', source: e['source'] as String? ?? '', )) .toList(); }); /// 商品列表页参数 class CpsListArgs { final String title; final String source; final String categoryCode; final String city; final String scene; // 点击日志场景标记(haircut/item_buy/...) const CpsListArgs({ required this.title, this.source = '', this.categoryCode = '', this.city = '', this.scene = '', }); @override bool operator ==(Object other) => other is CpsListArgs && other.source == source && other.categoryCode == categoryCode && other.city == city && other.scene == scene; @override int get hashCode => Object.hash(source, categoryCode, city, scene); } /// 选品池分页(上滑加载更多;Riverpod 3 family 参数经构造函数注入) class CpsProductListNotifier extends AsyncNotifier> { CpsProductListNotifier(this.arg); final CpsListArgs arg; int _page = 1; bool _hasMore = true; @override Future> build() async { _page = 1; _hasMore = true; return _fetch(1); } Future loadMore() async { if (!_hasMore || state.isLoading) return; _page += 1; try { final next = await _fetch(_page); state = AsyncData([...?state.value, ...next]); } catch (_) { _page -= 1; // 失败回退页码,允许下次重试 } } Future> _fetch(int page) async { final api = ref.read(apiClientProvider); final data = await api.get>('/cps/product/list', query: { if (arg.source.isNotEmpty) 'source': arg.source, if (arg.categoryCode.isNotEmpty) 'category_code': arg.categoryCode, if (arg.city.isNotEmpty) 'city': arg.city, 'page': page, }); _hasMore = data['has_more'] as bool? ?? false; return (data['list'] as List? ?? []) .map((e) => _parseProduct(e as Map)) .toList(); } } final cpsProductProvider = AsyncNotifierProvider.family< CpsProductListNotifier, List, CpsListArgs>( CpsProductListNotifier.new); /// 方案驱动推荐(plan_id + scene) final cpsRecommendProvider = FutureProvider.family, ({int planId, String scene})>((ref, args) async { final api = ref.read(apiClientProvider); final data = await api.get>('/cps/plan/recommend', query: {'plan_id': args.planId, 'scene': args.scene}); return (data['list'] as List? ?? []) .map((e) => _parseProduct(e as Map)) .toList(); }); /// 衣橱升级款 final cpsUpgradeProvider = FutureProvider.family, int>((ref, itemId) async { final api = ref.read(apiClientProvider); final data = await api.get>('/cps/wardrobe/upgrade', query: {'item_id': itemId}); return (data['list'] as List? ?? []) .map((e) => _parseProduct(e as Map)) .toList(); }); /// 转链动作(POST /cps/product/link,返回 deeplink) class CpsLinkAction { Future link( WidgetRef ref, { required int productId, String scene = '', int planId = 0, }) async { final api = ref.read(apiClientProvider); final data = await api.post>('/cps/product/link', { 'product_id': productId, 'scene': scene, 'plan_id': planId, }); return data['deeplink'] as String? ?? ''; } } final cpsLinkProvider = Provider((ref) => CpsLinkAction()); /// 最近优惠(会员中心) final cpsRecentProvider = FutureProvider>((ref) async { final api = ref.read(apiClientProvider); final data = await api.get>('/cps/my/recent'); return (data['list'] as List? ?? []) .map((e) => _parseProduct(e as Map)) .toList(); });