Files
nj/app/lib/data/models/auth_token.dart
iven 0fe3bc705c feat(app): 实现认证模块 — F2 Auth BLoC + 登录/注册/角色选择/班级码加入
新增文件 (10):
- data/models/user.dart — 用户+角色模型 (匹配后端 UserResp/RoleResp)
- data/models/auth_token.dart — 认证令牌模型 (匹配后端 LoginResp)
- data/repositories/auth_repository.dart — 认证仓库 (JWT 安全持久化 + PIPL 合规)
- features/auth/bloc/auth_bloc.dart — 认证 BLoC (8 种事件, 6 种状态)
- features/auth/bloc/auth_event.dart — 认证事件 (sealed class 穷尽匹配)
- features/auth/bloc/auth_state.dart — 认证状态 (Authenticated 含角色/班级码流程)
- features/auth/views/login_page.dart — 登录/注册页面 (重写占位页面)
- features/auth/views/role_selection_page.dart — 角色选择页 (4 种角色卡片)
- features/auth/views/class_code_join_page.dart — 班级码加入页 (6 位输入)

修改文件 (5):
- pubspec.yaml — 添加 flutter_secure_storage 依赖
- app.dart — 注入 AuthBloc + RepositoryProvider
- main.dart — 简化入口 (认证恢复在 BLoC 中处理)
- core/routing/app_router.dart — 添加认证路由守卫 + 2 新路由
- erp-diary/service/class_service.rs — 移除未使用的 PaginatorTrait import

验证: flutter analyze (0 error) + cargo check 通过
2026-06-01 01:22:53 +08:00

55 lines
1.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 认证令牌模型 — 匹配后端 LoginResp
//
// 管理访问令牌和刷新令牌,支持自动计算过期时间。
// 令牌通过 flutter_secure_storage 安全持久化PIPL 合规要求)。
/// 认证令牌 — 包含访问令牌、刷新令牌和过期信息
class AuthToken {
final String accessToken;
final String refreshToken;
final int expiresIn;
final DateTime expiresAt;
const AuthToken({
required this.accessToken,
required this.refreshToken,
required this.expiresIn,
required this.expiresAt,
});
/// 令牌是否已过期
bool get isExpired => DateTime.now().isAfter(expiresAt);
/// 令牌是否即将过期5 分钟内)
bool get isExpiringSoon =>
DateTime.now().isAfter(expiresAt.subtract(const Duration(minutes: 5)));
/// 从后端 LoginResp JSON 创建
factory AuthToken.fromJson(Map<String, dynamic> json) {
final expiresIn = (json['expires_in'] as int?) ?? 3600;
return AuthToken(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
expiresIn: expiresIn,
expiresAt: DateTime.now().add(Duration(seconds: expiresIn)),
);
}
Map<String, dynamic> toJson() => {
'access_token': accessToken,
'refresh_token': refreshToken,
'expires_in': expiresIn,
'expires_at': expiresAt.toIso8601String(),
};
/// 从持久化存储恢复(使用保存的过期时间)
factory AuthToken.fromStorage(Map<String, dynamic> json) => AuthToken(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
expiresIn: (json['expires_in'] as int?) ?? 3600,
expiresAt: json['expires_at'] != null
? DateTime.parse(json['expires_at'] as String)
: DateTime.now().add(const Duration(hours: 1)),
);
}