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,15 +15,19 @@ class NotificationApi {
Future<NotificationListResult> listNotifications({
int limit = 20,
String? cursor,
String locale = 'zh',
}) async {
final queryParts = <String>['limit=$limit'];
final queryParameters = <String, Object>{'limit': limit, 'locale': locale};
if (cursor != null) {
queryParts.add('cursor=$cursor');
queryParameters['cursor'] = cursor;
}
final path = '/api/v1/notifications?${queryParts.join("&")}';
try {
final json = await _apiClient.getJson(path);
final response = await _apiClient.rawDio.get<Map<String, dynamic>>(
'/api/v1/notifications',
queryParameters: queryParameters,
);
final json = response.data ?? <String, dynamic>{};
final itemsJson = json['items'] as List<dynamic>? ?? [];
final items = itemsJson
.map((e) => parseNotificationItem(e as Map<String, dynamic>))
@@ -59,21 +63,16 @@ class NotificationApi {
}
}
Future<NotificationItem> markRead({required String notificationId}) async {
_logger.info(
message: 'Mark read request started',
extra: {'notification_id': notificationId},
);
Future<NotificationItem> markRead({
required String notificationId,
String locale = 'zh',
}) async {
try {
final response = await _apiClient.rawDio.patch<Map<String, dynamic>>(
'/api/v1/notifications/$notificationId/read',
queryParameters: {'locale': locale},
);
final item = parseNotificationItem(response.data!);
_logger.info(
message: 'Mark read request succeeded',
extra: {'notification_id': notificationId, 'is_read': item.isRead},
);
return item;
return parseNotificationItem(response.data!);
} on DioException catch (error, stackTrace) {
_logger.error(
message: 'Mark read failed',
@@ -85,17 +84,11 @@ class NotificationApi {
}
Future<int> markAllRead() async {
_logger.info(message: 'Mark all read request started');
try {
final response = await _apiClient.rawDio.patch<Map<String, dynamic>>(
'/api/v1/notifications/mark-all-read',
);
final updatedCount = response.data?['updatedCount'] as int? ?? 0;
_logger.info(
message: 'Mark all read request succeeded',
extra: {'updated_count': updatedCount},
);
return updatedCount;
return response.data?['updatedCount'] as int? ?? 0;
} on DioException catch (error, stackTrace) {
_logger.error(
message: 'Mark all read failed',