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,130 @@
import '../../../../data/network/i_api_client.dart';
import '../../../../data/cache/cache_policy.dart';
import '../../../../data/cache/cached_repository.dart';
import '../models/inbox_message.dart';
abstract class InboxRepository {
Future<List<InboxMessage>> getMessages({bool? isRead});
Future<InboxMessage> markAsRead(String messageId);
}
class InboxRepositoryImpl extends CachedRepository<List<InboxMessage>>
implements InboxRepository {
final IApiClient _apiClient;
static const _prefix = '/api/v1/inbox/messages';
InboxRepositoryImpl({required IApiClient apiClient, required super.store})
: _apiClient = apiClient,
super(
policy: const CachePolicy(
softTtl: Duration(seconds: 15),
hardTtl: Duration(minutes: 2),
minRefreshInterval: Duration(seconds: 10),
),
encodeValue: _encodeMessages,
decodeValue: _decodeMessages,
);
@override
Future<List<InboxMessage>> getMessages({bool? isRead}) async {
return getOrLoad(
key: _messagesKey(isRead),
loadFromRemote: () => _loadMessagesFromRemote(isRead: isRead),
);
}
Future<List<InboxMessage>> _loadMessagesFromRemote({bool? isRead}) async {
final queryParams = isRead != null ? '?is_read=$isRead' : '';
final response = await _apiClient.get<List<dynamic>>(
'$_prefix$queryParams',
);
final data = response.data;
if (data == null) {
throw StateError('Invalid getMessages response: empty payload');
}
return data
.whereType<Map<String, dynamic>>()
.map(InboxMessage.fromJson)
.toList(growable: false);
}
@override
Future<InboxMessage> markAsRead(String messageId) async {
final response = await _apiClient.patch<Map<String, dynamic>>(
'$_prefix/$messageId/read',
);
final data = response.data;
if (data == null) {
throw StateError('Invalid markAsRead response: empty payload');
}
final message = InboxMessage.fromJson(data);
await Future.wait([
removeCacheKey(_messagesKey(false)),
removeCacheKey(_messagesKey(true)),
removeCacheKey(_messagesKey(null)),
]);
return message;
}
static String _messagesKey(bool? isRead) {
if (isRead == null) {
return 'inbox:list:all';
}
return isRead ? 'inbox:list:read' : 'inbox:list:unread';
}
static Object? _encodeMessages(List<InboxMessage> messages) {
return messages.map(_encodeMessage).toList(growable: false);
}
static List<InboxMessage> _decodeMessages(Object? raw) {
if (raw is! List) {
throw const FormatException('Invalid cached inbox message payload');
}
return raw
.whereType<Map>()
.map((item) => InboxMessage.fromJson(Map<String, dynamic>.from(item)))
.toList(growable: false);
}
static Map<String, Object?> _encodeMessage(InboxMessage message) {
return <String, Object?>{
'id': message.id,
'recipient_id': message.recipientId,
'sender_id': message.senderId,
'message_type': _messageTypeToApi(message.messageType),
'schedule_item_id': message.scheduleItemId,
'friendship_id': message.friendshipId,
'content': message.content,
'is_read': message.isRead,
'status': _messageStatusToApi(message.status),
'created_at': message.createdAt.toIso8601String(),
};
}
static String _messageTypeToApi(InboxMessageType type) {
switch (type) {
case InboxMessageType.friendRequest:
return 'friend_request';
case InboxMessageType.calendar:
return 'calendar';
case InboxMessageType.system:
return 'system';
case InboxMessageType.group:
return 'group';
}
}
static String _messageStatusToApi(InboxMessageStatus status) {
switch (status) {
case InboxMessageStatus.pending:
return 'pending';
case InboxMessageStatus.accepted:
return 'accepted';
case InboxMessageStatus.rejected:
return 'rejected';
case InboxMessageStatus.dismissed:
return 'dismissed';
}
}
}