This commit is contained in:
2026-07-13 01:38:13 +03:00
parent 9eda119527
commit 98c5a7a1d1
54 changed files with 860 additions and 454 deletions

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import '../../domain/entities/client_notification.dart';
class ClientNotificationDto {
@@ -43,12 +42,19 @@ class ClientNotificationDto {
clientId: clientId,
type: _parseType(type),
category: _parseCategory(category),
createdAt: DateTime.parse(createdAt),
canceledAt: canceledAt != null ? DateTime.parse(canceledAt!) : null,
readAt: readAt != null ? DateTime.parse(readAt!) : null,
createdAt: _parseUtcDate(createdAt),
canceledAt: canceledAt != null ? _parseUtcDate(canceledAt!) : null,
readAt: readAt != null ? _parseUtcDate(readAt!) : null,
);
}
DateTime _parseUtcDate(String dateString) {
if (dateString.endsWith('Z') || dateString.contains('+') || dateString.contains('-')) {
return DateTime.parse(dateString).toLocal();
}
return DateTime.parse('${dateString}Z').toLocal();
}
NotificationType _parseType(String type) {
switch (type.toLowerCase()) {
case 'info':
@@ -80,4 +86,4 @@ class ClientNotificationDto {
return NotificationCategory.companyInfo;
}
}
}
}

View File

@@ -147,11 +147,12 @@ class ApiService {
}
dynamic balanceRaw = profileData["balance"];
int? balance;
if (balanceRaw is int) {
balance = balanceRaw;
double? balance;
if (balanceRaw is num) {
balance = balanceRaw.toDouble();
} else if (balanceRaw is String) {
balance = int.tryParse(balanceRaw);
balance = double.tryParse(balanceRaw);
}
return UserProfile(
@@ -238,11 +239,11 @@ class ApiService {
}
dynamic balanceRaw = data["balance"];
int? balance;
if (balanceRaw is int) {
double? balance;
if (balanceRaw is double) {
balance = balanceRaw;
} else if (balanceRaw is String) {
balance = int.tryParse(balanceRaw);
balance = double.tryParse(balanceRaw);
}
return UserProfile(
@@ -420,7 +421,7 @@ class ApiService {
int page = 1,
int pageSize = 500,
}) async {
const path = "$baseUrl/zone/available";
const path = "$baseUrl/zone/available2";
final queryParams = {
'readAll': 'true',
@@ -708,17 +709,13 @@ class ApiService {
}
}
Future<ScooterOrder?> cancelRide(int orderId) async {
Future<void> cancelRide(int orderId) async {
try {
final response = await _dio.put(
"$baseUrl/scooterorder/$orderId/cancel",
options: await _getAuthOptions(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return ScooterOrder.fromJson(response.data);
}
return null;
} on DioException catch (e) {
_handleDioError(e);
return null;
@@ -759,7 +756,7 @@ class ApiService {
}
}
Future<ScooterOrder?> finishRide({
Future<void> finishRide({
required int orderId,
required List<int> filesId,
}) async {
@@ -770,10 +767,6 @@ class ApiService {
options: await _getAuthOptions(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return ScooterOrder.fromJson(response.data);
}
return null;
} on DioException catch (e) {
if (e.response?.statusCode == 400) {
final data = e.response?.data;
@@ -1013,10 +1006,10 @@ class ApiService {
if (event.data != null && event.data!.isNotEmpty) {
try {
final Map<String, dynamic> jsonData = jsonDecode(event.data!);
print("✅ [SSE] Parsed JSON: $jsonData");
print("[SSE] Parsed JSON: $jsonData");
controller.add(jsonData);
} catch (e) {
print("❌ [SSE] JSON Parse Error: $e | Raw: ${event.data}");
print("[SSE] JSON Parse Error: $e | Raw: ${event.data}");
}
}
},
@@ -1034,17 +1027,19 @@ class ApiService {
return controller.stream;
}
Future<List<Map<String, dynamic>>> getNotifications() async {
final url = Uri.parse('$baseUrl/notification/client');
Future<Map<String, dynamic>> getNotifications({
int page = 1,
int pageSize = 10,
}) async {
final url = Uri.parse('$baseUrl/notification/client').replace(
queryParameters: {
'page': page.toString(),
'pageSize': pageSize.toString(),
},
);
final accessToken = await _securityService.getAccessToken();
if (accessToken == null) {
print("APISERVICE Error: Access token is null.");
throw UnauthorizedException();
}
print("GET NOTIFICATIONS REQUEST:");
print("URL: $url");
if (accessToken == null) throw UnauthorizedException();
final response = await http.get(
url,
@@ -1054,34 +1049,17 @@ class ApiService {
},
);
print("GET NOTIFICATIONS RESPONSE:");
print("STATUS: ${response.statusCode}");
print("BODY: ${response.body}");
if (response.statusCode == 200) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
// ✅ Проверяем, является ли ответ массивом или объектом с data[]
if (data is List) {
return data.cast<Map<String, dynamic>>();
} else if (data is Map<String, dynamic>) {
final list = data['data'];
if (list is List) {
return list.cast<Map<String, dynamic>>();
} else {
throw Exception(
'Expected a List under "data" but got ${list.runtimeType}',
);
}
} else {
throw Exception('Expected a List or Map but got ${data.runtimeType}');
if (data is Map<String, dynamic>) {
return data;
}
throw Exception('Expected Map with data and pagination');
} else if (response.statusCode == 401) {
throw UnauthorizedException();
} else if (response.statusCode == 403) {
throw AuthBlockException();
}
throw Exception('Ошибка сервера: ${response.statusCode}');
}

View File

@@ -1,7 +1,9 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import '../../data/network/api_service.dart';
import '../../domain/entities/pagination_meta.dart';
import '../../domain/repositories/notification_repository.dart';
import '../../domain/usecase/get_notifications_usecase.dart';
import '../models/client_notification_dto.dart';
class NotificationRepositoryImpl implements NotificationRepository {
@@ -35,19 +37,27 @@ class NotificationRepositoryImpl implements NotificationRepository {
}
@override
Future<List<ClientNotification>> getNotifications() async {
Future<NotificationsResult> getNotifications({
int page = 1,
int pageSize = 10,
}) async {
try {
final List<Map<String, dynamic>> data = await _apiService.getNotifications();
final notifications = data.map((json) {
final dto = ClientNotificationDto.fromJson(json);
return dto.toEntity();
}).toList();
final response = await _apiService.getNotifications(
page: page,
pageSize: pageSize,
);
// dev.log('NotificationRepository: Загружено ${notifications.length} уведомлений');
final notifications = (response['data'] as List)
.map((json) => ClientNotificationDto.fromJson(json).toEntity())
.toList();
return notifications;
final pagination = PaginationMeta.fromJson(response['pagination']);
return NotificationsResult(
notifications: notifications,
pagination: pagination,
);
} catch (e, stackTrace) {
// dev.log('NotificationRepository: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить уведомления: $e');
}
}

View File

@@ -192,15 +192,11 @@ class ScooterRepositoryImpl extends ScooterRepository {
}
@override
Future<Result<ScooterOrder>> cancelRide(int orderId) async {
late final Result<ScooterOrder> result;
Future<Result<void>> cancelRide(int orderId) async {
late final Result<void> result;
try {
final order = await _apiService.cancelRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
await _apiService.cancelRide(orderId);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
@@ -246,18 +242,11 @@ class ScooterRepositoryImpl extends ScooterRepository {
}
@override
Future<Result<ScooterOrder>> finishRide(int orderId, List<int> files) async {
Future<Result<void>> finishRide(int orderId, List<int> files) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.finishRide(
orderId: orderId,
filesId: files,
);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
await _apiService.finishRide(orderId: orderId, filesId: files);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on WrongZoneException catch (e) {
@@ -274,7 +263,6 @@ class ScooterRepositoryImpl extends ScooterRepository {
try {
await _apiService.payRide(orderId);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
@@ -345,7 +333,7 @@ class ScooterRepositoryImpl extends ScooterRepository {
cardId: cardId,
isBalance: isBalance,
);
result = Success(null);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {