feat: 切换邮箱认证并重构前后端启动与门禁

This commit is contained in:
qzl
2026-04-02 18:39:35 +08:00
parent 92cdfd9fca
commit 31594558eb
116 changed files with 5608 additions and 628 deletions
@@ -0,0 +1,98 @@
import 'package:flutter/foundation.dart';
import '../../../../core/logging/logger.dart';
import '../../data/repositories/auth_repository.dart';
import 'auth_state.dart';
class AuthBloc extends ChangeNotifier {
AuthBloc({required AuthRepository repository}) : _repository = repository;
final AuthRepository _repository;
final Logger _logger = getLogger('features.auth.bloc');
AuthState _state = AuthState.initial;
bool _handlingUnauthorized = false;
AuthState get state => _state;
Future<void> start() async {
_state = _state.copyWith(status: AuthStatus.loading, errorMessage: null);
notifyListeners();
try {
final user = await _repository.recoverSession();
if (user == null) {
_state = const AuthState(status: AuthStatus.unauthenticated);
} else {
_state = AuthState(status: AuthStatus.authenticated, user: user);
}
notifyListeners();
} catch (error, stackTrace) {
_logger.error(
message: 'Session recovery failed',
error: error,
stackTrace: stackTrace,
);
await _repository.clearLocalSession();
_state = AuthState(
status: AuthStatus.unauthenticated,
errorMessage: _toSafeMessage(error),
);
notifyListeners();
}
}
Future<void> sendOtp(String email) async {
await _repository.sendOtp(email);
}
Future<void> loginWithOtp({
required String email,
required String otp,
}) async {
final user = await _repository.loginWithEmailOtp(email: email, otp: otp);
_logger.info(message: 'User logged in', extra: {'user_id': user.id});
_state = AuthState(status: AuthStatus.authenticated, user: user);
notifyListeners();
}
Future<void> logout() async {
Object? caughtError;
StackTrace? caughtStackTrace;
try {
await _repository.logout();
} catch (error, stackTrace) {
caughtError = error;
caughtStackTrace = stackTrace;
_logger.error(
message: 'User logout failed',
error: error,
stackTrace: stackTrace,
);
}
_logger.info(message: 'User logged out');
_state = const AuthState(status: AuthStatus.unauthenticated);
notifyListeners();
if (caughtError != null) {
Error.throwWithStackTrace(caughtError, caughtStackTrace!);
}
}
Future<void> handleUnauthorized401() async {
if (_handlingUnauthorized) {
return;
}
_handlingUnauthorized = true;
try {
await _repository.clearLocalSession();
_logger.warning(message: 'Session invalidated by 401 callback');
_state = const AuthState(status: AuthStatus.unauthenticated);
notifyListeners();
} finally {
_handlingUnauthorized = false;
}
}
String _toSafeMessage(Object error) {
return 'Request failed, please try again';
}
}
@@ -0,0 +1,25 @@
import '../../data/models/auth_user.dart';
enum AuthStatus { initial, loading, authenticated, unauthenticated }
class AuthState {
const AuthState({required this.status, this.user, this.errorMessage});
final AuthStatus status;
final AuthUser? user;
final String? errorMessage;
AuthState copyWith({
AuthStatus? status,
AuthUser? user,
String? errorMessage,
}) {
return AuthState(
status: status ?? this.status,
user: user ?? this.user,
errorMessage: errorMessage,
);
}
static const AuthState initial = AuthState(status: AuthStatus.initial);
}