From 5e169251fe3d63c8705732a2ae21b32c3ccef8c7 Mon Sep 17 00:00:00 2001 From: qzl Date: Tue, 3 Mar 2026 10:12:46 +0800 Subject: [PATCH] refactor(apps): simplify API layer by removing redundant wrapper classes - Remove ApiClientWrapper and MockApiClientWrapper - Simplify IApiClient interface - Update dependency injection configuration - Optimize calendar service and AG-UI chat models --- apps/lib/core/api/api_client.dart | 7 ++- apps/lib/core/api/i_api_client.dart | 52 ------------------- apps/lib/core/api/mock_api_client.dart | 28 ++++------ apps/lib/core/di/injection.dart | 48 ++++++++++------- apps/lib/features/auth/data/auth_api.dart | 4 +- .../data/services/mock_calendar_service.dart | 48 ++++++++--------- .../ui/screens/calendar_month_screen.dart | 2 +- .../ui/widgets/create_event_sheet.dart | 2 +- .../chat/data/models/ag_ui_event.dart | 4 +- .../chat/data/models/ag_ui_event.g.dart | 9 ---- .../chat/data/services/ag_ui_service.dart | 16 +++--- apps/lib/features/users/data/users_api.dart | 4 +- 12 files changed, 84 insertions(+), 140 deletions(-) diff --git a/apps/lib/core/api/api_client.dart b/apps/lib/core/api/api_client.dart index 70ec78c..e3421ac 100644 --- a/apps/lib/core/api/api_client.dart +++ b/apps/lib/core/api/api_client.dart @@ -1,9 +1,10 @@ import 'package:dio/dio.dart'; import 'api_exception.dart'; import 'api_interceptor.dart'; +import 'i_api_client.dart'; import '../storage/token_storage.dart'; -class ApiClient { +class ApiClient implements IApiClient { final Dio _dio; final TokenStorage _tokenStorage; final ApiInterceptor _interceptor; @@ -44,6 +45,7 @@ class ApiClient { }; } + @override Future> get(String path, {Options? options}) async { try { return await _dio.get(path, options: options); @@ -52,6 +54,7 @@ class ApiClient { } } + @override Future> post( String path, { dynamic data, @@ -64,6 +67,7 @@ class ApiClient { } } + @override Future> patch( String path, { dynamic data, @@ -76,6 +80,7 @@ class ApiClient { } } + @override Future> delete( String path, { dynamic data, diff --git a/apps/lib/core/api/i_api_client.dart b/apps/lib/core/api/i_api_client.dart index bd1e22a..7059b7d 100644 --- a/apps/lib/core/api/i_api_client.dart +++ b/apps/lib/core/api/i_api_client.dart @@ -1,6 +1,4 @@ import 'package:dio/dio.dart'; -import 'api_client.dart'; -import 'mock_api_client.dart'; abstract class IApiClient { Future> get(String path, {Options? options}); @@ -8,53 +6,3 @@ abstract class IApiClient { Future> patch(String path, {dynamic data, Options? options}); Future> delete(String path, {dynamic data, Options? options}); } - -class ApiClientWrapper implements IApiClient { - final ApiClient _client; - - ApiClientWrapper(this._client); - - @override - Future> get(String path, {Options? options}) => - _client.get(path, options: options); - - @override - Future> post(String path, {dynamic data, Options? options}) => - _client.post(path, data: data, options: options); - - @override - Future> patch(String path, {dynamic data, Options? options}) => - _client.patch(path, data: data, options: options); - - @override - Future> delete( - String path, { - dynamic data, - Options? options, - }) => _client.delete(path, data: data, options: options); -} - -class MockApiClientWrapper implements IApiClient { - final MockApiClient _client; - - MockApiClientWrapper(this._client); - - @override - Future> get(String path, {Options? options}) => - _client.get(path, options: options); - - @override - Future> post(String path, {dynamic data, Options? options}) => - _client.post(path, data: data, options: options); - - @override - Future> patch(String path, {dynamic data, Options? options}) => - _client.patch(path, data: data, options: options); - - @override - Future> delete( - String path, { - dynamic data, - Options? options, - }) => _client.delete(path, data: data, options: options); -} diff --git a/apps/lib/core/api/mock_api_client.dart b/apps/lib/core/api/mock_api_client.dart index aef76c4..d10baa9 100644 --- a/apps/lib/core/api/mock_api_client.dart +++ b/apps/lib/core/api/mock_api_client.dart @@ -1,9 +1,12 @@ import 'package:dio/dio.dart'; +import 'i_api_client.dart'; -class MockApiClient { - final Map _handlers = {}; +typedef MockHandler = dynamic Function(dynamic data); - void registerHandler(String path, String method, _MockHandler handler) { +class MockApiClient implements IApiClient { + final Map _handlers = {}; + + void registerHandler(String path, String method, MockHandler handler) { final key = '$path:$method'; _handlers[key] = handler; } @@ -12,10 +15,12 @@ class MockApiClient { _handlers.clear(); } + @override Future> get(String path, {Options? options}) async { return _handleRequest('GET', path, options: options); } + @override Future> post( String path, { dynamic data, @@ -24,6 +29,7 @@ class MockApiClient { return _handleRequest('POST', path, data: data, options: options); } + @override Future> patch( String path, { dynamic data, @@ -32,6 +38,7 @@ class MockApiClient { return _handleRequest('PATCH', path, data: data, options: options); } + @override Future> delete( String path, { dynamic data, @@ -70,18 +77,3 @@ class MockApiClient { ); } } - -typedef _MockHandler = dynamic Function(dynamic data); - -class MockApiClientHolder { - static MockApiClient? _instance; - - static MockApiClient get instance { - _instance ??= MockApiClient(); - return _instance!; - } - - static void reset() { - _instance = null; - } -} diff --git a/apps/lib/core/di/injection.dart b/apps/lib/core/di/injection.dart index 87f0db9..4458909 100644 --- a/apps/lib/core/di/injection.dart +++ b/apps/lib/core/di/injection.dart @@ -2,6 +2,8 @@ import 'package:dio/dio.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get_it/get_it.dart'; import '../api/api_client.dart'; +import '../api/i_api_client.dart'; +import '../api/mock_api_client.dart'; import '../storage/token_storage.dart'; import '../config/env.dart'; import '../../features/auth/data/auth_api.dart'; @@ -13,20 +15,27 @@ import '../../features/calendar/ui/calendar_state_manager.dart'; final sl = GetIt.instance; Future configureDependencies() async { - if (sl.isRegistered()) { + if (sl.isRegistered()) { await sl.reset(); } - final dio = Dio(BaseOptions(baseUrl: Env.apiUrl)); - final secureStorage = const FlutterSecureStorage(); - final tokenStorage = SecureTokenStorage(secureStorage); + final IApiClient apiClient; + final SecureTokenStorage tokenStorage; - final apiClient = ApiClient( - baseUrl: Env.apiUrl, - tokenStorage: tokenStorage, - dio: dio, - ); - sl.registerSingleton(apiClient); + if (Env.isMockApi) { + apiClient = MockApiClient(); + tokenStorage = SecureTokenStorage(const FlutterSecureStorage()); + } else { + final dio = Dio(BaseOptions(baseUrl: Env.apiUrl)); + tokenStorage = SecureTokenStorage(const FlutterSecureStorage()); + apiClient = ApiClient( + baseUrl: Env.apiUrl, + tokenStorage: tokenStorage, + dio: dio, + ); + } + + sl.registerSingleton(apiClient); final authApi = AuthApi(apiClient); sl.registerSingleton(authApi); @@ -37,16 +46,17 @@ Future configureDependencies() async { ); sl.registerSingleton(authRepository); - apiClient.setRefreshCallback((token) async { - try { - await authRepository.refreshSession(token); - return true; - } catch (_) { - return false; - } - }); + if (!Env.isMockApi) { + (apiClient as ApiClient).setRefreshCallback((token) async { + try { + await authRepository.refreshSession(token); + return true; + } catch (_) { + return false; + } + }); + } sl.registerSingleton(AuthBloc(authRepository)); - sl.registerSingleton(CalendarStateManager()); } diff --git a/apps/lib/features/auth/data/auth_api.dart b/apps/lib/features/auth/data/auth_api.dart index 2fa3afb..95d7db3 100644 --- a/apps/lib/features/auth/data/auth_api.dart +++ b/apps/lib/features/auth/data/auth_api.dart @@ -1,10 +1,10 @@ -import 'package:social_app/core/api/api_client.dart'; +import 'package:social_app/core/api/i_api_client.dart'; import 'models/signup_request.dart'; import 'models/login_request.dart'; import 'models/auth_response.dart'; class AuthApi { - final ApiClient _client; + final IApiClient _client; static const _prefix = '/api/v1/auth'; AuthApi(this._client); diff --git a/apps/lib/features/calendar/data/services/mock_calendar_service.dart b/apps/lib/features/calendar/data/services/mock_calendar_service.dart index b6b4178..9719f33 100644 --- a/apps/lib/features/calendar/data/services/mock_calendar_service.dart +++ b/apps/lib/features/calendar/data/services/mock_calendar_service.dart @@ -1,4 +1,4 @@ -import 'package:social_app/core/config/env.dart'; +import 'package:social_app/core/api/i_api_client.dart'; import '../models/schedule_item_model.dart'; class MockCalendarService { @@ -56,54 +56,50 @@ class MockCalendarService { } class CalendarService { - static final CalendarService _instance = CalendarService._internal(); - factory CalendarService() => _instance; - CalendarService._internal(); + final IApiClient? _apiClient; + final MockCalendarService _mock = MockCalendarService(); - MockCalendarService get _mock => MockCalendarService(); + CalendarService({IApiClient? apiClient}) : _apiClient = apiClient; List getEventsForDay(DateTime date) { - if (Env.isMockApi) { - return _mock.getEventsForDay(date); + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + return _mock.getEventsForDay(date); } List getEventsForRange(DateTime start, DateTime end) { - if (Env.isMockApi) { - return _mock.getEventsForRange(start, end); + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + return _mock.getEventsForRange(start, end); } ScheduleItemModel? getEventById(String id) { - if (Env.isMockApi) { - return _mock.getEventById(id); + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + return _mock.getEventById(id); } void addEvent(ScheduleItemModel event) { - if (Env.isMockApi) { - _mock.addEvent(event); - return; + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + _mock.addEvent(event); } void updateEvent(ScheduleItemModel event) { - if (Env.isMockApi) { - _mock.updateEvent(event); - return; + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + _mock.updateEvent(event); } void deleteEvent(String id) { - if (Env.isMockApi) { - _mock.deleteEvent(id); - return; + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } - throw UnimplementedError('Real API not implemented'); + _mock.deleteEvent(id); } } diff --git a/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart b/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart index 6c681c2..7c4712c 100644 --- a/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart +++ b/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart @@ -304,7 +304,7 @@ class _CalendarMonthScreenState extends State { vertical: 2, ), decoration: BoxDecoration( - color: color.withOpacity(0.2), + color: color.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: Text( diff --git a/apps/lib/features/calendar/ui/widgets/create_event_sheet.dart b/apps/lib/features/calendar/ui/widgets/create_event_sheet.dart index 96200bd..8d69ae1 100644 --- a/apps/lib/features/calendar/ui/widgets/create_event_sheet.dart +++ b/apps/lib/features/calendar/ui/widgets/create_event_sheet.dart @@ -368,7 +368,7 @@ class _CreateEventSheetState extends State Row( children: defaultColors.map((color) { final colorHex = - '#${color.value.toRadixString(16).substring(2).toUpperCase()}'; + '#${color.toARGB32().toRadixString(16).substring(2).toUpperCase()}'; final isSelected = _selectedColor == colorHex; return GestureDetector( onTap: () => setState(() => _selectedColor = colorHex), diff --git a/apps/lib/features/chat/data/models/ag_ui_event.dart b/apps/lib/features/chat/data/models/ag_ui_event.dart index fde755d..d31cd6b 100644 --- a/apps/lib/features/chat/data/models/ag_ui_event.dart +++ b/apps/lib/features/chat/data/models/ag_ui_event.dart @@ -89,7 +89,7 @@ final _typeToFactory = { AgUiEventType.unknown: UnknownAgUiEvent.fromJson, }; -@JsonSerializable() +@JsonSerializable(createFactory: false) class AgUiEvent { final AgUiEventType type; @@ -104,7 +104,7 @@ class AgUiEvent { Map toJson() => _$AgUiEventToJson(this); } -@JsonSerializable() +@JsonSerializable(createFactory: false, createToJson: false) class UnknownAgUiEvent extends AgUiEvent { final Map rawJson; diff --git a/apps/lib/features/chat/data/models/ag_ui_event.g.dart b/apps/lib/features/chat/data/models/ag_ui_event.g.dart index 684f7d5..ac6858c 100644 --- a/apps/lib/features/chat/data/models/ag_ui_event.g.dart +++ b/apps/lib/features/chat/data/models/ag_ui_event.g.dart @@ -6,9 +6,6 @@ part of 'ag_ui_event.dart'; // JsonSerializableGenerator // ************************************************************************** -AgUiEvent _$AgUiEventFromJson(Map json) => - AgUiEvent(type: $enumDecode(_$AgUiEventTypeEnumMap, json['type'])); - Map _$AgUiEventToJson(AgUiEvent instance) => { 'type': _$AgUiEventTypeEnumMap[instance.type]!, }; @@ -29,12 +26,6 @@ const _$AgUiEventTypeEnumMap = { AgUiEventType.unknown: 'unknown', }; -UnknownAgUiEvent _$UnknownAgUiEventFromJson(Map json) => - UnknownAgUiEvent(rawJson: json['rawJson'] as Map); - -Map _$UnknownAgUiEventToJson(UnknownAgUiEvent instance) => - {'rawJson': instance.rawJson}; - RunStartedEvent _$RunStartedEventFromJson(Map json) => RunStartedEvent( threadId: json['threadId'] as String, diff --git a/apps/lib/features/chat/data/services/ag_ui_service.dart b/apps/lib/features/chat/data/services/ag_ui_service.dart index 1eef20c..074f6f3 100644 --- a/apps/lib/features/chat/data/services/ag_ui_service.dart +++ b/apps/lib/features/chat/data/services/ag_ui_service.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:convert'; -import 'package:social_app/core/config/env.dart'; +import 'package:social_app/core/api/i_api_client.dart'; import '../ai/ai_decision_engine.dart'; import '../models/ag_ui_event.dart'; @@ -24,27 +24,29 @@ const _textChunkSize = 10; typedef EventCallback = void Function(AgUiEvent event); class AgUiService { + final IApiClient? _apiClient; EventCallback onEvent; final AiDecisionEngine _decisionEngine; final MockHistoryService _historyService; - AgUiService({EventCallback? onEvent}) + AgUiService({EventCallback? onEvent, IApiClient? apiClient}) : onEvent = onEvent ?? ((_) {}), + _apiClient = apiClient, _decisionEngine = AiDecisionEngine(), _historyService = MockHistoryService(); Future sendMessage(String content) async { - if (Env.isMockApi) { - await _mockEventStream(content); - } else { + if (_apiClient != null) { throw UnimplementedError('Real API not implemented'); } + await _mockEventStream(content); } Future loadHistory({DateTime? beforeDate}) async { - if (Env.isMockApi) { - await _mockLoadHistory(beforeDate: beforeDate); + if (_apiClient != null) { + throw UnimplementedError('Real API not implemented'); } + await _mockLoadHistory(beforeDate: beforeDate); } bool hasEarlierHistory(DateTime fromDate) { diff --git a/apps/lib/features/users/data/users_api.dart b/apps/lib/features/users/data/users_api.dart index a55cc7e..0d5cf50 100644 --- a/apps/lib/features/users/data/users_api.dart +++ b/apps/lib/features/users/data/users_api.dart @@ -1,8 +1,8 @@ -import 'package:social_app/core/api/api_client.dart'; +import 'package:social_app/core/api/i_api_client.dart'; import 'models/user_response.dart'; class UsersApi { - final ApiClient _client; + final IApiClient _client; static const _prefix = '/api/v1/users'; UsersApi(this._client);