feat: 切换邮箱认证并重构前后端启动与门禁
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../core/logging/logger.dart';
|
||||
import '../../core/network/api_problem.dart';
|
||||
|
||||
class ApiClient {
|
||||
ApiClient({
|
||||
required String baseUrl,
|
||||
Future<String?> Function()? tokenProvider,
|
||||
Future<void> Function()? onUnauthorized,
|
||||
}) : _dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 15),
|
||||
headers: const {'Content-Type': 'application/json'},
|
||||
),
|
||||
) {
|
||||
if (tokenProvider != null) {
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) async {
|
||||
final token = await tokenProvider();
|
||||
if (token != null && token.isNotEmpty) {
|
||||
options.headers['Authorization'] = 'Bearer $token';
|
||||
}
|
||||
handler.next(options);
|
||||
},
|
||||
onError: (error, handler) async {
|
||||
final status = error.response?.statusCode;
|
||||
final authHeader =
|
||||
error.requestOptions.headers['Authorization'] as String?;
|
||||
final hasAuthHeader = authHeader != null && authHeader.isNotEmpty;
|
||||
if (status == 401 && hasAuthHeader && onUnauthorized != null) {
|
||||
await onUnauthorized();
|
||||
}
|
||||
handler.next(error);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final Dio _dio;
|
||||
final Logger _logger = getLogger('data.network.api_client');
|
||||
|
||||
Future<void> postNoContent(String path, {Map<String, dynamic>? data}) async {
|
||||
try {
|
||||
await _dio.post<void>(path, data: data);
|
||||
} on DioException catch (error, stackTrace) {
|
||||
_logger.error(
|
||||
message: 'POST no-content failed',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
throw _mapProblem(error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteNoContent(
|
||||
String path, {
|
||||
Map<String, dynamic>? data,
|
||||
}) async {
|
||||
try {
|
||||
await _dio.delete<void>(path, data: data);
|
||||
} on DioException catch (error, stackTrace) {
|
||||
_logger.error(
|
||||
message: 'DELETE no-content failed',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
throw _mapProblem(error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> postJson(
|
||||
String path, {
|
||||
Map<String, dynamic>? data,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.post<Map<String, dynamic>>(path, data: data);
|
||||
return response.data ?? <String, dynamic>{};
|
||||
} on DioException catch (error, stackTrace) {
|
||||
_logger.error(
|
||||
message: 'POST json failed',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
throw _mapProblem(error);
|
||||
}
|
||||
}
|
||||
|
||||
ApiProblem _mapProblem(DioException error) {
|
||||
final status = error.response?.statusCode ?? 500;
|
||||
final data = error.response?.data;
|
||||
|
||||
if (data is Map<String, dynamic>) {
|
||||
return ApiProblem(
|
||||
status: status,
|
||||
title: (data['title'] as String?) ?? 'Request failed',
|
||||
detail: (data['detail'] as String?) ?? '',
|
||||
code: data['code'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
return ApiProblem(
|
||||
status: status,
|
||||
title: 'Network error',
|
||||
detail: error.message ?? 'Request failed',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LocalKvStore {
|
||||
Future<SharedPreferences> get _prefs async {
|
||||
return SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
Future<void> setString(String key, String value) async {
|
||||
final prefs = await _prefs;
|
||||
await prefs.setString(key, value);
|
||||
}
|
||||
|
||||
Future<String?> getString(String key) async {
|
||||
final prefs = await _prefs;
|
||||
return prefs.getString(key);
|
||||
}
|
||||
|
||||
Future<void> setBool(String key, bool value) async {
|
||||
final prefs = await _prefs;
|
||||
await prefs.setBool(key, value);
|
||||
}
|
||||
|
||||
Future<bool> getBool(String key, {bool fallback = false}) async {
|
||||
final prefs = await _prefs;
|
||||
return prefs.getBool(key) ?? fallback;
|
||||
}
|
||||
|
||||
Future<void> remove(String key) async {
|
||||
final prefs = await _prefs;
|
||||
await prefs.remove(key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user