new project stable version

This commit is contained in:
2026-05-10 19:11:31 +03:00
commit 3616f84556
391 changed files with 23857 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
class AuthBlockException implements Exception {
String description = "Неверный код. Вы временно заблокированы, попробуйте позже.";
AuthBlockException();
@override
String toString() => description;
}

View File

@@ -0,0 +1,9 @@
class AuthException implements Exception {
int attemptsLeft;
String? description;
AuthException(this.description, this.attemptsLeft);
@override
String toString() => description ?? "Ошибка авторизации";
}

View File

@@ -0,0 +1,8 @@
class RouteHistoryNotFoundException implements Exception {
final String message;
RouteHistoryNotFoundException({this.message = "История маршрута не найдена"});
@override
String toString() => message;
}

View File

@@ -0,0 +1,8 @@
class ScooterNotFoundException implements Exception {
final String message;
ScooterNotFoundException({this.message = "Самокат не найден"});
@override
String toString() => message;
}

View File

@@ -0,0 +1,5 @@
class UnauthorizedException implements Exception {
@override
String toString() => "Ошибка авторизации. Пользователь не авторизован";
}

View File

@@ -0,0 +1,6 @@
class WrongZoneException implements Exception {
final String message;
WrongZoneException({required this.message});
}

View File

@@ -0,0 +1,13 @@
class AuthResponseDto {
final String accessToken;
final String refreshToken;
AuthResponseDto({required this.accessToken, required this.refreshToken});
factory AuthResponseDto.fromJson(Map<String, dynamic> json) {
return AuthResponseDto(
accessToken: json["accessToken"] ?? "",
refreshToken: json["refreshToken"] ?? "",
);
}
}

View File

@@ -0,0 +1,83 @@
import 'dart:convert';
import '../../domain/entities/client_notification.dart';
class ClientNotificationDto {
final int id;
final String content;
final int clientId;
final String type;
final String category;
final String createdAt;
final String? canceledAt;
final String? readAt;
ClientNotificationDto({
required this.id,
required this.content,
required this.clientId,
required this.type,
required this.category,
required this.createdAt,
this.canceledAt,
this.readAt,
});
factory ClientNotificationDto.fromJson(Map<String, dynamic> json) {
return ClientNotificationDto(
id: json['id'] as int,
content: json['content'] as String,
clientId: json['clientId'] as int,
type: json['type'] as String,
category: json['category'] as String,
createdAt: json['createdAt'] as String,
canceledAt: json['canceledAt'] as String?,
readAt: json['readAt'] as String?,
);
}
ClientNotification toEntity() {
return ClientNotification(
id: id,
content: content,
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,
);
}
NotificationType _parseType(String type) {
switch (type.toLowerCase()) {
case 'info':
return NotificationType.info;
case 'attention':
return NotificationType.attention;
case 'warning':
return NotificationType.warning;
default:
return NotificationType.info;
}
}
NotificationCategory _parseCategory(String category) {
switch (category.toLowerCase()) {
case 'auth':
return NotificationCategory.auth;
case 'zone':
return NotificationCategory.zone;
case 'payment':
return NotificationCategory.payment;
case 'companyinfo':
return NotificationCategory.companyInfo;
case 'admininfo':
return NotificationCategory.adminInfo;
case 'scooter':
return NotificationCategory.scooter;
default:
return NotificationCategory.companyInfo;
}
}
}

View File

@@ -0,0 +1,9 @@
class LoginRequestDto {
final String phone;
LoginRequestDto({required this.phone});
Map<String, dynamic> toJson() {
return {"phone": phone};
}
}

View File

@@ -0,0 +1,25 @@
class PaymentCardRequestDto {
final String cardNumber;
final String cardHolder;
final int expirationMonth;
final int expirationYear;
final String cvv;
PaymentCardRequestDto({
required this.cardNumber,
required this.cardHolder,
required this.expirationMonth,
required this.expirationYear,
required this.cvv,
});
Map<String, dynamic> toJson() {
return {
'cardNumber': cardNumber.replaceAll(' ', ''),
'cardHolder': cardHolder,
'expirationMonth': expirationMonth,
'expirationYear': expirationYear,
'cvv': cvv,
};
}
}

View File

@@ -0,0 +1,49 @@
import '../../domain/entities/payment_card.dart';
class PaymentCardResponseDto {
final int id;
final int clientId;
final int expirationMonth;
final int expirationYear;
final String cardHolder;
final String cardLastNumber;
final String type;
final bool isMain;
PaymentCardResponseDto({
required this.id,
required this.clientId,
required this.expirationMonth,
required this.expirationYear,
required this.cardHolder,
required this.cardLastNumber,
required this.type,
required this.isMain,
});
factory PaymentCardResponseDto.fromJson(Map<String, dynamic> json) {
return PaymentCardResponseDto(
id: json['id'] as int,
clientId: json['clientId'] as int,
expirationMonth: json['expirationMonth'] as int,
expirationYear: json['expirationYear'] as int,
cardHolder: json['cardHolder'] as String,
cardLastNumber: json['cardLastNumber'] as String,
isMain: json['isMain'] as bool,
type: json['type'] as String,
);
}
PaymentCard toEntity(String? fullCardNumber) {
return PaymentCard(
id: id,
clientId: clientId,
expirationMonth: expirationMonth,
expirationYear: expirationYear,
cardHolder: cardHolder,
cardLastNumber: cardLastNumber,
isMain: isMain,
type: type,
);
}
}

View File

@@ -0,0 +1,21 @@
import '../../domain/entities/scooter_order.dart';
import '../../domain/entities/pagination.dart';
class ScooterOrderHistoryResponse {
final List<ScooterOrder> orders;
final Pagination pagination;
ScooterOrderHistoryResponse({
required this.orders,
required this.pagination,
});
factory ScooterOrderHistoryResponse.fromJson(Map<String, dynamic> json) {
return ScooterOrderHistoryResponse(
orders: (json['data'] as List<dynamic>)
.map((e) => ScooterOrder.fromJson(e as Map<String, dynamic>))
.toList(),
pagination: Pagination.fromJson(json['pagination']),
);
}
}

View File

@@ -0,0 +1,21 @@
import '../../domain/entities/pagination.dart';
import '../../domain/entities/scooter.dart';
class ScootersResponse {
final List<Scooter> scooters;
final Pagination pagination;
ScootersResponse({
required this.scooters,
required this.pagination,
});
factory ScootersResponse.fromJson(Map<String, dynamic> json) {
return ScootersResponse(
scooters: (json['data'] as List<dynamic>)
.map((e) => Scooter.fromJson(e as Map<String, dynamic>))
.toList(),
pagination: Pagination.fromJson(json['pagination']),
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/domain/entities/subscription.dart';
class SubscriptionsResponse {
final List<Subscription> subscriptions;
SubscriptionsResponse({required this.subscriptions});
factory SubscriptionsResponse.fromJson(Map<String, dynamic> json) {
return SubscriptionsResponse(
subscriptions: (json['data'] as List<dynamic>)
.map((e) => Subscription.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/domain/entities/tariff.dart';
class TariffsResponse {
final List<Tariff> tariffs;
TariffsResponse({required this.tariffs});
factory TariffsResponse.fromJson(Map<String, dynamic> json) {
return TariffsResponse(
tariffs: (json['data'] as List<dynamic>)
.map((e) => Tariff.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
}

View File

@@ -0,0 +1,22 @@
class UserCheckResponseDto {
final bool success;
final bool hasFine;
final bool hasUnpaidOrder;
final bool hasCard;
UserCheckResponseDto({
required this.success,
required this.hasFine,
required this.hasUnpaidOrder,
required this.hasCard,
});
factory UserCheckResponseDto.fromJson(Map<String, dynamic> json) {
return UserCheckResponseDto(
success: json['success'] ?? false,
hasFine: json['hasFine'] ?? false,
hasUnpaidOrder: json['hasUnpaidOrder'] ?? false,
hasCard: json['hasCard'] ?? false,
);
}
}

View File

@@ -0,0 +1,10 @@
class VerifyCodeRequestDto {
final String code;
final String token;
VerifyCodeRequestDto({required this.code, required this.token});
Map<String, dynamic> toJson() {
return {"code": code, "token": token};
}
}

View File

@@ -0,0 +1,22 @@
import '../../domain/entities/pagination.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/zone.dart';
class ZonesResponse {
final List<Zone> zones;
final Pagination pagination;
ZonesResponse({
required this.zones,
required this.pagination,
});
factory ZonesResponse.fromJson(Map<String, dynamic> json) {
return ZonesResponse(
zones: (json['data'] as List<dynamic>)
.map((e) => Zone.fromJson(e as Map<String, dynamic>))
.toList(),
pagination: Pagination.fromJson(json['pagination']),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
import 'dart:async';
import 'package:yandex_mapkit/yandex_mapkit.dart';
class GeocodingRemoteDataSource {
Future<String> getAddressFromPoint({
required double latitude,
required double longitude,
}) async {
final point = Point(
latitude: latitude,
longitude: longitude,
);
final (session, resultFuture) = await YandexSearch.searchByPoint(
point: point,
zoom: 16,
searchOptions: const SearchOptions(
searchType: SearchType.geo,
resultPageSize: 1,
),
);
try {
final result = await resultFuture;
if (result.items == null || result.items!.isEmpty) {
throw Exception("Адрес не найден");
}
final item = result.items!.first;
print("ADDRESS FETCH RESULT ${item.name}");
final toponymAddress =
item.toponymMetadata?.address?.formattedAddress;
if (toponymAddress != null && toponymAddress.isNotEmpty) {
return toponymAddress;
}
final businessAddress =
item.businessMetadata?.address?.formattedAddress;
if (businessAddress != null && businessAddress.isNotEmpty) {
return businessAddress;
}
return item.name;
} catch (e) {
throw Exception("Ошибка получения адреса: $e");
} finally {
await session.close();
}
}
Future<List<MasstransitRoute>?> getPedestrianRoutes(Point userPosition,
Point targetPosition) async {
final (session, resultFuture) = await YandexPedestrian.requestRoutes(
points: [
RequestPoint(
point: userPosition, requestPointType: RequestPointType.wayPoint),
RequestPoint(point: targetPosition,
requestPointType: RequestPointType.wayPoint)
],
fitnessOptions: FitnessOptions(avoidSteep: false, avoidStairs: false),
timeOptions: TimeOptions()
);
try {
final result = await resultFuture;
final distance = result.routes?.first.metadata.weight.walkingDistance.value;
print("Дистанция до самоката: $distance");
return result.routes;
} catch (e) {
print('Error: $e');
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
import 'package:be_happy/data/service/app_setting_service.dart';
import 'package:be_happy/domain/entities/map_settings.dart';
import 'package:be_happy/domain/repositories/app_settings_repository.dart';
class AppSettingsRepositoryImpl extends AppSettingsRepository {
static const String SHOW_ALL_PLACEMARKS = "all_placemarks";
static const String SHOW_ALL_ZONES = "all_zones";
static const String SHOW_PARKING_ZONES = "parking_zones";
static const String SHOW_RESTRICTED_PARKING_ZONES = "restricted_parking_zones";
static const String SHOW_RESTRICTED_DRIVING_ZONES = "restricted_driving_zones";
final AppSettingsService appSettingsService;
AppSettingsRepositoryImpl(this.appSettingsService);
@override
Future<MapSettings> getMapSettings() async {
MapSettings settings = MapSettings(
all_placemarks: appSettingsService.getMapSettingsFlag(
SHOW_ALL_PLACEMARKS),
all_zones: appSettingsService.getMapSettingsFlag(
SHOW_ALL_ZONES),
parking_zones: appSettingsService.getMapSettingsFlag(
SHOW_PARKING_ZONES),
restricted_parking_zones: appSettingsService.getMapSettingsFlag(
SHOW_RESTRICTED_PARKING_ZONES),
restricted_driving_zones: appSettingsService.getMapSettingsFlag(
SHOW_RESTRICTED_DRIVING_ZONES)
);
return settings;
}
@override
Future<void> saveMapSettings(MapSettings settings) async {
appSettingsService.saveMapSettingsFlag(SHOW_ALL_PLACEMARKS, settings.all_placemarks);
appSettingsService.saveMapSettingsFlag(SHOW_ALL_ZONES, settings.all_zones);
appSettingsService.saveMapSettingsFlag(SHOW_PARKING_ZONES, settings.parking_zones);
appSettingsService.saveMapSettingsFlag(SHOW_RESTRICTED_PARKING_ZONES, settings.restricted_parking_zones);
appSettingsService.saveMapSettingsFlag(SHOW_RESTRICTED_DRIVING_ZONES, settings.restricted_driving_zones);
}
}

View File

@@ -0,0 +1,85 @@
import 'package:be_happy/core/failures.dart';
import 'package:be_happy/core/result.dart';
import 'package:be_happy/data/exceptions/auth_block_exception.dart';
import 'package:be_happy/data/exceptions/auth_exception.dart';
import 'package:be_happy/data/repositories/pin_repository_impl.dart';
import 'package:be_happy/domain/service/device_info_service.dart';
import 'package:be_happy/domain/service/security_service.dart';
import '../../domain/repositories/auth_repository.dart';
import '../../domain/entities/user_auth_data.dart';
import '../network/api_service.dart';
class AuthRepositoryImpl implements AuthRepository {
final ApiService _apiService;
final DeviceInfoService _deviceInfoService;
final SecurityService _securityService;
String tempToken = "";
AuthRepositoryImpl(
this._apiService,
this._deviceInfoService,
this._securityService,
);
@override
Future<String> login(String phone) async {
final systemId = await _deviceInfoService.getSystemId() ?? "UnknownId";
final deviceModel = await _deviceInfoService.getDeviceModel();
final response = await _apiService.sendPhone(phone, deviceModel, systemId);
if (response != null) {
tempToken = response;
print("TEMP TOKEN IS $tempToken");
return response;
} else {
throw Exception("Login failed");
}
}
@override
Future<Result<void>> verifyCode(String code, String token) async {
late final Result<UserAuthData> result;
try {
final response = await _apiService.verifyCode(code, tempToken);
if (response != null) {
final authData = UserAuthData(
accessToken: response["accessToken"]!,
refreshToken: response["refreshToken"]!,
);
await _securityService.saveTokens(authData);
result = Success(null);
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException {
result = Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<UserAuthData> refreshToken() async {
final response = await _apiService.refresh();
print("REFRESH: $response");
if (response != null) {
final authData = UserAuthData(
accessToken: response["accessToken"]!,
refreshToken: response["refreshToken"]!,
);
await _securityService.saveTokens(authData);
return authData;
} else {
throw Exception("Refresh token failed");
}
}
@override
Future<void> logout() async {
await _securityService.removeTokens();
}
}

View File

@@ -0,0 +1,59 @@
import '../../core/failures.dart';
import '../../core/result.dart';
import '../../domain/entities/certificate.dart';
import '../../domain/repositories/certificate_repository.dart';
import '../../domain/service/security_service.dart';
import '../network/api_service.dart';
import '../exceptions/auth_exception.dart';
import '../exceptions/auth_block_exception.dart';
import '../exceptions/unauthorized_exception.dart';
class CertificateRepositoryImpl implements CertificateRepository {
final ApiService apiService;
final SecurityService securityService;
CertificateRepositoryImpl(this.apiService, this.securityService);
@override
Future<Result<List<Certificate>>> getCertificates() async {
try {
final certificates = await apiService.getCertificates();
return Success(certificates);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
@override
Future<Result<Map<String, dynamic>>> purchaseCertificate({
required int certificateId,
required int cardId,
}) async {
try {
final result = await apiService.purchaseCertificate(
certificateId: certificateId,
cardId: cardId,
);
if (result != null) {
return Success(result);
} else {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
}

View File

@@ -0,0 +1,46 @@
import '../../domain/entities/news.dart';
import '../../domain/repositories/news_repository.dart';
import '../service/news_api_service.dart';
import 'dart:developer' as dev;
class NewsRepositoryImpl implements NewsRepository {
final NewsApiService _apiService;
NewsRepositoryImpl(this._apiService);
@override
Future<List<NewsEntity>> getNews() async {
try {
dev.log('NewsRepository: Загрузка новостей...');
final response = await _apiService.getNews();
final List<dynamic> data = response['data'] ?? [];
final newsList = data.map((json) => NewsEntity.fromJson(json)).toList();
dev.log('NewsRepository: Загружено ${newsList.length} новостей');
return newsList;
} catch (e, stackTrace) {
dev.log('NewsRepository: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить новости: $e');
}
}
@override
Future<NewsEntity> getNewsById(int id) async {
try {
dev.log('NewsRepository: Загрузка новости с ID: $id');
final response = await _apiService.getNewsById(id);
final news = NewsEntity.fromJson(response);
dev.log('NewsRepository: Успешно загружена новость с ID: $id');
return news;
} catch (e, stackTrace) {
dev.log('NewsRepository: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить новость: $e');
}
}
}

View File

@@ -0,0 +1,36 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import '../../data/network/api_service.dart';
import '../../domain/repositories/notification_repository.dart';
import '../models/client_notification_dto.dart';
class NotificationRepositoryImpl implements NotificationRepository {
final ApiService _apiService;
NotificationRepositoryImpl(this._apiService);
@override
Stream<ClientNotification> getNotificationsStream() {
return _apiService.getNotificationsStream().map((data) {
// Создаем DTO из данных API
final dto = ClientNotificationDto.fromJson(data);
// Преобразуем в entity
return dto.toEntity();
});
}
@override
Future<ClientNotification> cancelNotification(int id) async {
final data = await _apiService.cancelNotification(id);
if (data == null) {
throw Exception("Failed to cancel notification");
}
final dto = ClientNotificationDto.fromJson(data);
return dto.toEntity();
}
@override
void closeStream() {
// соединение закрывается автоматически при отписке от stream
}
}

View File

@@ -0,0 +1,116 @@
import '../../core/failures.dart';
import '../../core/result.dart';
import '../../domain/entities/payment_card.dart';
import '../../domain/repositories/payment_repository.dart';
import '../../domain/service/security_service.dart';
import '../network/api_service.dart';
import '../exceptions/auth_exception.dart';
import '../exceptions/auth_block_exception.dart';
import '../exceptions/unauthorized_exception.dart';
import '../service/security_service_impl.dart';
class PaymentRepositoryImpl implements PaymentRepository {
final ApiService apiService;
final SecurityService securityService;
PaymentRepositoryImpl(this.apiService, this.securityService);
@override
Future<Result<List<PaymentCard>>> getPaymentCards() async {
try {
final cards = await apiService.getPaymentCards();
return Success(cards);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
@override
Future<Result<void>> addPaymentCard({
required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
}) async {
try {
final cardId = await apiService.addPaymentCard(
cardNumber: cardNumber,
cardHolder: cardHolder,
expirationMonth: int.parse(expiryMonth),
expirationYear: int.parse(expiryYear),
cvv: cvv,
);
// Сохраняем полный номер карты локально
await securityService.saveCardFullNumber(cardId, cardNumber);
return Success(null);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} on FormatException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
@override
Future<Result<void>> setMainPaymentCard(int cardId) async {
try {
await apiService.setMainPaymentCard(cardId);
return Success(null);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
@override
Future<Result<void>> removePaymentCard(int cardId) async {
try {
await apiService.removePaymentCard(cardId);
await securityService.removeCardFullNumber(cardId);
return Success(null);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
@override
Future<Result<bool>> activateSubscription(int optionId) async {
try {
final success = await apiService.activateSubscription(optionId: optionId);
return Success(success);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
return Failure(AuthBlockFailure("Ошибка. Вы заблокированы."));
} on UnauthorizedException catch (_) {
return Failure(UnknownFailure("Неизвестная ошибка"));
} catch (e) {
return Failure(UnknownFailure("Неизвестная ошибка"));
}
}
}

View File

@@ -0,0 +1,24 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class PinRepositoryImpl implements PinRepository {
final FlutterSecureStorage _storage;
PinRepositoryImpl(this._storage);
@override
Future<String?> getSavedPin() {
return _storage.read(key: "user_pin");
}
@override
Future<void> savePin(String? pin) async {
await _storage.write(key: "user_pin", value: pin);
}
@override
Future<void> removePin() async {
await _storage.delete(key: "user_pin");
}
}

View File

@@ -0,0 +1,48 @@
import 'dart:io';
import 'package:be_happy/data/network/api_service.dart';
import '../../domain/entities/user_profile.dart';
import '../../domain/entities/user_check_flags.dart';
import '../../domain/repositories/profile_repository.dart';
class UserProfileRepositoryImpl implements UserProfileRepository {
final ApiService _apiService;
static const String kAccessToken = "access_token";
static const String kRefreshToken = "refresh_token";
final UserProfile _cachedProfile = UserProfile(
name: 'Иванов Антон',
birthDate: '12-03-2005',
phone: '+375 00 000-00-00',
balance: null,
email: 'почта@gmail.com',
);
UserProfileRepositoryImpl(this._apiService);
@override
Future<UserProfile> getProfile() async {
return await _apiService.getProfile() ?? _cachedProfile;
}
@override
Future<UserProfile?> updateProfile(UserProfile profile) async {
//await Future.delayed(const Duration(milliseconds: 300));
print("UPDATE PROFILE DATA: $profile");
return await _apiService.updateProfile(profile);
}
@override
Future<int?> uploadProfilePhoto(File imageFile) async {
return await _apiService.uploadPhoto(imageFile);
}
@override
Future<UserCheckFlags?> checkUser() async {
return await _apiService.checkUser();
}
}

View File

@@ -0,0 +1,444 @@
import 'dart:io';
import 'package:be_happy/data/exceptions/auth_block_exception.dart';
import 'package:be_happy/data/exceptions/route_history_not_found_exception.dart';
import 'package:be_happy/data/exceptions/scooter_not_found_exception.dart';
import 'package:be_happy/data/exceptions/unauthorized_exception.dart';
import 'package:be_happy/data/exceptions/wrong_zone_exception.dart';
import 'package:be_happy/domain/entities/point.dart';
import 'package:be_happy/domain/repositories/scooter_repository.dart';
import '../../core/failures.dart';
import '../../core/result.dart';
import '../../domain/entities/active_scooter_order.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/tariff.dart';
import '../../domain/entities/subscription.dart';
import '../../domain/entities/scooter_order.dart';
import '../exceptions/auth_exception.dart';
import '../network/api_service.dart';
class ScooterRepositoryImpl extends ScooterRepository {
final ApiService _apiService;
final scooter = Scooter(
id: 123,
title: "Unnamed",
status: "Available",
latitude: 55.178960,
longitude: 30.222316,
batteryLevel: 89,
isOnline: true,
maxSpeed: 25,
number: "Unnamed",
);
ScooterRepositoryImpl(this._apiService);
@override
Future<List<Scooter>> getScooters(
List<double> area,
int page,
int pageSize,
) async {
final responce = await _apiService.getScooters(
area: area,
page: page,
pageSize: pageSize,
);
final List<Scooter>? list = responce?.scooters;
// list?.add(scooter);
// print("Scooters: ${list?.first.status}");
if (responce != null) {
return list ?? List.empty();
}
return List.empty();
}
@override
Future<Result<Scooter?>> getScooter(int id) async {
late final Result<Scooter> result;
try {
final scooter = await _apiService.getScooterById(id: id);
if (scooter != null) {
result = Success(scooter);
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<Tariff>>> getAvailableTariffs(int scooterId) async {
late final Result<List<Tariff>> result;
try {
final response = await _apiService.getAvailableTariffs(
scooterId: scooterId,
);
if (response != null) {
result = Success(response.tariffs);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<Subscription>>> getAvailableSubscriptions() async {
late final Result<List<Subscription>> result;
try {
final response = await _apiService.getAvailableSubscriptions();
if (response != null) {
result = Success(response.subscriptions);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<Subscription>> getSubscriptionById(int id) async {
late final Result<Subscription> result;
try {
final subscription = await _apiService.getSubscriptionById(id: id);
if (subscription != null) {
result = Success(subscription);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<Subscription>>> getClientSubscriptions() async {
late final Result<List<Subscription>> result;
try {
final subscriptions = await _apiService.getClientSubscriptions();
result = Success(subscriptions);
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> bookScooter({
required int scooterId,
required int planId,
int? subscriptionId,
int? cardId,
required bool isBalance,
required bool isInsurance,
}) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.bookScooter(
scooterId: scooterId,
planId: planId,
subscriptionId: subscriptionId,
cardId: cardId,
isBalance: isBalance,
isInsurance: isInsurance,
);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> startRide(int orderId) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.startRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> cancelRide(int orderId) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.cancelRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> pauseRide(int orderId) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.pauseRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> resumeRide(int orderId) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.resumeRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> 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("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on WrongZoneException catch (e) {
result = Failure(WrongZoneFailure(e.message));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> payRide(int orderId) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.payRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<ScooterOrder>>> getClientOrders() async {
late final Result<List<ScooterOrder>> result;
try {
final orders = await _apiService.getClientOrders();
result = Success(orders);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<int>>> uploadScooterPhotos(List<File> images) async {
late final Result<List<int>> result;
try {
final filesId = await _apiService.uploadScooterPhotos(images);
result = Success(filesId);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ActiveScooterOrder>> updateScooterOrderData({
required int orderId,
}) async {
late final Result<ActiveScooterOrder> result;
try {
final order = await _apiService.updateScooterOrderData(orderId: orderId);
if (order != null) {
print("ORDER DATA FROM REPOSITORY: $order");
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> payScooterOrderWithPhotos({
required int orderId,
required int? cardId,
required bool isBalance,
}) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.payScooterOrderWithPhotos(
orderId: orderId,
cardId: cardId,
isBalance: isBalance,
);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<ScooterOrder>> getScooterOrderById(int id) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.getScooterOrderById(id: id);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<ScooterOrder>>> getScooterOrderHistory({
int page = 1,
int pageSize = 20,
}) async {
late final Result<List<ScooterOrder>> result;
try {
final response = await _apiService.getScooterOrderHistory(
page: page,
pageSize: pageSize,
);
final List<dynamic> data = response['data'] ?? [];
final orders = data.map((json) => ScooterOrder.fromJson(json)).toList();
result = Success(orders);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<Scooter?>> getScooterByTitle(String title) async {
late final Result<Scooter?> result;
try {
final scooter = await _apiService.getScooterByTitle(title: title);
if (scooter != null) {
result = Success(scooter);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
} on ScooterNotFoundException catch (e) {
result = Failure(ScooterNotFoundFailure(e.message));
} on UnauthorizedException catch (e) {
result = Failure(AuthFailure(0, message: e.toString()));
} on AuthBlockException catch (e) {
result = Failure(AuthBlockFailure(e.toString()));
} catch (e, stacktrace) {
print("REPOSITORY ERROR: $e");
print("STACKTRACE: $stacktrace");
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<Point>>> getScooterOrderRouteHistory(int id) async {
late final Result<List<Point>> result;
try {
final route = await _apiService.getScooterOrderRouteHistory(id: id);
result = Success(route);
} on RouteHistoryNotFoundException catch (e) {
result = Failure(RouteHistoryNotFoundFailure(e.message));
} on UnauthorizedException catch (e) {
result = Failure(AuthFailure(0, message: e.toString()));
} on AuthBlockException catch (e) {
result = Failure(AuthBlockFailure(e.toString()));
} catch (e, stacktrace) {
print("REPOSITORY ERROR: $e");
print("STACKTRACE: $stacktrace");
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
}

View File

@@ -0,0 +1,69 @@
import 'package:be_happy/domain/entities/zone.dart';
import 'package:be_happy/domain/repositories/zone_repository.dart';
import '../../domain/entities/point.dart';
import '../network/api_service.dart';
class ZoneRepositoryImpl extends ZoneRepository {
final ApiService _apiService;
ZoneRepositoryImpl(this._apiService);
@override
Future<List<Zone>> getZones(List<double> area,
int page,
int pageSize,) async {
final List<Point> list = [];
final List<Point> list2 = [];
/*list.add(Point(55.182372,30.203347));
list.add(Point(55.173746,30.200257));
list.add(Point(55.172153,30.212531));
list.add(Point(55.179946,30.215964));
list2.add(Point(55.178304,30.227380));
list2.add(Point(55.178059,30.229654));
list2.add(Point(55.191339,30.234718));
list2.add(Point(55.194352,30.234890));
list2.add(Point(55.194377,30.231972));
list2.add(Point(55.192123,30.232744));
final zone = Zone(id: 123,
title: "Zone 01",
description: "description",
type: "Finish",
isActive: true,
shapeType: "polygon",
points: list,
speedLimit: "speedLimit");
final zone2 = Zone(id: 124,
title: "Zone 02",
description: "description",
type: "NotDrive",
isActive: true,
shapeType: "polygon",
points: list2,
speedLimit: "speedLimit");
*/
final responce = await _apiService.getZones(
area: area,
page: page,
pageSize: pageSize,
);
final List<Zone>? zones = responce?.zones;
// zones?.add(zone);
// zones?.add(zone2);
print("Scooters: ${zones?.first.title}");
if (responce != null) {
return zones ?? List.empty();
}
return List.empty();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:shared_preferences/shared_preferences.dart';
class AppSettingsService {
final SharedPreferences sharedPreferences;
AppSettingsService(this.sharedPreferences);
Future<void> saveMapSettingsFlag(String key, bool value) async {
sharedPreferences.setBool(key, value);
}
bool getMapSettingsFlag(String key) {
return sharedPreferences.getBool(key) ?? false;
}
}

View File

@@ -0,0 +1,43 @@
import 'dart:io';
import 'package:android_id/android_id.dart';
import 'package:device_info_plus/device_info_plus.dart';
import '../../domain/service/device_info_service.dart';
class DeviceInfoServiceImpl extends DeviceInfoService {
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
final AndroidId _androidId = const AndroidId();
@override
Future<String> getDeviceModel() async {
try {
if (Platform.isAndroid) {
final androidInfo = await _deviceInfo.androidInfo;
return '${androidInfo.manufacturer} ${androidInfo.model}';
} else if (Platform.isIOS) {
final iosInfo = await _deviceInfo.iosInfo;
return iosInfo.utsname.machine;
}
} catch (e) {
print("ERROR: $e");
}
return 'Unknown';
}
@override
Future<String?> getSystemId() async {
try {
if (Platform.isAndroid) {
return await _androidId.getId();
} else if (Platform.isIOS) {
final iosInfo = await _deviceInfo.iosInfo;
return iosInfo.identifierForVendor;
}
} catch (e) {
print("ERROR: $e");
return null;
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
import 'dart:developer' as dev;
import '../network/api_service.dart';
class NewsApiService {
final ApiService _apiService;
NewsApiService(this._apiService);
Future<Map<String, dynamic>> getNews() async {
try {
dev.log('NewsApiService: Запрос GET /news');
final response = await _apiService.getNews();
dev.log('NewsApiService: Успешно получено ${response['data']?.length ?? 0} новостей');
return response;
} catch (e, stackTrace) {
dev.log('NewsApiService: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить новости: $e');
}
}
Future<Map<String, dynamic>> getNewsById(int newsId) async {
try {
dev.log('NewsApiService: Запрос GET /news/$newsId');
final response = await _apiService.getNewsById(newsId);
dev.log('NewsApiService: Успешно получена новость с ID: $newsId');
return response!;
} catch (e, stackTrace) {
dev.log('NewsApiService: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить новость: $e');
}
}
}

View File

@@ -0,0 +1,80 @@
import 'package:be_happy/domain/entities/user_auth_data.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../../domain/service/security_service.dart';
class SecurityServiceImpl extends SecurityService {
static const String kAccessToken = "access_token";
static const String kRefreshToken = "refresh_token";
static const String kCardNumberPrefix = "card_number_";
final FlutterSecureStorage _secureStorage;
SecurityServiceImpl(this._secureStorage);
@override
Future<void> saveTokens(UserAuthData data) async {
await _secureStorage.write(key: kAccessToken, value: data.accessToken);
await _secureStorage.write(key: kRefreshToken, value: data.refreshToken);
}
@override
Future<void> removeTokens() async {
await _secureStorage.delete(key: kRefreshToken);
await _secureStorage.delete(key: kAccessToken);
}
@override
Future<String?> getAccessToken() async {
return await _secureStorage.read(key: kAccessToken);
}
@override
Future<String?> getRefreshToken() async {
return await _secureStorage.read(key: kRefreshToken);
}
@override
Future<void> saveCardFullNumber(int cardId, String fullCardNumber) async {
await _secureStorage.write(
key: '${kCardNumberPrefix}_$cardId',
value: fullCardNumber,
);
}
@override
Future<String?> getCardFullNumber(int cardId) async {
return await _secureStorage.read(key: '${kCardNumberPrefix}_$cardId');
}
@override
Future<void> removeCardFullNumber(int cardId) async {
await _secureStorage.delete(key: '${kCardNumberPrefix}_$cardId');
}
@override
Future<void> clearAllCardsNumbers() async {
// Получаем все ключи и удаляем те, что начинаются с префикса карты
final allKeys = await _secureStorage.readAll();
for (final key in allKeys.keys) {
if (key.startsWith(kCardNumberPrefix)) {
await _secureStorage.delete(key: key);
}
}
}
// @override
// Future<UserAuthData?> getTokens() async {
// final accessToken = await _secureStorage.read(key: kAccessToken);
// final refreshToken = await _secureStorage.read(key: kRefreshToken);
//
// if(accessToken != null && refreshToken != null) {
// return UserAuthData(
// accessToken: accessToken,
// refreshToken: refreshToken,
// );
// }
//
// return null;
// }
}