17 KiB
17 KiB
Genex Flutter 移动端开发指南
Consumer App(消费者端)+ Merchant App(商户核销端)
1. 技术栈概览
| 技术 | 版本 | 用途 |
|---|---|---|
| Flutter | 3.x | 跨平台UI框架 |
| Dart | 3.x | 开发语言 |
| Riverpod | 2.x | 状态管理(Provider替代方案,编译安全) |
| GoRouter | 最新 | 声明式路由、深链接 |
| Dio | 5.x | HTTP客户端 |
| Freezed | 最新 | 不可变数据模型代码生成 |
| Hive/Isar | 最新 | 本地持久化 |
| WebSocket | 内置 | AI Agent实时通信、行情推送 |
| flutter_localizations | 内置 | 国际化 |
2. 项目架构:Clean Architecture + Riverpod
2.1 目录结构
genex_mobile/
├── lib/
│ ├── main.dart # 应用入口
│ ├── app/
│ │ ├── app.dart # MaterialApp配置
│ │ ├── router.dart # GoRouter路由定义
│ │ └── theme/ # 主题配置
│ ├── core/
│ │ ├── constants/ # 常量(API地址、术语映射表)
│ │ ├── network/ # Dio配置、拦截器、错误处理
│ │ ├── storage/ # 本地存储封装
│ │ ├── utils/ # 工具类
│ │ └── extensions/ # Dart扩展方法
│ ├── features/ # 按功能模块组织
│ │ ├── auth/ # 注册/登录
│ │ │ ├── data/
│ │ │ │ ├── datasources/ # 远程/本地数据源
│ │ │ │ ├── models/ # DTO(Freezed生成)
│ │ │ │ └── repositories/ # Repository实现
│ │ │ ├── domain/
│ │ │ │ ├── entities/ # 领域实体
│ │ │ │ ├── repositories/ # Repository接口
│ │ │ │ └── usecases/ # 用例
│ │ │ └── presentation/
│ │ │ ├── providers/ # Riverpod Providers
│ │ │ ├── pages/ # 页面Widget
│ │ │ └── widgets/ # 模块专用组件
│ │ ├── coupons/ # 券浏览/购买/持有
│ │ ├── trading/ # 二级市场交易
│ │ ├── wallet/ # 余额/资产/交易记录
│ │ ├── redeem/ # 券使用/核销
│ │ ├── transfer/ # P2P转赠
│ │ ├── profile/ # 个人中心/KYC
│ │ ├── ai_agent/ # AI Agent对话/建议
│ │ └── merchant/ # 商户端核销(共享模块)
│ ├── shared/
│ │ ├── widgets/ # 全局共享组件
│ │ ├── providers/ # 全局Provider
│ │ └── models/ # 共享数据模型
│ └── l10n/ # 国际化资源
├── test/ # 单元/Widget测试
├── integration_test/ # 集成测试
└── pubspec.yaml
2.2 Clean Architecture 分层
┌────────────────────────────────────────┐
│ Presentation Layer │
│ Pages → Widgets → Riverpod Providers │
├────────────────────────────────────────┤
│ Domain Layer │
│ Entities → UseCases → Repository(接口)│
├────────────────────────────────────────┤
│ Data Layer │
│ Models(DTO) → DataSources → Repo实现 │
└────────────────────────────────────────┘
依赖方向:Presentation → Domain ← Data(Domain层不依赖任何外层)
3. 核心模块实现
3.1 术语映射(全局执行)
所有面向用户的UI文本必须使用Web2术语,禁止出现区块链术语。
// lib/core/constants/terminology.dart
class Terminology {
// 用户界面术语 → 底层技术术语
static const Map<String, String> mapping = {
'我的账户': '链上钱包地址',
'我的券': 'ERC-721/1155 NFT资产',
'我的余额': '链上稳定币(USDC)余额',
'转赠给朋友': 'P2P链上转移',
'购买': '链上原子交换',
'核销/使用': '合约兑付(Redemption)',
'订单号': '交易哈希(TX Hash)',
'安全验证': '链上签名(MPC钱包后台执行)',
};
}
3.2 认证模块
// lib/features/auth/domain/entities/user.dart
@freezed
class User with _$User {
const factory User({
required String id,
required String phone, // 或 email
required KycLevel kycLevel, // L0/L1/L2/L3
required WalletMode walletMode, // standard/pro
String? displayName,
String? avatar,
}) = _User;
}
enum KycLevel { L0, L1, L2, L3 }
enum WalletMode { standard, pro }
// lib/features/auth/domain/usecases/register_usecase.dart
class RegisterUseCase {
final AuthRepository _repo;
RegisterUseCase(this._repo);
/// 注册:手机号/邮箱 → 后台自动创建MPC钱包,用户无感知
Future<Either<Failure, User>> call(RegisterParams params) {
return _repo.register(
phone: params.phone,
email: params.email,
password: params.password,
);
}
}
3.3 券资产模块
// lib/features/coupons/domain/entities/coupon.dart
@freezed
class Coupon with _$Coupon {
const factory Coupon({
required String id, // 券ID(链上唯一)
required String issuerName, // 发行方名称
required double faceValue, // 面值
required double currentPrice, // 当前市场价
required DateTime expiryDate, // 到期日期
required CouponStatus status, // 可用/已使用/已过期
required CouponType type, // utility/security
String? imageUrl,
String? description,
List<String>? usageConditions,
}) = _Coupon;
}
enum CouponStatus { available, used, expired, listed }
enum CouponType { utility, security }
// lib/features/coupons/presentation/providers/coupon_providers.dart
final couponListProvider = FutureProvider.autoDispose<List<Coupon>>((ref) {
final repo = ref.watch(couponRepositoryProvider);
return repo.getMyCoupons();
});
final couponDetailProvider = FutureProvider.autoDispose.family<Coupon, String>(
(ref, couponId) {
final repo = ref.watch(couponRepositoryProvider);
return repo.getCouponDetail(couponId);
},
);
3.4 交易模块
// lib/features/trading/domain/entities/order.dart
@freezed
class TradeOrder with _$TradeOrder {
const factory TradeOrder({
required String orderId, // 订单号(映射TX Hash)
required String couponId,
required OrderSide side, // buy/sell
required double price,
required OrderStatus status,
required DateTime createdAt,
}) = _TradeOrder;
}
enum OrderSide { buy, sell }
enum OrderStatus { pending, matched, settled, cancelled }
Utility Track价格校验(前端 + 后端双重验证):
// lib/features/trading/presentation/providers/sell_provider.dart
class SellNotifier extends StateNotifier<SellState> {
// Utility Track券:卖出价 ≤ 面值
bool validatePrice(double sellPrice, double faceValue, CouponType type) {
if (type == CouponType.utility && sellPrice > faceValue) {
return false; // 消费型券不允许溢价
}
return true;
}
}
3.5 P2P转赠
// 转赠流程:输入朋友手机号 → 翻译层解析为链上地址 → 链上P2P转移
class TransferUseCase {
final TransferRepository _repo;
TransferUseCase(this._repo);
Future<Either<Failure, TransferResult>> call({
required String couponId,
required String recipientPhone, // 手机号(非链上地址)
}) {
return _repo.transferByPhone(
couponId: couponId,
recipientPhone: recipientPhone,
);
// 后端翻译层:手机号 → 链上地址 → Gas代付 → 链上P2P转移
}
}
4. AI Agent 集成
4.1 架构
┌──────────────────────────────┐
│ AI Agent UI Layer │
│ 悬浮按钮 / 对话面板 / 建议条 │
├──────────────────────────────┤
│ AI Agent SDK │
│ 对话管理 / 上下文组装 / 流式 │
├──────────────────────────────┤
│ WebSocket连接 │
│ Agent Gateway API │
└──────────────────────────────┘
4.2 悬浮入口按钮
// lib/features/ai_agent/presentation/widgets/ai_fab.dart
class AiAgentFab extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final unreadCount = ref.watch(aiUnreadCountProvider);
return Positioned(
right: 16,
bottom: 80,
child: GestureDetector(
onTap: () => _showAgentPanel(context),
onLongPress: () => _showQuickActions(context),
child: Stack(
children: [
CircleAvatar(
radius: 28,
child: Icon(Icons.smart_toy_outlined),
),
if (unreadCount > 0)
Positioned(
right: 0, top: 0,
child: Badge(count: unreadCount),
),
],
),
),
);
}
}
4.3 对话面板(流式输出)
// lib/features/ai_agent/presentation/pages/agent_chat_panel.dart
class AgentChatPanel extends ConsumerStatefulWidget {
@override
_AgentChatPanelState createState() => _AgentChatPanelState();
}
class _AgentChatPanelState extends ConsumerState<AgentChatPanel> {
final _channel = WebSocketChannel.connect(
Uri.parse('wss://api.gogenex.com/agent/ws'),
);
void _sendMessage(String text) {
final context = _buildContext(); // 组装上下文
_channel.sink.add(jsonEncode({
'type': 'chat',
'message': text,
'context': context,
}));
}
Map<String, dynamic> _buildContext() {
return {
'user_profile': ref.read(userProfileProvider).toJson(),
'current_page': GoRouter.of(context).location,
'coupon_portfolio': ref.read(myCouponsProvider).toSummary(),
'recent_actions': ref.read(recentActionsProvider),
};
}
}
4.4 消费者端AI场景
| 场景 | 实现方式 |
|---|---|
| 智能选券推荐 | 首页Feed中插入AI推荐卡片 |
| 价格顾问 | 券详情页底部AI分析标签 |
| 到期管理 | 我的券列表AI排序+推送提醒 |
| 出售定价 | 出售页面AI建议价格+解释 |
| 自然语言搜索 | 对话面板返回筛选券卡片 |
5. 商户端核销模块
5.1 核销方式
| 方式 | 场景 | 技术 |
|---|---|---|
| 扫码核销 | 门店扫消费者券码 | Camera → QR解码 → API调用 |
| 输码核销 | 手动输入券码 | 文本输入 → API调用 |
| 离线核销 | 网络不可用 | 本地验证 → 队列缓存 → 联网同步 |
5.2 离线核销实现
// lib/features/merchant/data/services/offline_redeem_service.dart
class OfflineRedeemService {
final _queue = HiveBox<PendingRedemption>('offline_queue');
final _localValidator = LocalCouponValidator();
/// 离线核销:本地验证 + 缓存 + 联网自动同步
Future<RedeemResult> redeemOffline(String couponCode) async {
// 1. 本地验证(预下载的有效券列表 + 签名验证)
final isValid = await _localValidator.validate(couponCode);
if (!isValid) return RedeemResult.invalid();
// 2. 本地标记已核销,加入同步队列
await _queue.add(PendingRedemption(
couponCode: couponCode,
timestamp: DateTime.now(),
storeId: currentStoreId,
));
return RedeemResult.pendingSync();
}
/// 联网后自动同步
Future<void> syncPendingRedemptions() async {
final pending = _queue.values.toList();
for (final item in pending) {
try {
await _api.confirmRedemption(item);
await _queue.delete(item.key);
} catch (_) {
// 重试逻辑
}
}
}
}
6. 网络层
6.1 Dio配置
// lib/core/network/api_client.dart
class ApiClient {
late final Dio _dio;
ApiClient() {
_dio = Dio(BaseOptions(
baseUrl: 'https://api.gogenex.com/api/v1',
connectTimeout: Duration(seconds: 10),
receiveTimeout: Duration(seconds: 30),
));
_dio.interceptors.addAll([
AuthInterceptor(), // JWT Token注入
LoggingInterceptor(), // 日志
RetryInterceptor(), // 重试策略
ErrorInterceptor(), // 统一错误处理
]);
}
}
6.2 错误处理
// lib/core/network/failure.dart
@freezed
class Failure with _$Failure {
const factory Failure.network({String? message}) = NetworkFailure;
const factory Failure.server({required int code, String? message}) = ServerFailure;
const factory Failure.auth({String? message}) = AuthFailure;
const factory Failure.validation({required Map<String, String> errors}) = ValidationFailure;
}
7. 状态管理模式(Riverpod)
// 只读数据查询
final couponListProvider = FutureProvider.autoDispose<List<Coupon>>((ref) async {
return ref.watch(couponRepoProvider).getAll();
});
// 可变状态管理
final cartProvider = StateNotifierProvider<CartNotifier, CartState>((ref) {
return CartNotifier(ref.watch(orderRepoProvider));
});
// 异步操作
final purchaseProvider = FutureProvider.family<Order, PurchaseParams>((ref, params) {
return ref.watch(orderRepoProvider).purchase(params);
});
8. 应用构建配置
8.1 多环境配置
// lib/core/config/env.dart
enum Environment { dev, staging, prod }
class AppConfig {
static late Environment env;
static String get apiBase => switch (env) {
Environment.dev => 'https://dev-api.gogenex.com',
Environment.staging => 'https://staging-api.gogenex.com',
Environment.prod => 'https://api.gogenex.com',
};
}
8.2 Flavor构建
# Consumer App
flutter run --flavor consumer -t lib/main_consumer.dart
# Merchant App
flutter run --flavor merchant -t lib/main_merchant.dart
Consumer和Merchant共享核心模块(core/、shared/),通过不同入口和路由配置区分功能。
9. 测试策略
| 层级 | 工具 | 覆盖目标 |
|---|---|---|
| 单元测试 | flutter_test + mocktail |
UseCase、Repository、Provider |
| Widget测试 | flutter_test |
关键页面组件 |
| 集成测试 | integration_test |
购买流程、核销流程、转赠流程 |
| Golden测试 | golden_toolkit |
UI快照回归 |
// test/features/trading/sell_notifier_test.dart
void main() {
test('Utility券不允许溢价出售', () {
final notifier = SellNotifier();
expect(
notifier.validatePrice(110.0, 100.0, CouponType.utility),
false, // 110 > 面值100,拒绝
);
expect(
notifier.validatePrice(85.0, 100.0, CouponType.utility),
true, // 85 < 面值100,允许
);
});
}
10. 性能优化
| 策略 | 实现 |
|---|---|
| 图片懒加载 | cached_network_image + CDN |
| 列表虚拟化 | ListView.builder + 分页加载 |
| 离线缓存 | Hive本地数据库 + 增量同步 |
| 启动优化 | 延迟初始化非核心服务 |
| 包体积 | Tree-shaking + 延迟加载 |
| 内存管理 | autoDispose Provider自动释放 |
11. 安全规范
- JWT Token存储于Flutter Secure Storage(非SharedPreferences)
- 敏感操作(大额交易、转赠)需二次验证(PIN/生物识别)
- API通信全链路HTTPS,Certificate Pinning
- 本地数据加密存储(Hive加密Box)
- 禁止在日志中输出敏感信息(Token、用户手机号)
- ProGuard/R8混淆(Android)
12. 发布流程
开发 → 提交PR → CI自动测试 → Code Review → 合入main
↓
自动构建(GitHub Actions / GitLab CI)
↓
┌────────────────┼────────────────┐
↓ ↓ ↓
Dev Build Staging Build Prod Build
(内部测试) (TestFlight/Beta) (App Store/Play Store)
文档版本: v1.0 基于: Genex 券交易平台 - 软件需求规格说明书 v4.1 技术栈: Flutter 3.x + Riverpod + Clean Architecture