import '../core/network/api_client.dart'; import '../core/network/endpoints.dart'; /// 推广归因服务(三段式自建方案): /// 1. 分享口令(剪贴板解析,准确率 100%)——主路径 /// 2. H5 中间页点击埋点 + 首启模糊匹配——辅助路径(本类 reportMatch) /// 3. 注册页邀请码手填——兜底 class InviteService { InviteService._(); /// 口令格式:#短视频邀请码XXXXXXXX#(邀请码 8 位大写字母数字,与服务端生成规则一致) static final RegExp _commandRe = RegExp(r'#短视频邀请码([0-9A-Z]{8})#'); /// 从剪贴板文本解析邀请口令,未命中返回 null static String? parseInviteCommand(String? text) { if (text == null || text.isEmpty) return null; return _commandRe.firstMatch(text)?.group(1); } /// 生成推广文案(邀请页"复制推广文案",口令包裹其中供被邀请人首启解析) static String buildShareText({required String inviteCode, required String inviteUrl}) { return '【短视频】精彩短视频刷不停,新人注册立送体验会员!\n' '① 点击链接下载:$inviteUrl\n' '② 或复制本段文字,打开 App 自动绑定我的邀请\n' '#短视频邀请码$inviteCode#'; } /// 首启模糊归因上报(无口令时的辅助路径); /// 命中结果由服务端按 device_id 缓存,注册时自动带出;失败不阻塞启动 static Future reportMatch(String deviceId) async { try { final data = await ApiClient.post>( Endpoints.inviteMatch, data: {'device_id': deviceId}, ); if (data['matched'] == true) return data['invite_code'] as String?; } catch (_) { // 归因失败不影响正常启动 } return null; } /// 我的邀请(邀请码/推广链接/统计,对应《03-接口文档》9.1) static Future fetchMyReferral() async { final data = await ApiClient.get>(Endpoints.referralMe); return ReferralInfo.fromJson(data); } } /// 我的邀请信息 class ReferralInfo { ReferralInfo({ required this.inviteCode, required this.inviteUrl, required this.eligible, required this.totalInvitees, required this.activatedCount, required this.rewardPointsTotal, required this.rebateAssetsTotal, }); final String inviteCode; final String inviteUrl; /// 是否具备奖励资格(会员) final bool eligible; final int totalInvitees; final int activatedCount; final int rewardPointsTotal; final double rebateAssetsTotal; factory ReferralInfo.fromJson(Map json) => ReferralInfo( inviteCode: (json['invite_code'] as String?) ?? '', inviteUrl: (json['invite_url'] as String?) ?? '', eligible: (json['eligible'] as bool?) ?? false, totalInvitees: (json['total_invitees'] as num?)?.toInt() ?? 0, activatedCount: (json['activated_count'] as num?)?.toInt() ?? 0, rewardPointsTotal: (json['reward_points_total'] as num?)?.toInt() ?? 0, rebateAssetsTotal: (json['rebate_assets_total'] as num?)?.toDouble() ?? 0, ); }