product availability
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
This commit is contained in:
parent
efe1a8f898
commit
b5f6a78845
1 changed files with 206 additions and 0 deletions
206
mnemo_cards_backend/lib/packs/product_availability_manager.dart
Normal file
206
mnemo_cards_backend/lib/packs/product_availability_manager.dart
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
import 'package:mnemo_cards_backend/database/database.dart';
|
||||||
|
import 'package:mnemo_cards_backend/packs/pack_manager.dart';
|
||||||
|
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
|
||||||
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
|
|
||||||
|
/// Менеджер для проверки доступности и управления доступом к продуктам (пакам, подпискам) пользователю
|
||||||
|
@lazySingleton
|
||||||
|
class ProductAvailabilityManager {
|
||||||
|
final PackManager _packManager;
|
||||||
|
final AppDatabase _db;
|
||||||
|
final Set<String> _publicPackIds;
|
||||||
|
|
||||||
|
ProductAvailabilityManager(
|
||||||
|
this._packManager,
|
||||||
|
this._db, {
|
||||||
|
Set<String>? publicPackIds,
|
||||||
|
}) : _publicPackIds = publicPackIds ?? const {'10'};
|
||||||
|
|
||||||
|
/// Проверить доступность пакета для пользователя
|
||||||
|
///
|
||||||
|
/// Возвращает true, если пакет доступен пользователю по одной из причин:
|
||||||
|
/// - Пакет является публичным (в списке publicPackIds)
|
||||||
|
/// - Пользователь является администратором
|
||||||
|
/// - У пользователя есть прямой доступ к пакету (через user_packs)
|
||||||
|
/// - У пользователя есть активная подписка с фичей SubscriptionFeatureEnum.packs
|
||||||
|
///
|
||||||
|
/// Параметры:
|
||||||
|
/// - [packId] - ID пакета для проверки
|
||||||
|
/// - [user] - пользователь (может быть null для неавторизованных)
|
||||||
|
///
|
||||||
|
/// Возвращает:
|
||||||
|
/// - true если пакет доступен
|
||||||
|
/// - false если пакет недоступен или не существует
|
||||||
|
Future<bool> isPackAvailable({
|
||||||
|
required String packId,
|
||||||
|
required UserModel? user,
|
||||||
|
}) async {
|
||||||
|
// Проверить существование и статус пакета
|
||||||
|
final pack = await _packManager.getPack(packId);
|
||||||
|
if (pack == null || !pack.enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Публичные паки доступны всем
|
||||||
|
if (_publicPackIds.contains(packId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Администраторы имеют доступ ко всем пакетам
|
||||||
|
if (user?.admin == true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для неавторизованных пользователей доступны только публичные паки
|
||||||
|
if (user == null || user.id == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверить прямой доступ через user_packs
|
||||||
|
final hasDirectAccess = await _db.userDao.hasPackAccess(user.id!, packId);
|
||||||
|
if (hasDirectAccess) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверить доступ через подписку
|
||||||
|
if (user.subscriptionModel != null && user.subscriptionModel!.isActive) {
|
||||||
|
final hasSubscriptionAccess = user.subscriptionModel!.features.contains(
|
||||||
|
SubscriptionFeatureEnum.packs,
|
||||||
|
);
|
||||||
|
if (hasSubscriptionAccess) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверить доступность продукта для пользователя
|
||||||
|
///
|
||||||
|
/// Универсальный метод для проверки доступности любого типа продукта
|
||||||
|
///
|
||||||
|
/// Параметры:
|
||||||
|
/// - [productId] - ID продукта
|
||||||
|
/// - [productType] - тип продукта (pack, subscription)
|
||||||
|
/// - [user] - пользователь (может быть null для неавторизованных)
|
||||||
|
///
|
||||||
|
/// Возвращает:
|
||||||
|
/// - true если продукт доступен
|
||||||
|
/// - false если продукт недоступен
|
||||||
|
Future<bool> isProductAvailable({
|
||||||
|
required String productId,
|
||||||
|
required MnemoCardsProductType productType,
|
||||||
|
required UserModel? user,
|
||||||
|
}) async {
|
||||||
|
switch (productType) {
|
||||||
|
case MnemoCardsProductType.pack:
|
||||||
|
return await isPackAvailable(packId: productId, user: user);
|
||||||
|
case MnemoCardsProductType.subscription:
|
||||||
|
// Подписки не проверяются на доступность, они являются продуктами для покупки
|
||||||
|
// Но можно проверить, есть ли у пользователя уже активная подписка
|
||||||
|
if (user?.subscriptionModel != null &&
|
||||||
|
user!.subscriptionModel!.isActive) {
|
||||||
|
// Пользователь уже имеет активную подписку
|
||||||
|
// В зависимости от бизнес-логики, можно вернуть true или false
|
||||||
|
return false; // По умолчанию, если есть активная подписка, покупка новой недоступна
|
||||||
|
}
|
||||||
|
return true; // Подписка доступна для покупки
|
||||||
|
case MnemoCardsProductType.discount:
|
||||||
|
case MnemoCardsProductType.game:
|
||||||
|
case MnemoCardsProductType.unknown:
|
||||||
|
// Для discount, game и unknown типов проверка доступности не применима
|
||||||
|
// Они являются продуктами для покупки или служебными типами
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверить, является ли пакет публичным
|
||||||
|
bool isPublicPack(String packId) {
|
||||||
|
return _publicPackIds.contains(packId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Получить список публичных пакетов
|
||||||
|
Set<String> get publicPackIds => _publicPackIds;
|
||||||
|
|
||||||
|
/// Предоставить пользователю доступ к пакету
|
||||||
|
///
|
||||||
|
/// Параметры:
|
||||||
|
/// - [userId] - ID пользователя
|
||||||
|
/// - [packId] - ID пакета
|
||||||
|
/// - [grantType] - тип предоставления доступа ('purchase', 'promo_code', 'free' и т.д.)
|
||||||
|
///
|
||||||
|
/// Метод автоматически проверяет существование пакета перед предоставлением доступа.
|
||||||
|
/// Если пакет не существует или отключен, выбрасывается исключение.
|
||||||
|
Future<void> grantPackAccess({
|
||||||
|
required String userId,
|
||||||
|
required String packId,
|
||||||
|
String grantType = 'purchase',
|
||||||
|
}) async {
|
||||||
|
// Проверить существование и статус пакета
|
||||||
|
final pack = await _packManager.getPack(packId);
|
||||||
|
if (pack == null) {
|
||||||
|
throw ArgumentError('Pack not found: $packId');
|
||||||
|
}
|
||||||
|
if (!pack.enabled) {
|
||||||
|
throw ArgumentError('Pack is disabled: $packId');
|
||||||
|
}
|
||||||
|
|
||||||
|
await _db.userDao.grantPackAccess(
|
||||||
|
userId: userId,
|
||||||
|
packId: packId,
|
||||||
|
grantType: grantType,
|
||||||
|
);
|
||||||
|
log('Granted access to pack $packId for user $userId (type: $grantType)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отозвать доступ пользователя к пакету
|
||||||
|
///
|
||||||
|
/// Параметры:
|
||||||
|
/// - [userId] - ID пользователя
|
||||||
|
/// - [packId] - ID пакета
|
||||||
|
Future<void> revokePackAccess({
|
||||||
|
required String userId,
|
||||||
|
required String packId,
|
||||||
|
}) async {
|
||||||
|
await _db.userDao.revokePackAccess(userId, packId);
|
||||||
|
log('Revoked access to pack $packId for user $userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Предоставить пользователю доступ к продукту
|
||||||
|
///
|
||||||
|
/// Универсальный метод для предоставления доступа к любому типу продукта
|
||||||
|
///
|
||||||
|
/// Параметры:
|
||||||
|
/// - [userId] - ID пользователя
|
||||||
|
/// - [product] - продукт (DTO)
|
||||||
|
/// - [grantType] - тип предоставления доступа
|
||||||
|
Future<void> grantProductAccess({
|
||||||
|
required String userId,
|
||||||
|
required MnemoCardsProductDto product,
|
||||||
|
String grantType = 'purchase',
|
||||||
|
}) async {
|
||||||
|
switch (product.type) {
|
||||||
|
case MnemoCardsProductType.pack:
|
||||||
|
if (product.id == null) {
|
||||||
|
throw ArgumentError('Product ID is required for pack');
|
||||||
|
}
|
||||||
|
await grantPackAccess(
|
||||||
|
userId: userId,
|
||||||
|
packId: product.id!,
|
||||||
|
grantType: grantType,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MnemoCardsProductType.subscription:
|
||||||
|
case MnemoCardsProductType.discount:
|
||||||
|
case MnemoCardsProductType.game:
|
||||||
|
case MnemoCardsProductType.unknown:
|
||||||
|
// Для этих типов продуктов логика предоставления доступа находится в других менеджерах
|
||||||
|
// (например, SubscriptionManager для подписок)
|
||||||
|
throw ArgumentError(
|
||||||
|
'Granting access for product type ${product.type} is not supported by ProductAvailabilityManager',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue