核心模式

1. 服务模式 (Service Pattern)

  • AuthService: 负责所有认证相关操作

  • PersistenceStorage: 负责本地数据持久化

  • GitCodeApiClient: 负责网络API通信

2. 状态管理 (State Management)

  • 使用setState进行组件级状态管理

  • 使用Stream进行异步数据流管理

  • 使用SharedPreferences进行持久化状态存储

3. 依赖解耦 (Dependency Decoupling)

  • API客户端不直接依赖AuthService,通过SharedPreferences间接获取Token

  • 各服务之间通过接口和回调进行通信,避免紧耦合

用户操作 → UI层 → 业务逻辑层 → 数据层 → 状态更新
    ↓        ↓         ↓         ↓        ↓
点击登录 → LoginPage → AuthService → API验证 → 保存Token
    ↓        ↓         ↓         ↓        ↓
收藏仓库 → DetailPage → Persistence → SharedPrefs → 更新UI
    ↓        ↓         ↓         ↓        ↓
查看收藏 → ProfilePage → 读取存储 → 聚合数据 → 显示统计

🛠️ 关键技术点

1. Token安全策略

// 安全策略:
// 1. Token不存储在代码中
// 2. 验证通过后才持久化
// 3. 每次API请求都携带Token
// 4. Token无效时自动引导重新登录

2. 多用户数据隔离

// 数据隔离机制:
// 键格式:${username}_${dataType}
// 示例:john_doe_starred_repositories
// 优势:支持多账户切换,数据互不干扰

代码组织结构

lib/
├── main.dart                    # 应用入口,认证包装器
├── services/
│   ├── auth_service.dart       # 认证服务核心
│   ├── persistence_storage.dart # 持久化存储
│   └── app_init.dart          # 应用初始化
├── core/
│   ├── api_client.dart         # API客户端
│   └── models/                # 数据模型
├── pages/
│   ├── login_page.dart        # 登录页面
│   ├── home_page.dart         # 首页
│   ├── profile_page.dart      # 个人中心
│   ├── repository_detail_page.dart # 仓库详情
│   └── user_detail_page.dart  # 用户详情
└── widgets/                   # 可复用组件

▸ auth_service.dart 是基础依赖层:提供登录、Token 校验、用户信息获取能力,为其他模块提供用户鉴权数据;
▸ persistence_storage.dart 是业务存储层:依赖认证服务的用户信息,实现多用户隔离的本地数据存储;
▸ api_client.dart 是网络请求层:独立读取本地 Token,实现无依赖的接口请求,为业务层提供服务端数据;

关键代码详解

1. 认证服务 (auth_service.dart)

该模块封装了「登录、Token 有效性校验、用户信息持久化、用户信息读取」等所有认证相关逻辑,是用户体系的基础;所有方法均为异步实现,包含完整的异常捕获与兜底处理,返回统一的认证结果模型,业务层可直接根据结果做逻辑处理。

// 登录方法实现
Future<AuthResult> login(String token) async {
  try {
    final prefs = await SharedPreferences.getInstance();
    // 1. 临时保存Token以供验证
    await prefs.setString(_tokenKey, token);

    // 2. 使用Token获取用户信息验证有效性
    final user = await GitCodeApiClient().getCurrentUser();

    // 3. 验证成功后持久化保存
    await prefs.setString(_usernameKey, user.login);
    await prefs.setString(_userKey, _userToJson(user));
    
    return AuthResult.success(user);
  } catch (e) {
    // 4. 验证失败则清理临时数据
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_tokenKey);
    
    return AuthResult.failure('登录失败: ${e.toString()}');
  }
}

// Token验证机制
Future<bool> validateToken(String token) async {
  if (token.isEmpty) return false;
  
  final uri = Uri.parse('$baseUrl/user').replace(
    queryParameters: {'access_token': token},
  );
  
  try {
    final response = await http.get(uri).timeout(const Duration(seconds: 10));
    return response.statusCode == 200;  // 200表示Token有效
  } catch (e) {
    return false;
  }
}
  • 临时Token策略:先保存Token进行验证,验证成功才持久化,防止无效Token污染存储

  • 双向验证:通过调用API接口验证Token,同时获取用户信息

  • 错误回滚:验证失败时清理临时数据,确保状态一致性

2. 持久化存储系统 (persistence_storage.dart)

该模块基于鸿蒙适配版 SharedPreferences 封装,核心承载「收藏仓库、关注用户」等私有业务数据的增删查操作;同时做了数据去重、空值兜底、异常捕获,是连接业务层与本地存储的桥梁,所有存储方法均为静态方法,业务层可直接调用,无需实例化。

// 用户隔离的存储键生成
static Future<String> _getUserKey(String baseKey) async {
  final username = await _authService.getCurrentUsername();
  if (username == null) {
    throw Exception('用户未登录');
  }
  return '${username}_$baseKey';  // 如: "john_doe_starred_repositories"
}

// 收藏仓库实现
static Future<void> starRepository(String repoFullName) async {
  final prefs = await SharedPreferences.getInstance();
  final key = await _getUserKey('starred_repositories');
  final starred = prefs.getStringList(key) ?? [];
  
  // 去重检查
  if (!starred.contains(repoFullName)) {
    starred.add(repoFullName);
    await prefs.setStringList(key, starred);
  }
}

// 检查收藏状态
static Future<bool> isRepositoryStarred(String repoFullName) async {
  try {
    final prefs = await SharedPreferences.getInstance();
    final key = await _getUserKey('starred_repositories');
    final starred = prefs.getStringList(key) ?? [];
    return starred.contains(repoFullName);  // 检查是否在列表中
  } catch (e) {
    return false;  // 出错时默认为未收藏
  }
}
  • 用户隔离:每个用户的收藏/关注数据使用用户名作为前缀,实现多用户数据隔离

  • 数据去重:添加时检查是否已存在,避免重复数据

  • 错误处理:读取失败时返回默认值,避免应用崩溃

  • 键名规范:使用统一命名规范,便于管理和维护

3. API客户端Token管理 (api_client.dart)

封装了所有 GitCode 开放平台的接口请求,核心处理「接口地址拼接、请求参数封装、Token 自动携带、数据解析、异常处理」等逻辑;API 客户端不依赖任何业务服务,直接从本地存储读取 Token,是所有业务层获取服务端数据的统一入口。

// Token获取方法(不依赖AuthService,打破循环依赖)
Future<String?> _getToken() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('user_token');  // 直接从SharedPrefs获取
}

// 所有API请求中的Token应用
Future<List<GitCodeRepository>> searchRepositories({
  required String keyword,
  int page = 1,
  int perPage = 20,
}) async {
  final token = await _getToken();  // 获取Token
  
  if (token == null || token.isEmpty) {
    throw Exception('请先登录获取访问令牌');  // Token检查
  }

  final uri = Uri.parse('$baseUrl/search/repositories').replace(
    queryParameters: {
      'q': keyword,
      'page': page.toString(),
      'per_page': perPage.toString(),
      'access_token': token,  // Token作为查询参数
    },
  );
  
  // ... 发起请求
}
  • 解耦设计:API客户端直接读取SharedPreferences,避免与AuthService循环依赖

  • 统一Token管理:所有API请求前都检查Token有效性

  • 异常处理:Token无效时抛出明确异常,引导用户重新登录

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐