import 'dart:convert'; import 'package:http/http.dart' as http; class AppNotification { const AppNotification({ required this.id, required this.type, required this.title, required this.body, required this.createdAt, this.resourceType, this.resourceId, this.readAt, }); final String id; final String type; final String title; final String body; final String? resourceType; final String? resourceId; final DateTime createdAt; final DateTime? readAt; bool get isRead => readAt != null; factory AppNotification.fromJson(Map json) => AppNotification( id: json['id']! as String, type: json['type']! as String, title: json['title']! as String, body: json['body']! as String, resourceType: json['resourceType'] as String?, resourceId: json['resourceId'] as String?, createdAt: DateTime.parse(json['createdAt']! as String), readAt: json['readAt'] == null ? null : DateTime.parse(json['readAt']! as String), ); } class NotificationRepository { NotificationRepository({ http.Client? client, this.baseUrl = const String.fromEnvironment( 'AIOA_API_BASE_URL', defaultValue: 'http://127.0.0.1:8080/api/v1', ), this.accessToken = const String.fromEnvironment('AIOA_ACCESS_TOKEN'), }) : _client = client ?? http.Client(); final http.Client _client; final String baseUrl; final String accessToken; Future> list() async { final response = await _client.get( Uri.parse('$baseUrl/notifications'), headers: _headers, ); _requireSuccess(response); return (jsonDecode(response.body) as List) .map( (item) => AppNotification.fromJson(Map.from(item as Map)), ) .toList(); } Future markRead(String id) async { final response = await _client.post( Uri.parse('$baseUrl/notifications/$id/read'), headers: _headers, ); _requireSuccess(response); return AppNotification.fromJson( jsonDecode(response.body) as Map, ); } Map get _headers => { 'Accept': 'application/json', if (accessToken.isNotEmpty) 'Authorization': 'Bearer $accessToken', }; void _requireSuccess(http.Response response) { if (response.statusCode >= 200 && response.statusCode < 300) return; throw Exception('通知请求失败(${response.statusCode})'); } }