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 InboxApi {
|
||||
final IApiClient _client;
|
||||
@@ -0,0 +1,74 @@
|
||||
enum InboxMessageType { friendRequest, calendar, system, group }
|
||||
|
||||
enum InboxMessageStatus { pending, accepted, rejected, dismissed }
|
||||
|
||||
class InboxMessage {
|
||||
final String id;
|
||||
final String recipientId;
|
||||
final String? senderId;
|
||||
final InboxMessageType messageType;
|
||||
final String? scheduleItemId;
|
||||
final String? friendshipId;
|
||||
final Map<String, dynamic>? content;
|
||||
final bool isRead;
|
||||
final InboxMessageStatus status;
|
||||
final DateTime createdAt;
|
||||
|
||||
const InboxMessage({
|
||||
required this.id,
|
||||
required this.recipientId,
|
||||
required this.senderId,
|
||||
required this.messageType,
|
||||
required this.scheduleItemId,
|
||||
required this.friendshipId,
|
||||
required this.content,
|
||||
required this.isRead,
|
||||
required this.status,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory InboxMessage.fromJson(Map<String, dynamic> json) {
|
||||
return InboxMessage(
|
||||
id: json['id'] as String,
|
||||
recipientId: json['recipient_id'] as String,
|
||||
senderId: json['sender_id'] as String?,
|
||||
messageType: _messageTypeFromApi(json['message_type'] as String),
|
||||
scheduleItemId: json['schedule_item_id'] as String?,
|
||||
friendshipId: json['friendship_id'] as String?,
|
||||
content: json['content'] as Map<String, dynamic>?,
|
||||
isRead: json['is_read'] as bool,
|
||||
status: _messageStatusFromApi(json['status'] as String),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InboxMessageType _messageTypeFromApi(String raw) {
|
||||
switch (raw) {
|
||||
case 'friend_request':
|
||||
return InboxMessageType.friendRequest;
|
||||
case 'calendar':
|
||||
return InboxMessageType.calendar;
|
||||
case 'system':
|
||||
return InboxMessageType.system;
|
||||
case 'group':
|
||||
return InboxMessageType.group;
|
||||
default:
|
||||
throw StateError('Unsupported inbox message type: $raw');
|
||||
}
|
||||
}
|
||||
|
||||
InboxMessageStatus _messageStatusFromApi(String raw) {
|
||||
switch (raw) {
|
||||
case 'pending':
|
||||
return InboxMessageStatus.pending;
|
||||
case 'accepted':
|
||||
return InboxMessageStatus.accepted;
|
||||
case 'rejected':
|
||||
return InboxMessageStatus.rejected;
|
||||
case 'dismissed':
|
||||
return InboxMessageStatus.dismissed;
|
||||
default:
|
||||
throw StateError('Unsupported inbox message status: $raw');
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@ 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/inbox_message.dart';
|
||||
import '../../../../data/repositories/inbox_repository.dart';
|
||||
import '../../../../data/repositories/user_repository.dart';
|
||||
import '../../../../features/calendar/data/repositories/calendar_repository.dart';
|
||||
import '../../../../features/messages/data/models/inbox_message.dart';
|
||||
import '../../../../features/messages/data/repositories/inbox_repository.dart';
|
||||
import '../../../../features/contacts/data/repositories/user_repository.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/page_header.dart';
|
||||
@@ -25,7 +25,7 @@ class MessageInviteDetailScreen extends StatefulWidget {
|
||||
|
||||
class _MessageInviteDetailScreenState extends State<MessageInviteDetailScreen> {
|
||||
late final InboxRepository _inboxRepository;
|
||||
late final CalendarEventRepository _calendarRepository;
|
||||
late final CalendarRepository _calendarRepository;
|
||||
late final UserRepository _userRepository;
|
||||
|
||||
InboxMessage? _message;
|
||||
@@ -43,7 +43,7 @@ class _MessageInviteDetailScreenState extends State<MessageInviteDetailScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_inboxRepository = sl<InboxRepository>();
|
||||
_calendarRepository = sl<CalendarEventRepository>();
|
||||
_calendarRepository = sl<CalendarRepository>();
|
||||
_userRepository = sl<UserRepository>();
|
||||
_loadDetail();
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../app/router/app_routes.dart';
|
||||
import '../../../../data/repositories/friend_repository.dart';
|
||||
import '../../../../data/repositories/inbox_repository.dart';
|
||||
import '../../../../data/repositories/models/friend_request.dart';
|
||||
import '../../../../data/repositories/models/inbox_message.dart';
|
||||
import '../../../../features/contacts/data/repositories/friend_repository.dart';
|
||||
import '../../../../features/messages/data/repositories/inbox_repository.dart';
|
||||
import '../../../../features/contacts/data/models/friend_request.dart';
|
||||
import '../../../../features/messages/data/models/inbox_message.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/app_pull_refresh_feedback.dart';
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:social_app/core/l10n/l10n.dart';
|
||||
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../data/inbox_api.dart';
|
||||
import '../../data/apis/inbox_api.dart';
|
||||
|
||||
class CalendarInviteCard extends StatelessWidget {
|
||||
final InboxMessageResponse message;
|
||||
|
||||
Reference in New Issue
Block a user