From c638b6ee4f441c463c1c32d27d2021cddfa2c4cb Mon Sep 17 00:00:00 2001 From: "Sanders (aider)" Date: Mon, 9 Jun 2025 22:23:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=81=D0=BD=D0=BE=D0=B2=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D1=8F=D1=81=D1=8C=20=D0=BD=D0=B0=20=D0=BF=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D1=82=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=D1=85,=20=D1=8F=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BB=D0=B0?= =?UTF-8?q?=D0=B3=D0=B0=D1=8E=20=D1=81=D0=BB=D0=B5=D0=B4=D1=83=D1=8E=D1=89?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: добавить поддержку пользователей в приложение учета бюджета --- .../hive_category_repository.dart | 24 +++++++ .../repositories/hive_user_repository.dart | 53 +++++++++++++++ .../interfaces/icategory_repository.dart | 11 ++++ .../interfaces/iuser_repository.dart | 23 +++++++ lib/models/category.dart | 8 +++ lib/models/user.dart | 55 ++++++++++++++++ lib/services/user_service.dart | 64 +++++++++++++++++++ 7 files changed, 238 insertions(+) create mode 100644 lib/data/repositories/hive_user_repository.dart create mode 100644 lib/data/repositories/interfaces/iuser_repository.dart create mode 100644 lib/models/user.dart create mode 100644 lib/services/user_service.dart diff --git a/lib/data/repositories/hive_category_repository.dart b/lib/data/repositories/hive_category_repository.dart index 0c2f33b..dc4d876 100644 --- a/lib/data/repositories/hive_category_repository.dart +++ b/lib/data/repositories/hive_category_repository.dart @@ -41,4 +41,28 @@ class HiveCategoryRepository implements ICategoryRepository { Future> getExpenseCategories() async { return _box.values.where((c) => !c.isIncome).toList(); } + + // Новые методы для работы с пользователями + + @override + Future> getAllByUser(String userId) async { + // Фильтруем все категории по userId + return _box.values.where((c) => c.userId == userId).toList(); + } + + @override + Future> getIncomeCategoriesByUser(String userId) async { + // Получаем только категории доходов конкретного пользователя + return _box.values + .where((c) => c.userId == userId && c.isIncome) + .toList(); + } + + @override + Future> getExpenseCategoriesByUser(String userId) async { + // Получаем только категории расходов конкретного пользователя + return _box.values + .where((c) => c.userId == userId && !c.isIncome) + .toList(); + } } diff --git a/lib/data/repositories/hive_user_repository.dart b/lib/data/repositories/hive_user_repository.dart new file mode 100644 index 0000000..120ea4e --- /dev/null +++ b/lib/data/repositories/hive_user_repository.dart @@ -0,0 +1,53 @@ +import 'package:hive_ce/hive.dart'; +import '/data/repositories/interfaces/iuser_repository.dart'; +import '/models/user.dart'; + +/// Реализация репозитория пользователей с использованием Hive +/// Hive - это быстрая NoSQL база данных для Flutter +class HiveUserRepository implements IUserRepository { + final Box _box; // Hive box для хранения пользователей + + /// Конструктор принимает готовый Hive box + HiveUserRepository(this._box); + + @override + Future> getAll() async { + // Возвращаем все значения из box как список + return _box.values.toList(); + } + + @override + Future getById(String id) async { + // Получаем пользователя по ключу (id) + return _box.get(id); + } + + @override + Future getByEmail(String email) async { + // Ищем пользователя по email среди всех пользователей + try { + return _box.values.firstWhere((user) => user.email == email); + } catch (e) { + // Если пользователь не найден, возвращаем null + return null; + } + } + + @override + Future add(User user) async { + // Сохраняем пользователя с его id как ключом + await _box.put(user.id, user); + } + + @override + Future update(User user) async { + // Обновление = перезапись существующего пользователя + await add(user); + } + + @override + Future delete(String id) async { + // Удаляем пользователя по id + await _box.delete(id); + } +} diff --git a/lib/data/repositories/interfaces/icategory_repository.dart b/lib/data/repositories/interfaces/icategory_repository.dart index 1187f38..ec41521 100644 --- a/lib/data/repositories/interfaces/icategory_repository.dart +++ b/lib/data/repositories/interfaces/icategory_repository.dart @@ -1,6 +1,7 @@ import '/models/category.dart'; abstract class ICategoryRepository { + // Старые методы для обратной совместимости Future> getAll(); Future getById(String id); Future add(Category category); @@ -8,4 +9,14 @@ abstract class ICategoryRepository { Future delete(String id); Future> getIncomeCategories(); Future> getExpenseCategories(); + + // Новые методы для работы с пользователями + /// Получить все категории конкретного пользователя + Future> getAllByUser(String userId); + + /// Получить категории доходов конкретного пользователя + Future> getIncomeCategoriesByUser(String userId); + + /// Получить категории расходов конкретного пользователя + Future> getExpenseCategoriesByUser(String userId); } diff --git a/lib/data/repositories/interfaces/iuser_repository.dart b/lib/data/repositories/interfaces/iuser_repository.dart new file mode 100644 index 0000000..4fab276 --- /dev/null +++ b/lib/data/repositories/interfaces/iuser_repository.dart @@ -0,0 +1,23 @@ +import '/models/user.dart'; + +/// Интерфейс для работы с пользователями в базе данных +/// Определяет основные операции CRUD (Create, Read, Update, Delete) +abstract class IUserRepository { + /// Получить всех пользователей + Future> getAll(); + + /// Получить пользователя по ID + Future getById(String id); + + /// Получить пользователя по email (для входа в систему) + Future getByEmail(String email); + + /// Добавить нового пользователя + Future add(User user); + + /// Обновить существующего пользователя + Future update(User user); + + /// Удалить пользователя + Future delete(String id); +} diff --git a/lib/models/category.dart b/lib/models/category.dart index 447c14a..80dad6e 100644 --- a/lib/models/category.dart +++ b/lib/models/category.dart @@ -31,6 +31,11 @@ class Category { /// false - расход (например покупки) final bool isIncome; + @HiveField(5) + /// Идентификатор пользователя, которому принадлежит эта категория + /// Это позволяет разделять категории между разными пользователями + final String userId; + /// Конструктор с обязательными параметрами Category({ String? id, @@ -38,6 +43,7 @@ class Category { required this.color, required this.icon, required this.isIncome, + required this.userId, // Теперь userId обязательный параметр }) : id = id ?? IdGenerator.generateId(); /// Метод для преобразования объекта в Map (полезно для работы с БД) @@ -48,6 +54,7 @@ class Category { 'color': color.value, // Сохраняем только значение цвета 'icon': icon.codePoint, 'isIncome': isIncome, + 'userId': userId, // Добавляем userId в Map }; } @@ -59,6 +66,7 @@ class Category { color: Color(map['color']), icon: IconData(map['icon'], fontFamily: 'MaterialIcons'), isIncome: map['isIncome'], + userId: map['userId'], // Добавляем userId при создании из Map ); } } diff --git a/lib/models/user.dart b/lib/models/user.dart new file mode 100644 index 0000000..a71c62a --- /dev/null +++ b/lib/models/user.dart @@ -0,0 +1,55 @@ +import 'package:hive_ce/hive.dart'; +import '../utils/id_generator.dart'; + +// Указываем Hive, что это модель для хранения в базе данных +// typeId: 3 - уникальный идентификатор типа для Hive (у нас уже есть 0,1,2) +part 'user.g.dart'; + +@HiveType(typeId: 3) +/// Модель пользователя приложения +/// Содержит основную информацию для идентификации пользователя +class User { + /// Уникальный идентификатор пользователя + @HiveField(0) // Поле 0 в Hive - первое поле модели + final String id; + + /// Имя пользователя для отображения в интерфейсе + @HiveField(1) // Поле 1 в Hive - второе поле модели + final String name; + + /// Email пользователя (может использоваться для входа в будущем) + @HiveField(2) // Поле 2 в Hive - третье поле модели + final String email; + + /// Конструктор пользователя + /// id генерируется автоматически, если не передан + User({ + String? id, // Опциональный параметр - если null, сгенерируется автоматически + required this.name, // Обязательный параметр + required this.email, // Обязательный параметр + }) : id = id ?? IdGenerator.generateId(); // Если id не передан, генерируем новый + + /// Преобразование объекта в Map для сохранения в JSON или передачи по сети + Map toMap() { + return { + 'id': id, + 'name': name, + 'email': email, + }; + } + + /// Создание объекта User из Map (например, при загрузке из JSON) + factory User.fromMap(Map map) { + return User( + id: map['id'], + name: map['name'], + email: map['email'], + ); + } + + /// Переопределяем toString для удобного отображения в логах + @override + String toString() { + return 'User(id: $id, name: $name, email: $email)'; + } +} diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart new file mode 100644 index 0000000..8b9d8f8 --- /dev/null +++ b/lib/services/user_service.dart @@ -0,0 +1,64 @@ +import 'package:flutter/foundation.dart'; +import 'package:hive_ce/hive.dart'; +import '/models/user.dart'; +import '/data/repositories/interfaces/iuser_repository.dart'; + +/// Сервис для управления текущим пользователем приложения +/// Использует ChangeNotifier для уведомления UI об изменениях +class UserService extends ChangeNotifier { + // Ключ для сохранения ID текущего пользователя в настройках + static const String _currentUserKey = 'currentUserId'; + + late final Box _settingsBox; // Box для хранения настроек + final IUserRepository _userRepository; // Репозиторий для работы с пользователями + + User? _currentUser; // Текущий активный пользователь + + /// Геттер для получения текущего пользователя + User? get currentUser => _currentUser; + + /// Проверяем, есть ли активный пользователь + bool get hasCurrentUser => _currentUser != null; + + /// Конструктор сервиса + UserService(this._userRepository) { + _settingsBox = Hive.box('settings'); + _loadCurrentUser(); // Загружаем сохраненного пользователя при запуске + } + + /// Загружаем текущего пользователя из настроек + Future _loadCurrentUser() async { + final userId = _settingsBox.get(_currentUserKey); + if (userId != null) { + _currentUser = await _userRepository.getById(userId); + notifyListeners(); // Уведомляем UI об изменении + } + } + + /// Устанавливаем текущего пользователя + Future setCurrentUser(User user) async { + _currentUser = user; + // Сохраняем ID пользователя в настройках для следующего запуска + await _settingsBox.put(_currentUserKey, user.id); + notifyListeners(); // Уведомляем UI об изменении + } + + /// Выход из аккаунта (сброс текущего пользователя) + Future logout() async { + _currentUser = null; + await _settingsBox.delete(_currentUserKey); + notifyListeners(); // Уведомляем UI об изменении + } + + /// Создание нового пользователя и установка его как текущего + Future createAndSetUser(String name, String email) async { + final user = User(name: name, email: email); + await _userRepository.add(user); + await setCurrentUser(user); + } + + /// Получение всех пользователей (для выбора) + Future> getAllUsers() async { + return await _userRepository.getAll(); + } +}