new project stable version

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

View File

@@ -0,0 +1,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);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import '../repositories/auth_repository.dart';
class LogoutUseCase {
final AuthRepository _authRepository;
final PinRepository _pinRepository;
LogoutUseCase(this._authRepository, this._pinRepository);
Future<void> call() async {
await _authRepository.logout();
await _pinRepository.removePin();
}
}

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 PauseRideUsecase {
final ScooterRepository repository;
PauseRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId) {
return repository.pauseRide(orderId);
}
}

View File

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

View File

@@ -0,0 +1,21 @@
import 'package:be_happy/core/result.dart';
import '../entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class PayScooterOrderWithPhotosUsecase {
final ScooterRepository repository;
PayScooterOrderWithPhotosUsecase(this.repository);
Future<Result<ScooterOrder>> call({
required int orderId,
required int cardId,
required bool isBalance,
}) {
return repository.payScooterOrderWithPhotos(
orderId: orderId,
cardId: cardId,
isBalance: isBalance,
);
}
}

View File

@@ -0,0 +1,19 @@
import '../entities/certificate.dart';
import '../repositories/certificate_repository.dart';
import '../../core/result.dart';
class PurchaseCertificateUsecase {
final CertificateRepository repository;
PurchaseCertificateUsecase(this.repository);
Future<Result<Map<String, dynamic>>> call({
required int certificateId,
required int cardId,
}) async {
return await repository.purchaseCertificate(
certificateId: certificateId,
cardId: cardId,
);
}
}

View File

@@ -0,0 +1,12 @@
import '../entities/user_auth_data.dart';
import '../repositories/auth_repository.dart';
class RefreshTokenUseCase {
final AuthRepository _repository;
RefreshTokenUseCase(this._repository);
Future<UserAuthData> execute() async {
return await _repository.refreshToken();
}
}

View File

@@ -0,0 +1,12 @@
import '../repositories/payment_repository.dart';
import '../../core/result.dart';
class RemovePaymentCardUsecase {
final PaymentRepository repository;
RemovePaymentCardUsecase(this.repository);
Future<Result<void>> call(int cardId) {
return repository.removePaymentCard(cardId);
}
}

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 ResumeRideUsecase {
final ScooterRepository repository;
ResumeRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId) {
return repository.resumeRide(orderId);
}
}

View File

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

View File

@@ -0,0 +1,12 @@
import '../repositories/payment_repository.dart';
import '../../core/result.dart';
class SetMainPaymentCardUsecase {
final PaymentRepository repository;
SetMainPaymentCardUsecase(this.repository);
Future<Result<void>> call(int cardId) {
return repository.setMainPaymentCard(cardId);
}
}

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 StartRideUsecase {
final ScooterRepository repository;
StartRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId) {
return repository.startRide(orderId);
}
}

View File

@@ -0,0 +1,12 @@
import '../entities/user_profile.dart';
import '../repositories/profile_repository.dart';
class UpdateProfileUseCase {
final UserProfileRepository repository;
UpdateProfileUseCase(this.repository);
Future<UserProfile?> call(UserProfile profile) {
return repository.updateProfile(profile);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/active_scooter_order.dart';
import '../entities/scooter_order.dart';
import '../repositories/scooter_repository.dart';
class UpdateScooterOrderDataUsecase {
final ScooterRepository repository;
UpdateScooterOrderDataUsecase(this.repository);
Future<Result<ActiveScooterOrder>> call({
required int orderId,
}) {
return repository.updateScooterOrderData(
orderId: orderId,
);
}
}

View File

@@ -0,0 +1,12 @@
import 'dart:io';
import '../repositories/profile_repository.dart';
class UploadProfilePhotoUsecase {
final UserProfileRepository repository;
UploadProfilePhotoUsecase(this.repository);
Future<int?> call(File imageFile) {
return repository.uploadProfilePhoto(imageFile);
}
}

View File

@@ -0,0 +1,13 @@
import 'dart:io';
import 'package:be_happy/core/result.dart';
import '../repositories/scooter_repository.dart';
class UploadScooterPhotosUsecase {
final ScooterRepository repository;
UploadScooterPhotosUsecase(this.repository);
Future<Result<List<int>>> call(List<File> images) {
return repository.uploadScooterPhotos(images);
}
}

View File

@@ -0,0 +1,14 @@
import 'package:be_happy/core/result.dart';
import '../entities/user_auth_data.dart';
import '../repositories/auth_repository.dart';
class VerifyCodeUseCase {
final AuthRepository _repository;
VerifyCodeUseCase(this._repository);
Future<Result<void>> execute(String code, String token) {
return _repository.verifyCode(code, token);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:be_happy/domain/repositories/pin_repository.dart';
import '../repositories/auth_repository.dart';
class VerifyPinUseCase {
final PinRepository repository;
VerifyPinUseCase(this.repository);
Future<bool> call(String enteredPin) async {
final savedPin = await repository.getSavedPin();
return enteredPin == savedPin;
}
}