refactor(apps): 重构数据层目录结构并新增启动预热编排器
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
import 'package:social_app/data/network/i_api_client.dart';
|
||||
|
||||
class TodoApi {
|
||||
final IApiClient _client;
|
||||
@@ -0,0 +1,104 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../../data/cache/cache_store.dart';
|
||||
import '../../../../data/cache/cache_policy.dart';
|
||||
import '../../../../data/cache/cached_repository.dart';
|
||||
import '../apis/todo_api.dart';
|
||||
|
||||
class TodoRepository extends CachedRepository<List<TodoResponse>> {
|
||||
static const String pendingListKey = 'todo:list:pending';
|
||||
|
||||
final TodoApi api;
|
||||
final CacheInvalidator invalidator;
|
||||
|
||||
TodoRepository({
|
||||
required this.api,
|
||||
required super.store,
|
||||
required this.invalidator,
|
||||
super.now,
|
||||
}) : super(
|
||||
policy: const CachePolicy(
|
||||
softTtl: Duration(seconds: 30),
|
||||
hardTtl: Duration(minutes: 10),
|
||||
minRefreshInterval: Duration(seconds: 15),
|
||||
),
|
||||
encodeValue: _encodeTodoList,
|
||||
decodeValue: _decodeTodoList,
|
||||
);
|
||||
|
||||
Future<List<TodoResponse>> getPendingTodos({
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
return getOrLoad(
|
||||
key: pendingListKey,
|
||||
forceRefresh: forceRefresh,
|
||||
loadFromRemote: api.getPendingTodos,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> completeTodo(String id) async {
|
||||
final CacheEntry<List<TodoResponse>>? cached = await readCacheEntry(
|
||||
pendingListKey,
|
||||
);
|
||||
if (cached != null) {
|
||||
final next = cached.value
|
||||
.where((todo) => todo.id != id)
|
||||
.toList(growable: false);
|
||||
await writeCacheEntry(pendingListKey, next);
|
||||
}
|
||||
|
||||
try {
|
||||
await api.completeTodo(id);
|
||||
invalidator.invalidate(pendingListKey);
|
||||
} catch (error) {
|
||||
if (cached != null) {
|
||||
await writeCacheEntry(pendingListKey, cached.value);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> invalidatePending() {
|
||||
invalidator.invalidate(pendingListKey);
|
||||
return Future<void>.value();
|
||||
}
|
||||
|
||||
static Object? _encodeTodoList(List<TodoResponse> todos) {
|
||||
return todos.map(_encodeTodo).toList(growable: false);
|
||||
}
|
||||
|
||||
static List<TodoResponse> _decodeTodoList(Object? raw) {
|
||||
if (raw is! List) {
|
||||
throw const FormatException('Invalid cached todo list payload');
|
||||
}
|
||||
return raw
|
||||
.whereType<Map>()
|
||||
.map((item) => TodoResponse.fromJson(Map<String, dynamic>.from(item)))
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
static Map<String, Object?> _encodeTodo(TodoResponse todo) {
|
||||
return <String, Object?>{
|
||||
'id': todo.id,
|
||||
'owner_id': todo.ownerId,
|
||||
'title': todo.title,
|
||||
'description': todo.description,
|
||||
'order': todo.order,
|
||||
'priority': todo.priority,
|
||||
'status': todo.status,
|
||||
'completed_at': todo.completedAt?.toIso8601String(),
|
||||
'created_at': todo.createdAt.toIso8601String(),
|
||||
'updated_at': todo.updatedAt.toIso8601String(),
|
||||
'schedule_items': todo.scheduleItems.map(_encodeScheduleItem).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
static Map<String, Object?> _encodeScheduleItem(ScheduleItemBasic item) {
|
||||
return <String, Object?>{
|
||||
'id': item.id,
|
||||
'title': item.title,
|
||||
'start_at': item.startAt.toIso8601String(),
|
||||
'end_at': item.endAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../data/cache/cache_entry.dart';
|
||||
import '../../../data/cache/cache_invalidator.dart';
|
||||
import '../../../data/cache/cache_policy.dart';
|
||||
import '../../../data/cache/cached_repository.dart';
|
||||
import 'todo_api.dart';
|
||||
|
||||
class TodoRepository extends CachedRepository<List<TodoResponse>> {
|
||||
static const String pendingListKey = 'todo:list:pending';
|
||||
|
||||
final TodoApi api;
|
||||
final CacheInvalidator invalidator;
|
||||
|
||||
TodoRepository({
|
||||
required this.api,
|
||||
required super.store,
|
||||
required this.invalidator,
|
||||
super.now,
|
||||
}) : super(
|
||||
policy: const CachePolicy(
|
||||
softTtl: Duration(days: 3650),
|
||||
hardTtl: Duration(days: 3650),
|
||||
minRefreshInterval: Duration(days: 3650),
|
||||
),
|
||||
);
|
||||
|
||||
Future<List<TodoResponse>> getPendingTodos({
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
return getOrLoad(
|
||||
key: pendingListKey,
|
||||
forceRefresh: forceRefresh,
|
||||
loadFromRemote: api.getPendingTodos,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> completeTodo(String id) async {
|
||||
final CacheEntry<List<TodoResponse>>? cached = await readCacheEntry(
|
||||
pendingListKey,
|
||||
);
|
||||
if (cached != null) {
|
||||
final next = cached.value
|
||||
.where((todo) => todo.id != id)
|
||||
.toList(growable: false);
|
||||
await writeCacheEntry(pendingListKey, next);
|
||||
}
|
||||
|
||||
try {
|
||||
await api.completeTodo(id);
|
||||
invalidator.invalidate(pendingListKey);
|
||||
} catch (error) {
|
||||
if (cached != null) {
|
||||
await store.write<CacheEntry<List<TodoResponse>>>(
|
||||
pendingListKey,
|
||||
cached,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> invalidatePending() {
|
||||
invalidator.invalidate(pendingListKey);
|
||||
return Future<void>.value();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import '../../../../shared/widgets/error_retry_surface.dart';
|
||||
import '../../../../shared/widgets/full_screen_loading.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/todo_api.dart';
|
||||
import '../../data/apis/todo_api.dart';
|
||||
|
||||
enum _TodoHeaderAction { edit, delete }
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../data/repositories/calendar_event_repository.dart';
|
||||
import '../../../../data/repositories/models/calendar_event.dart';
|
||||
import '../../../../features/calendar/data/repositories/calendar_repository.dart';
|
||||
import '../../../../features/calendar/data/models/schedule_item_model.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
@@ -15,7 +15,7 @@ import '../../../../shared/widgets/error_retry_surface.dart';
|
||||
import '../../../../shared/widgets/full_screen_loading.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/todo_api.dart';
|
||||
import '../../data/apis/todo_api.dart';
|
||||
|
||||
class TodoEditScreen extends StatefulWidget {
|
||||
final String? todoId;
|
||||
@@ -32,8 +32,7 @@ class TodoEditScreen extends StatefulWidget {
|
||||
|
||||
class _TodoEditScreenState extends State<TodoEditScreen> {
|
||||
final TodoApi _todoApi = sl<TodoApi>();
|
||||
final CalendarEventRepository _calendarRepository =
|
||||
sl<CalendarEventRepository>();
|
||||
final CalendarRepository _calendarRepository = sl<CalendarRepository>();
|
||||
|
||||
final TextEditingController _titleController = TextEditingController();
|
||||
final TextEditingController _descriptionController = TextEditingController();
|
||||
@@ -94,7 +93,7 @@ class _TodoEditScreenState extends State<TodoEditScreen> {
|
||||
..clear()
|
||||
..addAll(todo?.scheduleItems.map((item) => item.id) ?? const []);
|
||||
_scheduleItems = scheduleItems
|
||||
.where((item) => item.status == CalendarEventStatus.active)
|
||||
.where((item) => item.status == ScheduleStatus.active)
|
||||
.map(
|
||||
(item) => _ScheduleItemSimple(
|
||||
id: item.id,
|
||||
|
||||
@@ -16,8 +16,8 @@ import '../../../../shared/widgets/bottom_dock.dart';
|
||||
import '../../../../shared/state/calendar_state_manager.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/todo_api.dart';
|
||||
import '../../data/todo_repository.dart';
|
||||
import '../../data/apis/todo_api.dart';
|
||||
import '../../data/repositories/todo_repository.dart';
|
||||
|
||||
class TodoQuadrantsScreen extends StatefulWidget {
|
||||
const TodoQuadrantsScreen({super.key});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/features/todo/data/todo_api.dart';
|
||||
import 'package:social_app/features/todo/data/apis/todo_api.dart';
|
||||
|
||||
class TodoDragItem extends StatelessWidget {
|
||||
final TodoResponse todo;
|
||||
|
||||
Reference in New Issue
Block a user