docs(genex-mobile): 完善邀请分享模块注释与说明

SharePage:
  - 文件头注释:完整功能概述、支持的分享场景、数据来源、URL格式、依赖包说明
  - 类注释:生命周期描述、Widget 树结构图(ASCII)
  - 状态变量:详细说明 _info/_loading/_error/_baseInviteUrl
  - _inviteLink:注释已加载/未加载两种输出示例
  - _loadInfo:成功/失败两条路径说明
  - _showCopied:SnackBar 样式描述
  - _copyCode/_copyLink:示例复制内容
  - _shareNative:iOS/Android 行为说明 + 文案模板示例
  - _buildHeroCard:视觉层次注释 + QR 参数说明
  - _buildStatsCard:布局描述 + 数据来源注释
  - _buildShareActions:三项操作的点击行为说明

ReferralService:
  - 文件头:完整端点一览、推荐码格式、推荐链规则
  - ReferralInfo:字段含义 + 后端响应 JSON 示例
  - getMyInfo:登录要求说明
  - validateCode:用途说明 + 返回值降级策略
  - getDirectReferrals:分页参数范围 + 响应 JSON 示例

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-03-04 01:42:47 -08:00
parent 46d2404d19
commit 1c36c849e2
2 changed files with 261 additions and 32 deletions

View File

@ -1,14 +1,54 @@
import '../network/api_client.dart';
///
// ============================================================
// ReferralService /
//
// referral-service 3013 Kong :8080
//
//
// GET /api/v1/referral/my JWT
// GET /api/v1/referral/validate
// GET /api/v1/referral/direct JWT
//
// GNX + 5 0/O/I/1GNXAB2C3
// 50
// ============================================================
///
///
/// [GET /api/v1/referral/my] `data`
///
///
/// ```json
/// {
/// "code": 0,
/// "data": {
/// "userId": "uuid-...",
/// "referralCode": "GNXAB2C3",
/// "referrerId": "uuid-... | null",
/// "usedCode": "GNXPARENT | null",
/// "directReferralCount": 5,
/// "totalTeamCount": 23
/// }
/// }
/// ```
class ReferralInfo {
/// GNXxxxxx 8
final String referralCode;
///
final int directReferralCount;
///
final int totalTeamCount;
/// userId null
final String? referrerId;
/// referrerId referralCode便
final String? usedCode;
ReferralInfo({
const ReferralInfo({
required this.referralCode,
required this.directReferralCount,
required this.totalTeamCount,
@ -25,9 +65,22 @@ class ReferralInfo {
usedCode: json['usedCode'] as String?,
);
}
@override
String toString() =>
'ReferralInfo(code=$referralCode, direct=$directReferralCount, team=$totalTeamCount)';
}
/// Referral Service referral-service API
///
///
///
/// ```dart
/// final info = await ReferralService.instance.getMyInfo();
/// print(info.referralCode); // e.g. GNXAB2C3
/// ```
///
/// ApiClient JWT
/// token DioException401
class ReferralService {
static final ReferralService _instance = ReferralService._();
static ReferralService get instance => _instance;
@ -35,14 +88,56 @@ class ReferralService {
final _api = ApiClient.instance;
///
//
///
///
///
///
/// - Bearer Token
/// - Kafka
///
/// Throws [DioException] on network error or 401/403.
Future<ReferralInfo> getMyInfo() async {
final resp = await _api.get('/api/v1/referral/my');
final data = resp.data['data'] as Map<String, dynamic>;
return ReferralInfo.fromJson(data);
}
///
///
///
///
///
/// `{ "code": 0, "data": { "valid": true/false } }`
///
/// Returns `false` on any error (avoid blocking registration flow).
Future<bool> validateCode(String code) async {
try {
final resp = await _api.get(
'/api/v1/referral/validate',
queryParameters: {'code': code.toUpperCase()},
);
final data = resp.data['data'] as Map<String, dynamic>;
return data['valid'] == true;
} catch (_) {
return false;
}
}
///
///
/// userId
///
/// - [offset] 0
/// - [limit] 50
///
/// `data`
/// ```json
/// {
/// "items": [ { "userId": "...", "referralCode": "...", "createdAt": "..." } ],
/// "total": 5
/// }
/// ```
Future<List<Map<String, dynamic>>> getDirectReferrals({
int offset = 0,
int limit = 20,

View File

@ -1,3 +1,35 @@
// ============================================================
// SharePage / 广
//
// /share ProfilePage
//
//
// 1. Hero Card
// · qr_flutter URL
// ·
// 2. +
// 3. / /
// 4.
//
// App
// / QQ / WhatsApp / Telegram / Line / Twitter/X
// / / AirDrop / /
//
//
// GET /api/v1/referral/my ReferralService.getMyInfo()
// Kafka
// Loading
//
//
// https://app.gogenex.com/download?ref={referralCode}
// App Store / Google Play /
// ref
//
//
// qr_flutter ^4.1.0
// share_plus ^10.0.2 iOS Share Sheet / Android Sharesheet
// ============================================================
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
@ -11,11 +43,18 @@ import '../../../../app/i18n/app_localizations.dart';
/// A9. / 广
///
///
/// - App
/// - /
/// - WhatsAppTelegram
/// - +
///
/// initState _loadInfo() [Loading] [Content | Error]
///
/// Widget
/// ```
/// Scaffold
/// SingleChildScrollView
/// _buildHeroCard() QR +
/// _buildStatsCard() |
/// _buildShareActions() / /
/// GenexButton
/// ```
class SharePage extends StatefulWidget {
const SharePage({super.key});
@ -24,23 +63,52 @@ class SharePage extends StatefulWidget {
}
class _SharePageState extends State<SharePage> {
//
/// referral-service null
ReferralInfo? _info;
///
bool _loading = true;
///
String? _error;
/// App
//
/// App
///
///
/// 1. UAiOS / Android /
/// 2. iOS App Store
/// 3. Android Google Play APK
/// 4. `ref` localStorage
static const String _baseInviteUrl = 'https://app.gogenex.com/download';
//
/// + 使
///
/// https://app.gogenex.com/download?ref=GNXAB2C3
/// https://app.gogenex.com/download
String get _inviteLink => _info != null
? '$_baseInviteUrl?ref=${_info!.referralCode}'
: _baseInviteUrl;
//
@override
void initState() {
super.initState();
_loadInfo();
}
//
/// referral-service
///
/// [_info] loading
/// [_error]
Future<void> _loadInfo() async {
setState(() {
_loading = true;
@ -54,6 +122,9 @@ class _SharePageState extends State<SharePage> {
}
}
//
/// SnackBar +
void _showCopied(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@ -74,17 +145,33 @@ class _SharePageState extends State<SharePage> {
);
}
///
///
/// GNXAB2C3
Future<void> _copyCode() async {
if (_info == null) return;
await Clipboard.setData(ClipboardData(text: _info!.referralCode));
if (mounted) _showCopied(context.t('share.codeCopied'));
}
///
///
/// https://app.gogenex.com/download?ref=GNXAB2C3
Future<void> _copyLink() async {
await Clipboard.setData(ClipboardData(text: _inviteLink));
if (mounted) _showCopied(context.t('share.linkCopied'));
}
///
///
/// +
///
/// iOS Share SheetAirDrop
/// Android SharesheetWhatsAppTelegram
///
/// zh-CN
/// Genex 使 {code}
/// https://app.gogenex.com/download?ref={code}
Future<void> _shareNative() async {
final code = _info?.referralCode ?? '';
final text = context
@ -94,6 +181,8 @@ class _SharePageState extends State<SharePage> {
await Share.share(text, subject: context.t('share.nativeShareTitle'));
}
// Build
@override
Widget build(BuildContext context) {
return Scaffold(
@ -116,6 +205,8 @@ class _SharePageState extends State<SharePage> {
);
}
//
Widget _buildLoading() {
return Center(
child: Column(
@ -132,6 +223,9 @@ class _SharePageState extends State<SharePage> {
);
}
//
/// + +
Widget _buildError() {
return Center(
child: Padding(
@ -159,19 +253,21 @@ class _SharePageState extends State<SharePage> {
);
}
//
Widget _buildContent() {
return SingleChildScrollView(
padding: AppSpacing.pagePadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildHeroCard(),
_buildHeroCard(), // QR +
const SizedBox(height: 16),
_buildStatsCard(),
_buildStatsCard(), //
const SizedBox(height: 16),
_buildShareActions(),
_buildShareActions(), //
const SizedBox(height: 24),
GenexButton(
GenexButton( //
label: context.t('share.shareToFriend'),
onPressed: _shareNative,
),
@ -181,7 +277,18 @@ class _SharePageState extends State<SharePage> {
);
}
// Hero Card: QR +
// Hero Card +
//
//
// Container[ + 20 + ]
// Column
// + "扫码下载 Genex"
// "Genex" 3FontWeight.w800
// Container[] QrImageView180×180
// +
//
// QR _inviteLink https://app.gogenex.com/download?ref=GNXAB2C3
// QR eye= #4834D4module= #6C5CE7=
Widget _buildHeroCard() {
return Container(
@ -200,7 +307,7 @@ class _SharePageState extends State<SharePage> {
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28),
child: Column(
children: [
//
//
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -223,7 +330,8 @@ class _SharePageState extends State<SharePage> {
),
const SizedBox(height: 24),
// QR
//
// QrImageView
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
@ -238,14 +346,16 @@ class _SharePageState extends State<SharePage> {
],
),
child: QrImageView(
data: _inviteLink,
version: QrVersions.auto,
data: _inviteLink, // URL
version: QrVersions.auto, //
size: 180,
backgroundColor: Colors.white,
//
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.square,
color: Color(0xFF4834D4),
),
//
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.square,
color: Color(0xFF6C5CE7),
@ -254,7 +364,8 @@ class _SharePageState extends State<SharePage> {
),
const SizedBox(height: 24),
//
//
//
Text(
context.t('share.myReferralCode'),
style: AppTypography.caption.copyWith(color: Colors.white60),
@ -265,13 +376,14 @@ class _SharePageState extends State<SharePage> {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
color: Colors.white.withOpacity(0.15), //
borderRadius: BorderRadius.circular(40),
border: Border.all(color: Colors.white30),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// + 4
Text(
_info?.referralCode ?? '------',
style: AppTypography.h2.copyWith(
@ -282,6 +394,7 @@ class _SharePageState extends State<SharePage> {
),
),
const SizedBox(width: 12),
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
@ -313,7 +426,14 @@ class _SharePageState extends State<SharePage> {
);
}
//
//
//
// 线
// Icons.people
// Icons.groups
//
// ReferralInfo.directReferralCount / totalTeamCount
// 0
Widget _buildStatsCard() {
return Container(
@ -333,6 +453,7 @@ class _SharePageState extends State<SharePage> {
label: context.t('share.directReferrals'),
),
),
// 线
Container(width: 1, height: 44, color: AppColors.borderLight),
Expanded(
child: _buildStatItem(
@ -346,6 +467,7 @@ class _SharePageState extends State<SharePage> {
);
}
/// + +
Widget _buildStatItem({
required IconData icon,
required String value,
@ -358,22 +480,27 @@ class _SharePageState extends State<SharePage> {
children: [
Icon(icon, size: 16, color: AppColors.primary),
const SizedBox(width: 4),
Text(
value,
style: AppTypography.h2.copyWith(color: AppColors.primary),
),
Text(value, style: AppTypography.h2.copyWith(color: AppColors.primary)),
],
),
const SizedBox(height: 2),
Text(
label,
style: AppTypography.caption.copyWith(color: AppColors.textSecondary),
),
Text(label, style: AppTypography.caption.copyWith(color: AppColors.textSecondary)),
],
);
}
//
//
//
// ListTile Divider
//
// [] GNXAB2C3 >
// [] https://... >
// [绿] /WhatsApp >
//
//
// _copyCode() + SnackBar
// _copyLink() + SnackBar
// _shareNative()
Widget _buildShareActions() {
return Column(
@ -395,6 +522,7 @@ class _SharePageState extends State<SharePage> {
),
child: Column(
children: [
//
_buildActionTile(
icon: Icons.qr_code_rounded,
iconBg: AppColors.primaryContainer,
@ -404,6 +532,7 @@ class _SharePageState extends State<SharePage> {
onTap: _copyCode,
),
const Divider(indent: 60, height: 1),
//
_buildActionTile(
icon: Icons.link_rounded,
iconBg: AppColors.infoLight,
@ -413,6 +542,7 @@ class _SharePageState extends State<SharePage> {
onTap: _copyLink,
),
const Divider(indent: 60, height: 1),
//
_buildActionTile(
icon: Icons.share_rounded,
iconBg: AppColors.successLight,
@ -428,6 +558,10 @@ class _SharePageState extends State<SharePage> {
);
}
///
///
/// - [iconBg] 使
/// - [subtitle] /
Widget _buildActionTile({
required IconData icon,
required Color iconBg,