feat(notification): 通知标题和正文支持多语言

- 通知静态配置支持 title/body i18n
- 前端通知列表和详情页展示本地化内容
- 新增数据库迁移脚本
- 更新通知协议文档
This commit is contained in:
ZL-Q
2026-04-28 17:20:17 +08:00
parent b9617ae152
commit a940f2ea47
16 changed files with 601 additions and 213 deletions
@@ -15,6 +15,7 @@ class NotificationState {
this.unreadCount = 0,
this.hasMore = false,
this.nextCursor,
this.isLoadingMore = false,
this.errorMessage,
});
@@ -23,6 +24,7 @@ class NotificationState {
final int unreadCount;
final bool hasMore;
final String? nextCursor;
final bool isLoadingMore;
final String? errorMessage;
NotificationState copyWith({
@@ -31,6 +33,7 @@ class NotificationState {
int? unreadCount,
bool? hasMore,
String? nextCursor,
bool? isLoadingMore,
String? errorMessage,
}) {
return NotificationState(
@@ -39,6 +42,7 @@ class NotificationState {
unreadCount: unreadCount ?? this.unreadCount,
hasMore: hasMore ?? this.hasMore,
nextCursor: nextCursor ?? this.nextCursor,
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@@ -81,10 +85,13 @@ final class NotificationRevokedEvent extends NotificationEvent {
}
class NotificationBloc extends ChangeNotifier {
NotificationBloc({required NotificationRepository repository})
: _repository = repository;
NotificationBloc({
required NotificationRepository repository,
this.locale = 'zh',
}) : _repository = repository;
final NotificationRepository _repository;
final String locale;
final Logger _logger = getLogger('features.notifications.bloc');
NotificationState _state = const NotificationState();
@@ -119,7 +126,10 @@ class NotificationBloc extends ChangeNotifier {
notifyListeners();
try {
final result = await _repository.listNotifications(limit: 20);
final result = await _repository.listNotifications(
limit: 20,
locale: locale,
);
_state = _state.copyWith(
status: NotificationStatus.loaded,
items: result.items,
@@ -143,7 +153,10 @@ class NotificationBloc extends ChangeNotifier {
Future<void> _refreshNotifications() async {
try {
final result = await _repository.listNotifications(limit: 20);
final result = await _repository.listNotifications(
limit: 20,
locale: locale,
);
_state = _state.copyWith(
status: NotificationStatus.loaded,
items: result.items,
@@ -161,18 +174,25 @@ class NotificationBloc extends ChangeNotifier {
}
Future<void> _loadMore() async {
if (!_state.hasMore || _state.nextCursor == null) return;
if (_state.isLoadingMore || !_state.hasMore || _state.nextCursor == null) {
return;
}
_state = _state.copyWith(isLoadingMore: true);
notifyListeners();
try {
final result = await _repository.listNotifications(
limit: 20,
cursor: _state.nextCursor,
locale: locale,
);
final allItems = [..._state.items, ...result.items];
_state = _state.copyWith(
items: allItems,
hasMore: result.hasMore,
nextCursor: result.nextCursor,
isLoadingMore: false,
);
notifyListeners();
} catch (error, stackTrace) {
@@ -181,6 +201,8 @@ class NotificationBloc extends ChangeNotifier {
error: error,
stackTrace: stackTrace,
);
_state = _state.copyWith(isLoadingMore: false);
notifyListeners();
}
}
@@ -197,6 +219,7 @@ class NotificationBloc extends ChangeNotifier {
try {
final updated = await _repository.markRead(
notificationId: notificationId,
locale: locale,
);
final targetIndex = _state.items.indexWhere(
(item) => item.id == updated.id,