82 lines
3.1 KiB
Dart
82 lines
3.1 KiB
Dart
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<String?> reportMatch(String deviceId) async {
|
||
try {
|
||
final data = await ApiClient.post<Map<String, dynamic>>(
|
||
Endpoints.inviteMatch,
|
||
data: {'device_id': deviceId},
|
||
);
|
||
if (data['matched'] == true) return data['invite_code'] as String?;
|
||
} catch (_) {
|
||
// 归因失败不影响正常启动
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// 我的邀请(邀请码/推广链接/统计,对应《03-接口文档》9.1)
|
||
static Future<ReferralInfo> fetchMyReferral() async {
|
||
final data = await ApiClient.get<Map<String, dynamic>>(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<String, dynamic> 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,
|
||
);
|
||
}
|