refactor(apps): 重构数据层目录结构并新增启动预热编排器

This commit is contained in:
zl-q
2026-03-29 20:26:30 +08:00
parent 33340de8f9
commit 4db9a13bfe
108 changed files with 1653 additions and 1320 deletions
@@ -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';
}
}
}