refactor(apps): 重构数据层目录结构并新增启动预热编排器
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
import '../../../../data/cache/cache_policy.dart';
|
||||
import '../../../../data/cache/cached_repository.dart';
|
||||
import '../../../../data/network/i_api_client.dart';
|
||||
import '../models/schedule_item_model.dart';
|
||||
|
||||
class CalendarRepository extends CachedRepository<List<ScheduleItemModel>> {
|
||||
final IApiClient _apiClient;
|
||||
static const _prefix = '/api/v1/schedule-items';
|
||||
|
||||
CalendarRepository({
|
||||
required super.store,
|
||||
required IApiClient apiClient,
|
||||
CachePolicy? policy,
|
||||
super.now,
|
||||
}) : _apiClient = apiClient,
|
||||
super(
|
||||
policy:
|
||||
policy ??
|
||||
const CachePolicy(
|
||||
softTtl: Duration(minutes: 1),
|
||||
hardTtl: Duration(minutes: 10),
|
||||
minRefreshInterval: Duration(seconds: 30),
|
||||
),
|
||||
encodeValue: _encodeEventList,
|
||||
decodeValue: _decodeEventList,
|
||||
);
|
||||
|
||||
static String dayKey(DateTime date) {
|
||||
final day =
|
||||
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
return 'calendar:day:$day';
|
||||
}
|
||||
|
||||
static String monthKey(DateTime date) {
|
||||
return 'calendar:month:${date.year}-${date.month.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getDayEvents(
|
||||
DateTime date, {
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
final key = dayKey(date);
|
||||
final start = DateTime(date.year, date.month, date.day);
|
||||
final end = DateTime(date.year, date.month, date.day, 23, 59, 59);
|
||||
return getOrLoad(
|
||||
key: key,
|
||||
forceRefresh: forceRefresh,
|
||||
loadFromRemote: () => _listByRange(startAt: start, endAt: end),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getMonthEvents(
|
||||
DateTime monthStart, {
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
final key = monthKey(monthStart);
|
||||
final start = DateTime(monthStart.year, monthStart.month, 1);
|
||||
final end = DateTime(monthStart.year, monthStart.month + 1, 0, 23, 59, 59);
|
||||
return getOrLoad(
|
||||
key: key,
|
||||
forceRefresh: forceRefresh,
|
||||
loadFromRemote: () => _listByRange(startAt: start, endAt: end),
|
||||
);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> getEventById(String id) async {
|
||||
final response = await _apiClient.get<Map<String, dynamic>>('$_prefix/$id');
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
throw StateError('Invalid getEventById response: empty payload');
|
||||
}
|
||||
return ScheduleItemModel.fromJson(data);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> getById(String id) {
|
||||
return getEventById(id);
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> listEventsByRange({
|
||||
required DateTime start,
|
||||
required DateTime end,
|
||||
}) {
|
||||
return _listByRange(startAt: start, endAt: end);
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> listByRange({
|
||||
required DateTime startAt,
|
||||
required DateTime endAt,
|
||||
}) {
|
||||
return _listByRange(startAt: startAt, endAt: endAt);
|
||||
}
|
||||
|
||||
Future<void> acceptSubscription(String itemId) {
|
||||
return _apiClient.post<void>('$_prefix/$itemId/accept');
|
||||
}
|
||||
|
||||
Future<void> rejectSubscription(String itemId) {
|
||||
return _apiClient.post<void>('$_prefix/$itemId/reject');
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> _listByRange({
|
||||
required DateTime startAt,
|
||||
required DateTime endAt,
|
||||
}) async {
|
||||
final start = Uri.encodeQueryComponent(startAt.toUtc().toIso8601String());
|
||||
final end = Uri.encodeQueryComponent(endAt.toUtc().toIso8601String());
|
||||
final response = await _apiClient.get<List<dynamic>>(
|
||||
'$_prefix?start_at=$start&end_at=$end',
|
||||
);
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
throw StateError('Invalid listByRange response: empty payload');
|
||||
}
|
||||
return data
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ScheduleItemModel.fromJson)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
static Object? _encodeEventList(List<ScheduleItemModel> events) {
|
||||
return events.map(_encodeEvent).toList(growable: false);
|
||||
}
|
||||
|
||||
static List<ScheduleItemModel> _decodeEventList(Object? raw) {
|
||||
if (raw is! List) {
|
||||
throw const FormatException('Invalid cached calendar event list payload');
|
||||
}
|
||||
return raw
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => ScheduleItemModel.fromJson(Map<String, dynamic>.from(item)),
|
||||
)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
static Map<String, Object?> _encodeEvent(ScheduleItemModel item) {
|
||||
return <String, Object?>{
|
||||
'id': item.id,
|
||||
'owner_id': item.ownerId,
|
||||
'permission': item.permission,
|
||||
'is_owner': item.isOwner,
|
||||
'title': item.title,
|
||||
'description': item.description,
|
||||
'start_at': item.startAt.toIso8601String(),
|
||||
'end_at': item.endAt?.toIso8601String(),
|
||||
'timezone': item.timezone,
|
||||
'metadata': item.metadata?.toJson(),
|
||||
'source_type': _sourceTypeToApi(item.sourceType),
|
||||
'status': _statusToApi(item.status),
|
||||
'created_at': item.createdAt.toIso8601String(),
|
||||
'updated_at': item.updatedAt.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
static String _sourceTypeToApi(ScheduleSourceType sourceType) {
|
||||
switch (sourceType) {
|
||||
case ScheduleSourceType.manual:
|
||||
return 'manual';
|
||||
case ScheduleSourceType.imported:
|
||||
return 'imported';
|
||||
case ScheduleSourceType.agentGenerated:
|
||||
return 'agent_generated';
|
||||
}
|
||||
}
|
||||
|
||||
static String _statusToApi(ScheduleStatus status) {
|
||||
switch (status) {
|
||||
case ScheduleStatus.active:
|
||||
return 'active';
|
||||
case ScheduleStatus.archived:
|
||||
return 'archived';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user