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

93
lib/core/app_colors.dart Normal file
View File

@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
class AppColors {
// --------------------------
// ФОНЫ ЭКРАНОВ
// --------------------------
/// Градиент PhoneScreen (ввод телефона)
static const LinearGradient phoneScreenBg = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF293A69),
Color(0xFF202741),
],
);
/// Градиент PhoneLoginScreen & PinLoginScreen
static const LinearGradient authScreenBg = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF0D1A44),
Color(0xFF091233),
],
);
// --------------------------
// КНОПКИ
// --------------------------
/// Активная кнопка — бирюзово-зелёный градиент
static const LinearGradient activeButtonGradient = LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xFF75FBF0),
Color(0xFF8BFFAA),
],
);
/// Неактивная кнопка — тёмно-синий
static const Color disabledButtonColor = Color(0xFF2A3A6A);
/// Активный текст кнопки
static const Color activeButtonText = Color(0xFF000032);
/// Неактивный текст кнопки
static const Color disabledButtonText = Color(0x99FFFFFF);
// --------------------------
// ЦВЕТА ДЛЯ TOGGLE / CHECKBOX
// --------------------------
/// Бордер неактивного чекбокса
static const Color checkboxBorder = Colors.white70;
/// Бордер чекбокса с ошибкой
static const Color checkboxErrorBorder = Color(0xFFBA1A1A);
/// Цвет заливки активного чекбокса
static const Color checkboxFill = Color(0xFF8BFFAA);
// --------------------------
// PIN / CODE INPUT
// --------------------------
/// Цвет цифры в SMS-коде (PhoneLoginScreen)
static const Color smsDigit = Color(0xFF75FBF0);
/// Цвет точки-плейсхолдера
static const Color digitPlaceholder = Color(0x66FFFFFF);
/// Цвет правильного PIN
static const Color pinSuccess = Color(0xFF66FF99);
/// Цвет неверного PIN
static const Color pinError = Color(0xFFFF4F4F);
// --------------------------
// ТЕКСТ
// --------------------------
static const Color whiteText = Colors.white;
static const Color white70 = Colors.white70;
static const Color hint = Colors.white54;
// --------------------------
// ПРОЧЕЕ
// --------------------------
static const Color darkBlue = Color(0xFF000032);
}

29
lib/core/failures.dart Normal file
View File

@@ -0,0 +1,29 @@
sealed class EntityFailure {
final String? message;
const EntityFailure(this.message);
}
class AuthFailure extends EntityFailure {
final int attemptsLeft;
const AuthFailure(this.attemptsLeft, {String? message}) : super(message);
}
class AuthBlockFailure extends EntityFailure {
const AuthBlockFailure(super.message);
}
class WrongZoneFailure extends EntityFailure {
const WrongZoneFailure(super.message);
}
class ScooterNotFoundFailure extends EntityFailure {
const ScooterNotFoundFailure(super.message);
}
class RouteHistoryNotFoundFailure extends EntityFailure {
const RouteHistoryNotFoundFailure(super.message);
}
class UnknownFailure extends EntityFailure {
const UnknownFailure(super.message);
}

13
lib/core/result.dart Normal file
View File

@@ -0,0 +1,13 @@
import 'failures.dart';
sealed class Result<T> {}
class Success<T> extends Result<T> {
final T? data;
Success(this.data);
}
class Failure<T> extends Result<T> {
final EntityFailure failure;
Failure(this.failure);
}

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;
// }
}

374
lib/di/service_locator.dart Normal file
View File

@@ -0,0 +1,374 @@
import 'package:be_happy/data/repositories/app_settings_repository_impl.dart';
import 'package:be_happy/data/repositories/certificate_repository_impl.dart';
import 'package:be_happy/data/repositories/notification_repository_impl.dart';
import 'package:be_happy/data/repositories/payment_repository_impl.dart'; // ← новый
import 'package:be_happy/data/repositories/scooter_repository_impl.dart';
import 'package:be_happy/data/repositories/zone_repository_impl.dart';
import 'package:be_happy/data/service/app_setting_service.dart';
import 'package:be_happy/data/service/device_info_service_impl.dart';
import 'package:be_happy/data/service/security_service_impl.dart';
import 'package:be_happy/domain/repositories/app_settings_repository.dart';
import 'package:be_happy/domain/repositories/certificate_repository.dart';
import 'package:be_happy/domain/repositories/notification_repository.dart';
import 'package:be_happy/domain/repositories/payment_repository.dart'; // ← новый
import 'package:be_happy/domain/repositories/pin_repository.dart';
import 'package:be_happy/domain/repositories/profile_repository.dart';
import 'package:be_happy/domain/repositories/scooter_repository.dart';
import 'package:be_happy/domain/repositories/zone_repository.dart';
import 'package:be_happy/domain/service/security_service.dart';
import 'package:be_happy/domain/usecase/add_payment_card_usecase.dart'; // ← новый
import 'package:be_happy/domain/usecase/get_certificates_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_order_route_history_usecase.dart';
import 'package:be_happy/domain/usecase/is_pin_set_usecase.dart';
import 'package:be_happy/domain/usecase/purchase_certificate_usecase.dart';
import 'package:be_happy/domain/usecase/book_scooter_usecase.dart';
import 'package:be_happy/domain/usecase/cancel_notification_usecase.dart';
import 'package:be_happy/domain/usecase/create_pin_usecase.dart';
import 'package:be_happy/domain/usecase/get_address_by_point_usecase.dart';
import 'package:be_happy/domain/usecase/get_available_scooters_usecase.dart';
import 'package:be_happy/domain/usecase/get_available_subscriptions_usecase.dart';
import 'package:be_happy/domain/usecase/get_available_tariffs_usecase.dart';
import 'package:be_happy/domain/usecase/get_available_zones_usecase.dart';
import 'package:be_happy/domain/usecase/get_client_orders_usecase.dart';
import 'package:be_happy/domain/usecase/get_map_settings_usecase.dart';
import 'package:be_happy/domain/usecase/get_notifications_stream_usecase.dart';
import 'package:be_happy/domain/usecase/get_payment_cards_usecase.dart';
import 'package:be_happy/domain/usecase/get_pedestrian_routes_usecase.dart';
import 'package:be_happy/domain/usecase/get_profile_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_usecase.dart';
import 'package:be_happy/domain/usecase/get_subscription_by_id_usecase.dart';
import 'package:be_happy/domain/usecase/get_subscription_by_id_usecase.dart';
import 'package:be_happy/domain/usecase/login_usecase.dart';
import 'package:be_happy/domain/usecase/logout_usecase.dart';
import 'package:be_happy/domain/usecase/pay_ride_usecase.dart';
import 'package:be_happy/domain/usecase/refresh_token_usecase.dart';
import 'package:be_happy/domain/usecase/save_map_settings_usecase.dart';
import 'package:be_happy/domain/usecase/update_profile_usecase.dart';
import 'package:be_happy/domain/usecase/upload_profile_photo_usecase.dart';
import 'package:be_happy/domain/usecase/verify_code_usecase.dart';
import 'package:be_happy/domain/usecase/upload_scooter_photos_usecase.dart';
import 'package:be_happy/domain/usecase/update_scooter_order_data_usecase.dart';
import 'package:be_happy/domain/usecase/pay_scooter_order_with_photos_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_order_by_id_usecase.dart';
import 'package:be_happy/domain/usecase/finish_ride_usecase.dart';
import 'package:be_happy/domain/usecase/pause_ride_usecase.dart';
import 'package:be_happy/domain/usecase/resume_ride_usecase.dart';
import 'package:be_happy/domain/usecase/verify_pin_usecase.dart';
import 'package:be_happy/presentation/viewmodel/active_ride_bloc.dart';
import 'package:be_happy/presentation/viewmodel/add_card_bloc.dart'; // ← новый
import 'package:be_happy/presentation/viewmodel/current_rides_bloc.dart';
import 'package:be_happy/presentation/viewmodel/map_settings_modal_bloc.dart';
import 'package:be_happy/presentation/viewmodel/payment_confirm_bloc.dart';
import 'package:be_happy/presentation/viewmodel/payment_method_sheet_bloc.dart';
import 'package:be_happy/presentation/viewmodel/payment_methods_bloc.dart';
import 'package:be_happy/presentation/viewmodel/pin_bloc.dart';
import 'package:be_happy/presentation/viewmodel/profile_bloc.dart';
import 'package:be_happy/presentation/viewmodel/scooter_detail_bloc.dart';
import 'package:be_happy/presentation/viewmodel/scooter_code_bloc.dart';
import 'package:be_happy/presentation/viewmodel/send_photo_bloc.dart';
import 'package:be_happy/presentation/viewmodel/splash_bloc.dart';
import 'package:be_happy/presentation/viewmodel/tariff_sheet_bloc.dart';
import 'package:be_happy/domain/usecase/cancel_ride_usecase.dart';
import 'package:be_happy/domain/usecase/start_ride_usecase.dart';
import 'package:be_happy/domain/usecase/check_user_usecase.dart';
import 'package:be_happy/presentation/viewmodel/reserved_ride_bloc.dart';
import 'package:dio/dio.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../data/network/api_service.dart';
import '../data/network/geocoding_remote_datasource.dart';
import '../data/repositories/auth_repository_impl.dart';
import '../data/repositories/news_repository_impl.dart';
import '../data/repositories/pin_repository_impl.dart';
import '../data/repositories/profile_repository_impl.dart';
import '../data/service/news_api_service.dart';
import '../domain/repositories/auth_repository.dart';
import '../domain/repositories/news_repository.dart';
import '../domain/service/device_info_service.dart';
import '../domain/usecase/activate_subscription_usecase.dart';
import '../domain/usecase/get_client_subscriptions_usecase.dart';
import '../domain/usecase/get_news_by_id_usecase.dart';
import '../domain/usecase/get_scooter_by_title_usecase.dart';
import '../domain/usecase/get_scooter_order_history_usecase.dart';
import '../domain/usecase/remove_payment_card_usecase.dart';
import '../domain/usecase/set_main_payment_card_usecase.dart';
import '../presentation/viewmodel/auth_bloc.dart';
import '../presentation/viewmodel/edit_profile_bloc.dart';
import '../presentation/viewmodel/map_bloc.dart';
import '../presentation/viewmodel/news_bloc.dart';
import '../presentation/viewmodel/order_history_bloc.dart';
import '../presentation/viewmodel/scooter_detail_modal_bloc.dart';
import '../presentation/viewmodel/subscription_list_bloc.dart';
import '../presentation/viewmodel/verify_code_bloc.dart';
final getIt = GetIt.instance;
Future<void> setupDependencies() async {
final sharedPreferences = await SharedPreferences.getInstance();
final dio = Dio();
dio.interceptors.add(LogInterceptor(responseBody: true, requestBody: true));
// HTTP
getIt.registerSingleton<http.Client>(http.Client());
//SecureStorage
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
//SharedPrefs
getIt.registerSingleton<SharedPreferences>(sharedPreferences);
// Service
getIt.registerSingleton<SecurityService>(SecurityServiceImpl(getIt()));
getIt.registerLazySingleton(() => ApiService(getIt<SecurityService>(), dio));
getIt.registerSingleton<GeocodingRemoteDataSource>(
GeocodingRemoteDataSource(),
);
getIt.registerSingleton<DeviceInfoService>(DeviceInfoServiceImpl());
getIt.registerSingleton<AppSettingsService>(AppSettingsService(getIt()));
getIt.registerLazySingleton<NewsApiService>(
() => NewsApiService(getIt<ApiService>()),
);
// Repository
getIt.registerSingleton<AuthRepository>(
AuthRepositoryImpl(getIt(), getIt(), getIt()),
);
getIt.registerSingleton<PinRepository>(PinRepositoryImpl(getIt()));
getIt.registerSingleton<UserProfileRepository>(
UserProfileRepositoryImpl(getIt()),
);
getIt.registerSingleton<ScooterRepository>(ScooterRepositoryImpl(getIt()));
getIt.registerSingleton<ZoneRepository>(ZoneRepositoryImpl(getIt()));
getIt.registerSingleton<AppSettingsRepository>(
AppSettingsRepositoryImpl(getIt()),
);
getIt.registerSingleton<PaymentRepository>(
PaymentRepositoryImpl(getIt(), getIt()),
);
getIt.registerSingleton<CertificateRepository>(
CertificateRepositoryImpl(getIt(), getIt()),
);
getIt.registerLazySingleton<NewsRepository>(
() => NewsRepositoryImpl(getIt<NewsApiService>()),
);
getIt.registerSingleton<NotificationRepository>(
NotificationRepositoryImpl(getIt()),
);
// Use Cases
getIt.registerSingleton<LoginUseCase>(LoginUseCase(getIt()));
getIt.registerSingleton<LogoutUseCase>(LogoutUseCase(getIt(), getIt()));
getIt.registerSingleton<VerifyCodeUseCase>(VerifyCodeUseCase(getIt()));
getIt.registerSingleton<RefreshTokenUseCase>(RefreshTokenUseCase(getIt()));
getIt.registerSingleton<CreatePinUseCase>(CreatePinUseCase(getIt()));
getIt.registerSingleton<VerifyPinUseCase>(VerifyPinUseCase(getIt()));
getIt.registerSingleton<IsPinSetUsecase>(IsPinSetUsecase(getIt()));
getIt.registerSingleton<GetProfileUseCase>(GetProfileUseCase(getIt()));
getIt.registerSingleton<UpdateProfileUseCase>(UpdateProfileUseCase(getIt()));
getIt.registerSingleton<UploadProfilePhotoUsecase>(
UploadProfilePhotoUsecase(getIt()),
);
getIt.registerSingleton<GetAvailableScootersUsecase>(
GetAvailableScootersUsecase(getIt()),
);
getIt.registerSingleton<GetNewsByIdUsecase>(
GetNewsByIdUsecase(getIt<NewsRepository>()),
);
getIt.registerSingleton<GetScooterUsecase>(GetScooterUsecase(getIt()));
getIt.registerSingleton<GetAvailableTariffsUsecase>(
GetAvailableTariffsUsecase(getIt()),
);
getIt.registerSingleton<GetAddressByPointUsecase>(
GetAddressByPointUsecase(getIt()),
);
getIt.registerSingleton<GetPedestrianRoutesUsecase>(
GetPedestrianRoutesUsecase(getIt()),
);
getIt.registerSingleton<GetScooterOrderRouteHistoryUsecase>(
GetScooterOrderRouteHistoryUsecase(getIt()),
);
getIt.registerSingleton<GetAvailableZonesUsecase>(
GetAvailableZonesUsecase(getIt()),
);
getIt.registerSingleton<GetMapSettingsUsecase>(
GetMapSettingsUsecase(getIt()),
);
getIt.registerSingleton<SaveMapSettingsUsecase>(
SaveMapSettingsUsecase(getIt()),
);
getIt.registerSingleton<AddPaymentCardUsecase>(
AddPaymentCardUsecase(getIt()),
);
getIt.registerSingleton<GetPaymentCardsUsecase>(
GetPaymentCardsUsecase(getIt(), getIt()),
);
getIt.registerSingleton<SetMainPaymentCardUsecase>(
SetMainPaymentCardUsecase(getIt()),
);
getIt.registerSingleton<RemovePaymentCardUsecase>(
RemovePaymentCardUsecase(getIt()),
);
getIt.registerSingleton<GetCertificatesUsecase>(
GetCertificatesUsecase(getIt()),
);
getIt.registerSingleton<PurchaseCertificateUsecase>(
PurchaseCertificateUsecase(getIt()),
);
getIt.registerSingleton<GetClientOrdersUsecase>(
GetClientOrdersUsecase(getIt()),
);
getIt.registerSingleton<BookScooterUsecase>(BookScooterUsecase(getIt()));
getIt.registerSingleton<StartRideUsecase>(StartRideUsecase(getIt()));
getIt.registerSingleton<CancelRideUsecase>(CancelRideUsecase(getIt()));
getIt.registerSingleton<UploadScooterPhotosUsecase>(
UploadScooterPhotosUsecase(getIt()),
);
getIt.registerSingleton<UpdateScooterOrderDataUsecase>(
UpdateScooterOrderDataUsecase(getIt()),
);
getIt.registerSingleton<PayScooterOrderWithPhotosUsecase>(
PayScooterOrderWithPhotosUsecase(getIt()),
);
getIt.registerSingleton<GetScooterOrderByIdUsecase>(
GetScooterOrderByIdUsecase(getIt()),
);
getIt.registerSingleton<FinishRideUsecase>(FinishRideUsecase(getIt()));
getIt.registerSingleton<PauseRideUsecase>(PauseRideUsecase(getIt()));
getIt.registerSingleton<ResumeRideUsecase>(ResumeRideUsecase(getIt()));
getIt.registerSingleton<PayRideUsecase>(PayRideUsecase(getIt()));
getIt.registerSingleton<GetAvailableSubscriptionsUsecase>(
GetAvailableSubscriptionsUsecase(getIt()),
);
getIt.registerSingleton<GetSubscriptionByIdUsecase>(
GetSubscriptionByIdUsecase(getIt()),
);
getIt.registerSingleton<ActivateSubscriptionUsecase>(
ActivateSubscriptionUsecase(getIt()),
);
getIt.registerSingleton<GetClientSubscriptionsUsecase>(
GetClientSubscriptionsUsecase(getIt()),
);
getIt.registerSingleton<GetScooterByTitleUsecase>(
GetScooterByTitleUsecase(getIt()),
);
// Blocs
getIt.registerLazySingleton<SplashBloc>(() => SplashBloc(getIt()));
getIt.registerFactory<PhoneAuthBloc>(() => PhoneAuthBloc(getIt()));
getIt.registerFactory<VerifyCodeBloc>(() => VerifyCodeBloc(getIt()));
getIt.registerFactory<ProfileBloc>(() => ProfileBloc(getIt(), getIt(), getIt()));
getIt.registerFactory<EditProfileBloc>(
() => EditProfileBloc(getIt(), getIt()),
);
getIt.registerFactory<MapBloc>(
() => MapBloc(
getIt(),
getIt(),
getIt(),
getIt(),
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerFactory<ScooterDetailBloc>(() => ScooterDetailBloc(getIt()));
getIt.registerFactory<MapSettingsModalBloc>(
() => MapSettingsModalBloc(getIt(), getIt()),
);
getIt.registerFactory<AddCardBloc>(() => AddCardBloc(getIt()));
getIt.registerFactory<PaymentMethodSheetBloc>(
() => PaymentMethodSheetBloc(getIt()),
);
getIt.registerFactory<CurrentRidesBloc>(() => CurrentRidesBloc(getIt()));
getIt.registerFactory<ActiveRideBloc>(
() => ActiveRideBloc(getIt(), getIt(), getIt(), getIt(), getIt()),
);
getIt.registerFactory<ReservedRideBloc>(
() => ReservedRideBloc(getIt(), getIt()),
);
getIt.registerFactory<NewsBloc>(() => NewsBloc(getIt<NewsRepository>()));
getIt.registerFactory<SendPhotoBloc>(() => SendPhotoBloc(getIt(), getIt()));
getIt.registerFactory<SubscriptionListBloc>(
() => SubscriptionListBloc(
getAvailableSubscriptionsUsecase: getIt(),
getClientSubscriptionsUsecase: getIt(),
),
);
// UseCase
getIt.registerSingleton<GetScooterOrderHistoryUsecase>(
GetScooterOrderHistoryUsecase(getIt<ScooterRepository>()),
);
getIt.registerSingleton<CheckUserUseCase>(
CheckUserUseCase(getIt<UserProfileRepository>()),
);
getIt.registerSingleton<GetNotificationsStreamUseCase>(
GetNotificationsStreamUseCase(getIt<NotificationRepository>()),
);
getIt.registerSingleton<CancelNotificationUseCase>(
CancelNotificationUseCase(getIt<NotificationRepository>()),
);
// Bloc
getIt.registerFactory<OrderHistoryBloc>(
() => OrderHistoryBloc(getIt<GetScooterOrderHistoryUsecase>()),
);
getIt.registerFactory<ScooterCodeBloc>(
() => ScooterCodeBloc(getScooterByTitleUsecase: getIt<GetScooterByTitleUsecase>()),
);
}

View File

@@ -0,0 +1,50 @@
import 'scooter.dart';
class ActiveScooterOrder {
final int orderId;
final int scooterId;
final double latitude;
final double longitude;
final double mileage;
final double speed;
final double price;
final double time;
final bool zone;
ActiveScooterOrder({
required this.orderId,
required this.scooterId,
required this.latitude,
required this.longitude,
required this.mileage,
required this.speed,
required this.price,
required this.time,
required this.zone,
});
factory ActiveScooterOrder.fromJson(Map<String, dynamic> json) {
return ActiveScooterOrder(
orderId: json['orderId'] ?? 0,
scooterId: json['scooterId'] ?? 0,
latitude: (json['latitude'] ?? 0).toDouble(),
longitude: (json['longitude'] ?? 0).toDouble(),
mileage: (json['mileage'] ?? 0).toDouble(),
speed: (json['speed'] ?? 0).toDouble(),
price: (json['price'] ?? 0).toDouble(),
time: (json['time'] ?? 0.0).toDouble(),
zone: json['zone'],
);
}
Map<String, dynamic> toJson() {
return {
'orderId': orderId,
'scooterId': scooterId,
'latitude': latitude,
'longitude': longitude,
'mileage': mileage,
'speed': speed,
'price': price,
};
}
}

View File

@@ -0,0 +1,94 @@
class Currency {
final int id;
final String title;
final String currency;
final String code;
final bool isBase;
final int denomination;
final double exchangeRate;
final String formatString;
final String floatSeparator;
final int decimals;
final bool isHideZero;
Currency({
required this.id,
required this.title,
required this.currency,
required this.code,
required this.isBase,
required this.denomination,
required this.exchangeRate,
required this.formatString,
required this.floatSeparator,
required this.decimals,
required this.isHideZero,
});
factory Currency.fromJson(Map<String, dynamic> json) {
return Currency(
id: json['id'] as int,
title: json['title'] as String,
currency: json['currency'] as String,
code: json['code'] as String,
isBase: json['isBase'] as bool,
denomination: json['denomination'] as int,
exchangeRate: (json['exchangeRate'] as num).toDouble(),
formatString: json['formatString'] as String,
floatSeparator: json['floatSeparator'] as String,
decimals: json['decimals'] as int,
isHideZero: json['isHideZero'] as bool,
);
}
}
class Certificate {
final int id;
final String title;
final String? description;
final double price;
final int currencyId;
final int value;
final double discount;
final DateTime createdAt;
final DateTime updatedAt;
final int sort;
final bool isActive;
final Currency? currency;
final String? pricePrint;
Certificate({
required this.id,
required this.title,
this.description,
required this.price,
required this.currencyId,
required this.value,
required this.discount,
required this.createdAt,
required this.updatedAt,
required this.sort,
required this.isActive,
this.currency,
this.pricePrint,
});
factory Certificate.fromJson(Map<String, dynamic> json) {
return Certificate(
id: json['id'] as int,
title: json['title'] as String,
description: json['description'] as String?,
price: (json['price'] as num).toDouble(),
currencyId: json['currencyId'] as int,
value: json['value'] as int,
discount: (json['discount'] as num).toDouble(),
createdAt: DateTime.parse(json['createdAt'] as String),
updatedAt: DateTime.parse(json['updatedAt'] as String),
sort: json['sort'] as int,
isActive: json['isActive'] as bool,
currency: json['currency'] != null ? Currency.fromJson(json['currency']) : null,
pricePrint: json['pricePrint'] as String?,
);
}
}

View File

@@ -0,0 +1,58 @@
enum NotificationType {
info,
attention,
warning,
}
enum NotificationCategory {
auth,
zone,
payment,
companyInfo,
adminInfo,
scooter,
}
class ClientNotification {
final int id;
final String content;
final int clientId;
final NotificationType type;
final NotificationCategory category;
final DateTime createdAt;
final DateTime? canceledAt;
final DateTime? readAt;
ClientNotification({
required this.id,
required this.content,
required this.clientId,
required this.type,
required this.category,
required this.createdAt,
this.canceledAt,
this.readAt,
});
ClientNotification copyWith({
int? id,
String? content,
int? clientId,
NotificationType? type,
NotificationCategory? category,
DateTime? createdAt,
DateTime? canceledAt,
DateTime? readAt,
}) {
return ClientNotification(
id: id ?? this.id,
content: content ?? this.content,
clientId: clientId ?? this.clientId,
type: type ?? this.type,
category: category ?? this.category,
createdAt: createdAt ?? this.createdAt,
canceledAt: canceledAt ?? this.canceledAt,
readAt: readAt ?? this.readAt,
);
}
}

View File

@@ -0,0 +1,15 @@
class MapSettings {
final bool all_placemarks;
final bool all_zones;
final bool parking_zones;
final bool restricted_parking_zones;
final bool restricted_driving_zones;
MapSettings({
required this.all_placemarks,
required this.all_zones,
required this.parking_zones,
required this.restricted_parking_zones,
required this.restricted_driving_zones,
});
}

View File

@@ -0,0 +1,62 @@
class NewsEntity {
final int id;
final String title;
final String previewText;
final String text;
final DateTime createdAt;
final DateTime publishedAt;
final bool isActive;
final String? imageUrl;
final String? textJson;
final int? userId;
final int? pictureId;
final dynamic user;
final dynamic picture;
NewsEntity({
required this.id,
required this.title,
required this.previewText,
required this.text,
required this.createdAt,
required this.publishedAt,
required this.isActive,
this.imageUrl,
this.textJson,
this.userId,
this.pictureId,
this.user,
this.picture,
});
factory NewsEntity.fromJson(Map<String, dynamic> json) {
DateTime _parseDate(String? dateStr) {
try {
return dateStr != null ? DateTime.parse(dateStr) : DateTime.now();
} catch (_) {
return DateTime.now();
}
}
return NewsEntity(
id: json['id'] ?? 0,
title: json['title'] ?? '',
previewText: json['previewText'] ?? '',
text: json['text'] ?? '',
createdAt: _parseDate(json['createdAt']),
publishedAt: _parseDate(json['publishedAt']),
isActive: json['isActive'] ?? false,
imageUrl: json['picture'] != null
? 'https://sharing-api.sparkit.by/${json['picture']['path']}'
: null,
textJson: json['textJson'],
userId: json['userId'],
pictureId: json['pictureId'],
user: json['user'],
picture: json['picture'],
);
}
}

View File

@@ -0,0 +1,19 @@
class Pagination {
final int total;
final int currentPage;
final int lastPage;
Pagination({
required this.total,
required this.currentPage,
required this.lastPage,
});
factory Pagination.fromJson(Map<String, dynamic> json) {
return Pagination(
total: json['total'] ?? 0,
currentPage: json['currentPage'] ?? 0,
lastPage: json['lastPage'] ?? 0,
);
}
}

View File

@@ -0,0 +1,43 @@
class PaymentCard {
final int id;
final int clientId;
final int expirationMonth;
final int expirationYear;
final String cardHolder;
final String cardLastNumber;
final bool isMain;
final String type;
PaymentCard({
required this.id,
required this.clientId,
required this.expirationMonth,
required this.expirationYear,
required this.cardHolder,
required this.cardLastNumber,
required this.isMain,
required this.type,
});
PaymentCard copyWith({
int? id,
int? clientId,
int? expirationMonth,
int? expirationYear,
String? cardHolder,
String? cardLastNumber,
bool? isMain,
String? type,
}) {
return PaymentCard(
id: id ?? this.id,
clientId: clientId ?? this.clientId,
expirationMonth: expirationMonth ?? this.expirationMonth,
expirationYear: expirationYear ?? this.expirationYear,
cardHolder: cardHolder ?? this.cardHolder,
cardLastNumber: cardLastNumber ?? this.cardLastNumber,
isMain: isMain ?? this.isMain,
type: type ?? this.type,
);
}
}

View File

@@ -0,0 +1,12 @@
class Point {
final double latitude;
final double longitude;
Point(this.latitude, this.longitude);
@override
String toString() {
return 'Point{latitude: $latitude, longitude: $longitude}';
}
}

View File

@@ -0,0 +1,50 @@
class Scooter {
final int id;
final String title;
final String status;
final double latitude;
final double longitude;
final int batteryLevel;
final bool isOnline;
final int maxSpeed;
final String number;
double? distance;
double? timeToTravel;
Scooter({
required this.id,
required this.title,
required this.status,
required this.latitude,
required this.longitude,
required this.batteryLevel,
required this.isOnline,
required this.maxSpeed,
required this.number,
this.distance,
this.timeToTravel,
});
factory Scooter.fromJson(Map<String, dynamic> json) {
final scooterDetail = json['scooterDetail'] as Map<String, dynamic>? ?? {};
final model = json['model'] as Map<String, dynamic>? ?? {};
return Scooter(
id: json['id'] ?? 0,
title: json['title'] ?? 'Unknown',
status: json['status'] ?? 'Unavailable',
latitude: (scooterDetail['latitude'] as num?)?.toDouble() ?? 0.0,
longitude: (scooterDetail['longitude'] as num?)?.toDouble() ?? 0.0,
batteryLevel: (scooterDetail['batteryLevel'] as num?)?.toInt() ?? 0,
isOnline: scooterDetail['isOnline'] ?? false,
maxSpeed: (json['maxSpeed'] as num?)?.toInt() ?? 25,
number: json['title'] ?? 'Unknown',
);
}
@override
String toString() {
return 'Scooter{id: $id, title: $title}';
}
}

View File

@@ -0,0 +1,143 @@
import 'scooter.dart';
class ScooterOrder {
final int id;
final int scooterId;
final Scooter? scooter;
final int? planId;
final ScooterPlan? plan;
final int clientId;
final int? subscriptionId;
final int? cardId;
final bool isBalance;
final int decimals;
final bool isInsurance;
final double? insurancePrice;
final String? insurancePricePrint;
final double? holdPrice;
final String? holdPricePrint;
final double? totalPrice;
final String? totalPricePrint;
final int? currencyId;
final String status;
final DateTime createdAt;
final DateTime? updatedAt;
final DateTime? startAt;
final DateTime? finishAt;
final DateTime? expiresAt;
final DateTime? cancelAt;
final String? cancelDescription;
final double mileage; //эту
final double avgSpeed; //эту и снизу еще 4 есть
ScooterOrder({
required this.id,
required this.scooterId,
this.scooter,
this.planId,
this.plan,
required this.clientId,
this.subscriptionId,
this.cardId,
required this.isBalance,
required this.decimals,
required this.isInsurance,
this.insurancePrice,
this.insurancePricePrint,
this.holdPrice,
this.holdPricePrint,
this.totalPrice,
this.totalPricePrint,
this.currencyId,
required this.status,
required this.createdAt,
this.updatedAt,
this.startAt,
this.finishAt,
this.expiresAt,
this.cancelAt,
this.cancelDescription,
required this.mileage, //эту
required this.avgSpeed, //эту и снизу еще 2 есть
});
factory ScooterOrder.fromJson(Map<String, dynamic> json) {
return ScooterOrder(
id: json['id'] ?? 0,
scooterId: json['scooterId'] ?? 0,
scooter: json['scooter'] != null ? Scooter.fromJson(json['scooter']) : null,
planId: json['planId'],
plan: json['plan'] != null ? ScooterPlan.fromJson(json['plan']) : null,
clientId: json['clientId'] ?? 0,
subscriptionId: json['subscriptionId'],
cardId: json['cardId'],
isBalance: json['isBalance'] ?? false,
decimals: json['decimals'] ?? 0,
isInsurance: json['isInsurance'] ?? false,
insurancePrice: (json['insurancePrice'] as num?)?.toDouble(),
insurancePricePrint: json['insurancePricePrint'],
holdPrice: (json['holdPrice'] as num?)?.toDouble(),
holdPricePrint: json['holdPricePrint'],
totalPrice: (json['totalPrice'] as num?)?.toDouble(),
totalPricePrint: json['totalPricePrint'],
currencyId: json['currencyId'],
status: json['status'] ?? '',
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'])
: DateTime.now(),
updatedAt: json['updatedAt'] != null
? DateTime.parse(json['updatedAt'])
: null,
startAt: json['startAt'] != null
? DateTime.parse(json['startAt'])
: null,
finishAt: json['finishAt'] != null
? DateTime.parse(json['finishAt'])
: null,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'])
: null,
cancelAt: json['cancelAt'] != null
? DateTime.parse(json['cancelAt'])
: null,
cancelDescription: json['cancelDescription'],
mileage: (json['mileage'] as num?)?.toDouble() ?? 0.0, //эту
avgSpeed: (json['avgSpeed'] as num?)?.toDouble() ?? 0.0, //эту
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'scooterId': scooterId,
'planId': planId,
'subscriptionId': subscriptionId,
'cardId': cardId,
'isBalance': isBalance,
'isInsurance': isInsurance,
};
}
}
class ScooterPlan {
final int id;
final String title;
final double price;
final String? description;
ScooterPlan({
required this.id,
required this.title,
required this.price,
this.description,
});
factory ScooterPlan.fromJson(Map<String, dynamic> json) {
return ScooterPlan(
id: json['id'] ?? 0,
title: json['title'] ?? '',
price: (json['price'] as num?)?.toDouble() ?? 0.0,
description: json['description'],
);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:be_happy/domain/entities/subscription_period.dart';
class Subscription {
final int id;
final String title;
final String shortDescription;
final String fullDescription;
final int planId;
final bool isActive;
final String currency;
final DateTime? activeFrom;
final DateTime? activeTo;
final DateTime createdAt;
final DateTime updatedAt;
final List<SubscriptionPeriod> options;
Subscription({
required this.id,
required this.title,
required this.shortDescription,
required this.fullDescription,
required this.planId,
required this.isActive,
required this.currency,
this.activeFrom,
this.activeTo,
required this.createdAt,
required this.updatedAt,
required this.options,
});
factory Subscription.fromJson(Map<String, dynamic> json) {
final currencyData = json['currency'] as Map<String, dynamic>? ?? {};
final optionsData = json['options'] as List<dynamic>? ?? [];
return Subscription(
id: json['id'] ?? 0,
title: json['title'] ?? '',
shortDescription: json['shortDescription'] ?? '',
fullDescription: json['fullDescription'] ?? '',
planId: json['planId'] ?? 0,
isActive: json['isActive'] ?? false,
currency: currencyData['currency'] ?? 'BYN',
activeFrom: json['activeFrom'] != null ? DateTime.parse(json['activeFrom']) : null,
activeTo: json['activeTo'] != null ? DateTime.parse(json['activeTo']) : null,
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : DateTime.now(),
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : DateTime.now(),
options: optionsData.map((e) => SubscriptionPeriod.fromJson(e as Map<String, dynamic>)).toList(),
);
}
@override
String toString() {
return 'Subscription{id: $id, title: $title, isActive: $isActive}';
}
}

View File

@@ -0,0 +1,33 @@
class SubscriptionPeriod {
final int id;
final int subscriptionId;
final int days;
final String title;
final double price;
final String pricePrint;
SubscriptionPeriod({
required this.id,
required this.subscriptionId,
required this.days,
required this.title,
required this.price,
required this.pricePrint,
});
factory SubscriptionPeriod.fromJson(Map<String, dynamic> json) {
return SubscriptionPeriod(
id: json['id'] ?? 0,
subscriptionId: json['subscriptionId'] ?? 0,
days: json['days'] ?? 0,
title: json['title'] ?? '',
price: (json['price'] ?? 0).toDouble(),
pricePrint: json['pricePrint'] ?? '',
);
}
@override
String toString() {
return 'SubscriptionPeriod{id: $id, title: $title, days: $days, price: $price}';
}
}

View File

@@ -0,0 +1,51 @@
class Tariff {
final int id;
final String title;
final String description;
final bool isActive;
final String currency;
final double holdPrice; // Старт / бронь
final double drivePrice; // Цена минуты
final double pausePrice; // Пауза
final double startPrice; // Старт цена
final double cashback; // Процент кэшбэка
final double insurance; // Страховка
Tariff({
required this.id,
required this.title,
required this.description,
required this.isActive,
required this.currency,
required this.holdPrice,
required this.drivePrice,
required this.pausePrice,
required this.startPrice,
required this.cashback,
required this.insurance,
});
factory Tariff.fromJson(Map<String, dynamic> json) {
final planPrice = json['planPrice'] as Map<String, dynamic>? ?? {};
final currency = json['currency'] as Map<String, dynamic>? ?? {};
return Tariff(
id: json['id'] ?? 0,
title: json['title'] ?? 'Unknown',
description: json['description'] ?? '',
isActive: json['isActive'] ?? false,
currency: currency['currency'] ?? 'BYN',
holdPrice: (planPrice['hold'] as num?)?.toDouble() ?? 0.0,
drivePrice: (planPrice['drive'] as num?)?.toDouble() ?? 0.0,
pausePrice: (planPrice['pause'] as num?)?.toDouble() ?? 0.0,
startPrice: (planPrice['start'] as num?)?.toDouble() ?? 0.0,
cashback: (planPrice['cashback'] as num?)?.toDouble() ?? 0.0,
insurance: (planPrice['insurance'] as num?)?.toDouble() ?? 0.0,
);
}
@override
String toString() {
return 'Tariff{id: $id, title: $title, isActive: $isActive}';
}
}

View File

@@ -0,0 +1,17 @@
class TopUpTariff {
final int id;
final int points;
final double price;
final double? discountPercent;
TopUpTariff({
required this.id,
required this.points,
required this.price,
this.discountPercent,
});
double get finalPrice => discountPercent != null
? price * (1 - discountPercent! / 100)
: price;
}

View File

@@ -0,0 +1,9 @@
class UserAuthData {
final String accessToken;
final String refreshToken;
UserAuthData({
required this.accessToken,
required this.refreshToken,
});
}

View File

@@ -0,0 +1,23 @@
class UserCheckFlags {
final bool hasFine;
final bool hasUnpaidOrder;
final bool hasCard;
const UserCheckFlags({
required this.hasFine,
required this.hasUnpaidOrder,
required this.hasCard,
});
UserCheckFlags copyWith({
bool? hasFine,
bool? hasUnpaidOrder,
bool? hasCard,
}) {
return UserCheckFlags(
hasFine: hasFine ?? this.hasFine,
hasUnpaidOrder: hasUnpaidOrder ?? this.hasUnpaidOrder,
hasCard: hasCard ?? this.hasCard,
);
}
}

View File

@@ -0,0 +1,38 @@
class UserProfile {
String name;
String birthDate;
String phone;
String email;
int? balance;
int? avatarId;
String? avatarUrl;
UserProfile({
required this.name,
required this.birthDate,
required this.phone,
required this.email,
this.balance,
this.avatarId,
this.avatarUrl,
});
UserProfile copyWith({
String? name,
String? birthDate,
String? email,
int? balance,
int? avatarId,
String? avatarUrl,
}) {
return UserProfile(
name: name ?? this.name,
birthDate: birthDate ?? this.birthDate,
phone: phone, // телефон не меняется
email: email ?? this.email,
balance: balance ?? this.balance,
avatarId: avatarId ?? this.avatarId,
avatarUrl: avatarUrl ?? this.avatarUrl,
);
}
}

View File

@@ -0,0 +1,74 @@
import 'dart:convert';
import 'package:be_happy/domain/entities/point.dart';
class Zone {
final int id;
final String title;
final String description;
final String type;
final bool isActive;
final String shapeType;
final List<Point> points;
final String speedLimit;
Zone({
required this.id,
required this.title,
required this.description,
required this.type,
required this.isActive,
required this.shapeType,
required this.points,
required this.speedLimit,
});
factory Zone.fromJson(Map<String, dynamic> json) {
final zoneCoordinates = json['coordinates'] as Map<String, dynamic>? ?? {};
final String coordsString = zoneCoordinates['coordinates'] ?? '[]';
final String shapeType = zoneCoordinates['type'] ?? 'Polygon';
List<Point> points = [];
try {
final dynamic decoded = jsonDecode(coordsString);
if (decoded is List && decoded.isNotEmpty) {
List<dynamic> targetList = [];
if (shapeType == 'Polygon') {
// У полигона структура [[[lat, lon], ...]] -> уходим на 1 уровень вглубь
targetList = decoded[0] as List<dynamic>;
} else {
// У LineString структура [[lat, lon], ...] -> используем как есть
targetList = decoded;
}
points = targetList.map((item) {
final List<dynamic> coords = item as List<dynamic>;
return Point(
(coords[1] as num).toDouble(),
(coords[0] as num).toDouble(),
);
}).toList();
}
} catch (e) {
print("PARSE ERROR for Zone ID ${json['id']}: $e");
}
return Zone(
id: json['id'] ?? 0,
title: json['title'] ?? 'Unknown',
description: json['description'] ?? '',
type: json['type'] ?? '',
isActive: json['isActive'] ?? false,
speedLimit: json['speedLimit'] ?? '',
shapeType: shapeType,
points: points,
);
}
@override
String toString() {
return 'Zone{id: $id, title: $title, type: $type, points: $points}';
}
}

View File

@@ -0,0 +1,7 @@
import 'package:be_happy/domain/entities/map_settings.dart';
abstract class AppSettingsRepository {
Future<MapSettings> getMapSettings();
Future<void> saveMapSettings(MapSettings settings);
}

View File

@@ -0,0 +1,14 @@
import 'dart:ffi';
import '../../core/result.dart';
import '../entities/user_auth_data.dart';
abstract class AuthRepository {
Future<String> login(String phone);
Future<Result<void>> verifyCode(String code, String token);
Future<UserAuthData> refreshToken();
Future<void> logout();
}

View File

@@ -0,0 +1,11 @@
import '../../core/result.dart';
import '../entities/certificate.dart';
abstract class CertificateRepository {
Future<Result<List<Certificate>>> getCertificates();
Future<Result<Map<String, dynamic>>> purchaseCertificate({
required int certificateId,
required int cardId,
});
}

View File

@@ -0,0 +1,3 @@
abstract class LaunchRepository {
Future<bool> isFirstLaunch();
}

View File

@@ -0,0 +1,6 @@
import '../entities/news.dart';
abstract class NewsRepository {
Future<List<NewsEntity>> getNews();
Future<NewsEntity> getNewsById(int id);
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/domain/entities/client_notification.dart';
abstract class NotificationRepository {
/// Устанавливает постоянное SSE-соединение и возвращает поток уведомлений
Stream<ClientNotification> getNotificationsStream();
/// Отменяет уведомление по ID
Future<ClientNotification> cancelNotification(int id);
/// Закрывает SSE-соединение
void closeStream();
}

View File

@@ -0,0 +1,20 @@
import '../../core/result.dart';
import '../entities/payment_card.dart';
abstract class PaymentRepository {
Future<Result<List<PaymentCard>>> getPaymentCards();
Future<Result<void>> addPaymentCard({
required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
});
Future<Result<void>> setMainPaymentCard(int cardId);
Future<Result<void>> removePaymentCard(int cardId);
Future<Result<bool>> activateSubscription(int optionId);
}

View File

@@ -0,0 +1,9 @@
import '../entities/user_auth_data.dart';
abstract class PinRepository {
Future<String?> getSavedPin();
Future<void> savePin(String? pin);
Future<void> removePin();
}

View File

@@ -0,0 +1,12 @@
import 'dart:io';
import '../entities/user_check_flags.dart';
import '../entities/user_profile.dart';
abstract class UserProfileRepository {
Future<UserProfile> getProfile();
Future<UserProfile?> updateProfile(UserProfile profile);
Future<int?> uploadProfilePhoto(File imageFile);
Future<UserCheckFlags?> checkUser();
}

View File

@@ -0,0 +1,55 @@
import 'dart:io';
import 'package:be_happy/domain/entities/active_scooter_order.dart';
import '../../core/result.dart';
import '../entities/point.dart';
import '../entities/scooter.dart';
import '../entities/subscription.dart';
import '../entities/tariff.dart';
import '../entities/scooter_order.dart';
abstract class ScooterRepository {
Future<List<Scooter>> getScooters(List<double> area, int page, int pageSize);
Future<Result<Scooter?>> getScooter(int id);
Future<Result<List<Tariff>>> getAvailableTariffs(int scooterId);
Future<Result<List<Subscription>>> getAvailableSubscriptions();
Future<Result<Subscription>> getSubscriptionById(int id);
Future<Result<List<Subscription>>> getClientSubscriptions();
Future<Result<ScooterOrder>> bookScooter({
required int scooterId,
required int planId,
int? subscriptionId,
int? cardId,
required bool isBalance,
required bool isInsurance,
});
Future<Result<ScooterOrder>> startRide(int orderId);
Future<Result<ScooterOrder>> cancelRide(int orderId);
Future<Result<ScooterOrder>> pauseRide(int orderId);
Future<Result<ScooterOrder>> resumeRide(int orderId);
Future<Result<ScooterOrder>> finishRide(int orderId, List<int> files);
Future<Result<ScooterOrder>> payRide(int orderId);
Future<Result<List<ScooterOrder>>> getClientOrders();
Future<Result<List<int>>> uploadScooterPhotos(List<File> images);
Future<Result<ActiveScooterOrder>> updateScooterOrderData({
required int orderId,
});
Future<Result<ScooterOrder>> payScooterOrderWithPhotos({
required int orderId,
required int? cardId,
required bool isBalance,
});
Future<Result<ScooterOrder>> getScooterOrderById(int id);
Future<Result<List<ScooterOrder>>> getScooterOrderHistory({
int page = 1,
int pageSize = 20,
});
Future<Result<Scooter?>> getScooterByTitle(String title);
Future<Result<List<Point>>> getScooterOrderRouteHistory(int id);
}

View File

@@ -0,0 +1,6 @@
import '../entities/zone.dart';
abstract class ZoneRepository {
Future<List<Zone>> getZones(List<double> area, int page, int pageSize);
}

View File

@@ -0,0 +1,4 @@
abstract class DeviceInfoService {
Future<String?> getSystemId(); //SSAID - Android, IDFV - iOS (Vendor ID - один идентификатор для всех приложений одного разработчика, уникальный для одного устройства)
Future<String> getDeviceModel();
}

View File

@@ -0,0 +1,16 @@
import 'package:be_happy/domain/entities/user_auth_data.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
abstract class SecurityService {
Future<void> saveTokens(UserAuthData data);
Future<void> removeTokens();
Future<String?> getRefreshToken();
Future<String?> getAccessToken();
// Методы для работы с полными номерами карт
Future<void> saveCardFullNumber(int cardId, String fullCardNumber);
Future<String?> getCardFullNumber(int cardId);
Future<void> removeCardFullNumber(int cardId);
Future<void> clearAllCardsNumbers();
// Future<UserAuthData?> getTokens();
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/repositories/payment_repository.dart';
class ActivateSubscriptionUsecase {
final PaymentRepository repository;
ActivateSubscriptionUsecase(this.repository);
Future<Result<bool>> call(int optionId) {
return repository.activateSubscription(optionId);
}
}

View File

@@ -0,0 +1,24 @@
import '../repositories/payment_repository.dart';
import '../../core/result.dart';
class AddPaymentCardUsecase {
final PaymentRepository repository;
AddPaymentCardUsecase(this.repository);
Future<Result<void>> call({
required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
}) {
return repository.addPaymentCard(
cardNumber: cardNumber,
cardHolder: cardHolder,
expiryMonth: expiryMonth,
expiryYear: expiryYear,
cvv: cvv,
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class BookScooterUsecase {
final ScooterRepository repository;
BookScooterUsecase(this.repository);
Future<Result<ScooterOrder>> call({
required int scooterId,
required int planId,
int? subscriptionId,
int? cardId,
required bool isBalance,
required bool isInsurance,
}) {
return repository.bookScooter(
scooterId: scooterId,
planId: planId,
subscriptionId: subscriptionId,
cardId: cardId,
isBalance: isBalance,
isInsurance: isInsurance,
);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import 'package:be_happy/domain/repositories/notification_repository.dart';
class CancelNotificationUseCase {
final NotificationRepository repository;
CancelNotificationUseCase(this.repository);
Future<ClientNotification> call(int id) async {
return await repository.cancelNotification(id);
}
}

View File

@@ -0,0 +1,13 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class CancelRideUsecase {
final ScooterRepository repository;
CancelRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId) {
return repository.cancelRide(orderId);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import '../repositories/auth_repository.dart';
class ChangePinUseCase {
final PinRepository repository;
ChangePinUseCase(this.repository);
Future<void> call({
required String oldPin,
required String newPin,
}) async {
final savedPin = await repository.getSavedPin();
if (savedPin != oldPin) {
throw Exception('Wrong old PIN');
}
if (newPin.length != 6) {
throw Exception('Invalid new PIN');
}
await repository.savePin(newPin);
}
}

View File

@@ -0,0 +1,12 @@
import '../entities/user_check_flags.dart';
import '../repositories/profile_repository.dart';
class CheckUserUseCase {
final UserProfileRepository repository;
CheckUserUseCase(this.repository);
Future<UserCheckFlags?> call() {
return repository.checkUser();
}
}

View File

@@ -0,0 +1,27 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import '../repositories/auth_repository.dart';
class CreatePinUseCase {
final PinRepository repository;
CreatePinUseCase(this.repository);
Future<void> call(String pin) async {
_validate(pin);
final hashed = _hash(pin);
await repository.savePin(hashed);
}
void _validate(String pin) {
if (pin.length != 6) {
throw Exception('PIN must be 6 digits');
}
}
String _hash(String pin) {
// временно просто pin
return pin;
}
}

View File

@@ -0,0 +1,13 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class FinishRideUsecase {
final ScooterRepository repository;
FinishRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId, List<int> files) {
return repository.finishRide(orderId, files);
}
}

View File

@@ -0,0 +1,11 @@
import 'package:be_happy/data/network/geocoding_remote_datasource.dart';
class GetAddressByPointUsecase {
final GeocodingRemoteDataSource dataSource;
GetAddressByPointUsecase(this.dataSource);
Future<String> call(double latitude, double longitude) {
return dataSource.getAddressFromPoint(latitude: latitude, longitude: longitude);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/domain/entities/scooter.dart';
import '../repositories/scooter_repository.dart';
class GetAvailableScootersUsecase {
final ScooterRepository repository;
GetAvailableScootersUsecase(this.repository);
Future<List<Scooter>> call(List<double> area, int page, int pageSize) {
return repository.getScooters(area, page, pageSize);
}
}

View File

@@ -0,0 +1,14 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/subscription.dart';
import '../repositories/scooter_repository.dart';
class GetAvailableSubscriptionsUsecase {
final ScooterRepository repository;
GetAvailableSubscriptionsUsecase(this.repository);
Future<Result<List<Subscription>>> call() {
return repository.getAvailableSubscriptions();
}
}

View File

@@ -0,0 +1,14 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/tariff.dart';
import '../repositories/scooter_repository.dart';
class GetAvailableTariffsUsecase {
final ScooterRepository repository;
GetAvailableTariffsUsecase(this.repository);
Future<Result<List<Tariff>>> call(int scooterId) {
return repository.getAvailableTariffs(scooterId);
}
}

View File

@@ -0,0 +1,15 @@
import '../entities/zone.dart';
import '../repositories/zone_repository.dart';
class GetAvailableZonesUsecase {
final ZoneRepository repository;
GetAvailableZonesUsecase(this.repository);
Future<List<Zone>?> call(List<double> area, int page, int pageSize) {
return repository.getZones(area, page, pageSize);
}
}

View File

@@ -0,0 +1,13 @@
import '../entities/certificate.dart';
import '../repositories/certificate_repository.dart';
import '../../core/result.dart';
class GetCertificatesUsecase {
final CertificateRepository repository;
GetCertificatesUsecase(this.repository);
Future<Result<List<Certificate>>> call() async {
return await repository.getCertificates();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class GetClientOrdersUsecase {
final ScooterRepository repository;
GetClientOrdersUsecase(this.repository);
Future<Result<List<ScooterOrder>>> call() {
return repository.getClientOrders();
}
}

View File

@@ -0,0 +1,20 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/subscription.dart';
import '../repositories/scooter_repository.dart';
class GetClientSubscriptionsUsecase {
final ScooterRepository repository;
GetClientSubscriptionsUsecase(this.repository);
Future<Result<List<Subscription>>> call() {
return repository.getClientSubscriptions();
}
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/domain/entities/map_settings.dart';
import 'package:be_happy/domain/repositories/app_settings_repository.dart';
class GetMapSettingsUsecase {
AppSettingsRepository repository;
GetMapSettingsUsecase(this.repository);
Future<MapSettings> call() {
return repository.getMapSettings();
}
}

View File

@@ -0,0 +1,12 @@
import '../entities/news.dart';
import '../repositories/news_repository.dart';
class GetNewsByIdUsecase {
final NewsRepository repository;
GetNewsByIdUsecase(this.repository);
Future<NewsEntity> call(int id) {
return repository.getNewsById(id);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import 'package:be_happy/domain/repositories/notification_repository.dart';
class GetNotificationsStreamUseCase {
final NotificationRepository repository;
GetNotificationsStreamUseCase(this.repository);
Stream<ClientNotification> call() {
return repository.getNotificationsStream();
}
}

View File

@@ -0,0 +1,31 @@
import 'package:be_happy/domain/service/security_service.dart';
import '../entities/payment_card.dart';
import '../repositories/payment_repository.dart';
import '../../core/result.dart';
class GetPaymentCardsUsecase {
final PaymentRepository repository;
final SecurityService securityService;
GetPaymentCardsUsecase(this.repository, this.securityService);
Future<Result<List<PaymentCard>>> call() async {
final result = await repository.getPaymentCards();
if (result is Failure) {
return result;
}
final cards = (result as Success).data as List<PaymentCard>;
// Для каждой карты получаем полный номер из локального хранилища
/*final cardsWithFullNumbers = <PaymentCard>[];
for (final card in cards) {
final fullNumber = await securityService.getCardFullNumber(card.id);
cardsWithFullNumbers.add(card.copyWith(fullCardNumber: fullNumber));
}*/
return Success(cards);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:be_happy/data/network/geocoding_remote_datasource.dart';
import 'package:yandex_mapkit/yandex_mapkit.dart';
class GetPedestrianRoutesUsecase {
final GeocodingRemoteDataSource dataSource;
GetPedestrianRoutesUsecase(this.dataSource);
Future<List<MasstransitRoute>?> call(Point userPosition, Point targetPosition) {
return dataSource.getPedestrianRoutes(userPosition, targetPosition);
}
}

View File

@@ -0,0 +1,13 @@
import '../entities/user_profile.dart';
import '../repositories/profile_repository.dart';
class GetProfileUseCase {
final UserProfileRepository repository;
GetProfileUseCase(this.repository);
Future<UserProfile> call() {
return repository.getProfile();
}
}

View File

@@ -0,0 +1,14 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter.dart';
import '../repositories/scooter_repository.dart';
class GetScooterByTitleUsecase {
final ScooterRepository repository;
GetScooterByTitleUsecase(this.repository);
Future<Result<Scooter?>> call(String title) {
return repository.getScooterByTitle(title);
}
}

View File

@@ -0,0 +1,13 @@
import 'package:be_happy/core/result.dart';
import '../entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class GetScooterOrderByIdUsecase {
final ScooterRepository repository;
GetScooterOrderByIdUsecase(this.repository);
Future<Result<ScooterOrder>> call(int id) {
return repository.getScooterOrderById(id);
}
}

View File

@@ -0,0 +1,19 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter_order.dart';
import 'package:be_happy/domain/repositories/scooter_repository.dart';
class GetScooterOrderHistoryUsecase {
final ScooterRepository _repository;
GetScooterOrderHistoryUsecase(this._repository);
Future<Result<List<ScooterOrder>>> call({
int page = 1,
int pageSize = 20,
}) async {
return await _repository.getScooterOrderHistory(
page: page,
pageSize: pageSize,
);
}
}

View File

@@ -0,0 +1,13 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/point.dart';
import 'package:be_happy/domain/repositories/scooter_repository.dart';
class GetScooterOrderRouteHistoryUsecase {
final ScooterRepository _repository;
GetScooterOrderRouteHistoryUsecase(this._repository);
Future<Result<List<Point>>> call(int id) async {
return await _repository.getScooterOrderRouteHistory(id);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter.dart';
import '../repositories/scooter_repository.dart';
class GetScooterUsecase {
final ScooterRepository repository;
GetScooterUsecase(this.repository);
Future<Result<Scooter?>> call(int id) {
return repository.getScooter(id);
}
}

View File

@@ -0,0 +1,14 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/subscription.dart';
import '../repositories/scooter_repository.dart';
class GetSubscriptionByIdUsecase {
final ScooterRepository repository;
GetSubscriptionByIdUsecase(this.repository);
Future<Result<Subscription>> call(int id) {
return repository.getSubscriptionById(id);
}
}

View File

@@ -0,0 +1,17 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import '../repositories/auth_repository.dart';
class IsPinSetUsecase {
final PinRepository repository;
IsPinSetUsecase(this.repository);
Future<bool> call() async {
if (await repository.getSavedPin() == null) {
return false;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
import '../repositories/auth_repository.dart';
class LoginUseCase {
final AuthRepository _repository;
LoginUseCase(this._repository);
Future<String> execute(String phone) async {
return await _repository.login(phone);
}
}

Some files were not shown because too many files have changed in this diff Show More