mnemo_cards/mnemo_cards_backend/lib/database/daos/discount_dao.dart

253 lines
8.9 KiB
Dart
Raw Normal View History

2025-12-13 13:27:05 +00:00
import 'package:drift/drift.dart';
2025-12-13 23:35:14 +00:00
import 'package:drift_postgres/drift_postgres.dart';
2025-12-13 13:27:05 +00:00
import '../database.dart';
import '../tables/discounts.dart';
import '../tables/users.dart';
part 'discount_dao.g.dart';
@DriftAccessor(tables: [DiscountCampaigns, Discounts, DiscountUserDatas])
2025-12-20 18:26:15 +00:00
class DiscountDao extends DatabaseAccessor<AppDatabase>
with _$DiscountDaoMixin {
2025-12-13 13:27:05 +00:00
DiscountDao(super.db);
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
// ==================== DiscountCampaigns ====================
2025-12-20 18:26:15 +00:00
2025-12-14 20:42:38 +00:00
/// Получить кампанию по ID (только активные)
2025-12-13 20:55:50 +00:00
Future<DiscountCampaign?> getCampaignById(String id) {
2025-12-14 20:42:38 +00:00
return (select(db.discountCampaigns)
2025-12-20 18:26:15 +00:00
..where((c) => c.id.equals(id) & c.isDeleted.equals(false)))
.getSingleOrNull();
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Получить все активные кампании
Future<List<DiscountCampaign>> getActiveCampaigns() {
2025-12-13 23:35:14 +00:00
final now = PgDateTime(DateTime.now());
2025-12-13 13:27:05 +00:00
return (select(db.discountCampaigns)
2025-12-20 18:26:15 +00:00
..where((c) => c.status.equals('active'))
..where((c) => c.start.isSmallerOrEqualValue(now))
..where((c) => c.finish.isBiggerOrEqualValue(now))
..where((c) => c.isDeleted.equals(false)))
.get();
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Создать кампанию
2025-12-13 20:55:50 +00:00
Future<String> createCampaign(DiscountCampaignsCompanion campaign) async {
final inserted = await into(db.discountCampaigns).insertReturning(campaign);
return inserted.id;
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Обновить кампанию
Future<bool> updateCampaign(DiscountCampaign campaign) {
return update(db.discountCampaigns).replace(campaign);
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Обновить статус кампании
2025-12-13 20:55:50 +00:00
Future<void> updateCampaignStatus(String campaignId, String status) {
2025-12-20 18:26:15 +00:00
return (update(
db.discountCampaigns,
)..where((c) => c.id.equals(campaignId))).write(
DiscountCampaignsCompanion(
status: Value(status),
updatedAt: Value(PgDateTime(DateTime.now())),
),
);
}
2025-12-13 14:48:00 +00:00
/// Получить кампании по статусу
Future<List<DiscountCampaign>> getCampaignsByStatus(String status) {
return (select(db.discountCampaigns)
2025-12-20 18:26:15 +00:00
..where((c) => c.status.equals(status))
..where((c) => c.isDeleted.equals(false)))
.get();
2025-12-13 14:48:00 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Получить кампании по статусам (несколько)
Future<List<DiscountCampaign>> getCampaignsByStatuses(List<String> statuses) {
return (select(db.discountCampaigns)
2025-12-20 18:26:15 +00:00
..where((c) => c.status.isIn(statuses))
..where((c) => c.isDeleted.equals(false)))
.get();
2025-12-13 14:48:00 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Получить все кампании
Future<List<DiscountCampaign>> getAllCampaigns() {
2025-12-20 18:26:15 +00:00
return (select(
db.discountCampaigns,
)..where((c) => c.isDeleted.equals(false))).get();
2025-12-13 14:48:00 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Удалить кампанию (soft delete)
2025-12-14 21:10:06 +00:00
Future<void> deleteCampaign(String campaignId) async {
2025-12-20 18:26:15 +00:00
await (update(
db.discountCampaigns,
)..where((c) => c.id.equals(campaignId))).write(
DiscountCampaignsCompanion(
isDeleted: const Value(true),
deletedAt: Value(PgDateTime(DateTime.now())),
updatedAt: Value(PgDateTime(DateTime.now())),
),
);
}
2025-12-14 21:10:06 +00:00
/// Удалить кампанию (soft delete) - алиас для совместимости
Future<void> softDeleteCampaign(String campaignId) {
return deleteCampaign(campaignId);
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
// ==================== Discounts ====================
2025-12-20 18:26:15 +00:00
2025-12-14 20:42:38 +00:00
/// Получить скидку по ID (только активные)
2025-12-13 20:55:50 +00:00
Future<Discount?> getDiscountById(String id) {
2025-12-14 20:42:38 +00:00
return (select(db.discounts)
2025-12-20 18:26:15 +00:00
..where((d) => d.id.equals(id) & d.isDeleted.equals(false)))
.getSingleOrNull();
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-14 20:42:38 +00:00
/// Получить скидки кампании (только активные)
2025-12-13 20:55:50 +00:00
Future<List<Discount>> getDiscountsByCampaignId(String campaignId) {
2025-12-13 13:27:05 +00:00
return (select(db.discounts)
2025-12-20 18:26:15 +00:00
..where((d) => d.campaignId.equals(campaignId))
..where((d) => d.isDeleted.equals(false)))
.get();
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-14 20:42:38 +00:00
/// Удалить скидку (soft delete)
Future<void> softDeleteDiscount(String discountId) {
2025-12-20 18:26:15 +00:00
return (update(db.discounts)..where((d) => d.id.equals(discountId))).write(
DiscountsCompanion(
isDeleted: const Value(true),
deletedAt: Value(PgDateTime(DateTime.now())),
updatedAt: Value(PgDateTime(DateTime.now())),
),
);
}
2025-12-13 13:27:05 +00:00
/// Создать скидку
2025-12-13 20:55:50 +00:00
Future<String> createDiscount(DiscountsCompanion discount) async {
final inserted = await into(db.discounts).insertReturning(discount);
return inserted.id;
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Обновить скидку
Future<bool> updateDiscount(Discount discount) {
return update(db.discounts).replace(discount);
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
// ==================== DiscountUserDatas ====================
2025-12-20 18:26:15 +00:00
2025-12-14 20:42:38 +00:00
/// Получить скидки пользователя (только активные)
2025-12-13 20:55:50 +00:00
Future<List<Discount>> getUserDiscounts(String userId) async {
2025-12-13 13:27:05 +00:00
final query = select(db.discounts).join([
innerJoin(
db.discountUserDatas,
db.discountUserDatas.discountId.equalsExp(db.discounts.id) &
2025-12-20 18:26:15 +00:00
db.discountUserDatas.userId.equals(userId),
2025-12-13 13:27:05 +00:00
),
2025-12-20 18:26:15 +00:00
])..where(db.discounts.isDeleted.equals(false));
2025-12-13 13:27:05 +00:00
return query.map((row) => row.readTable(db.discounts)).get();
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Дать пользователю доступ к скидке
2025-12-13 20:55:50 +00:00
Future<void> grantDiscountToUser(String userId, String discountId) async {
2025-12-13 13:27:05 +00:00
await into(db.discountUserDatas).insert(
2025-12-20 18:26:15 +00:00
DiscountUserDatasCompanion.insert(userId: userId, discountId: discountId),
2025-12-13 13:27:05 +00:00
mode: InsertMode.insertOrIgnore,
);
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Отозвать скидку у пользователя
2025-12-13 20:55:50 +00:00
Future<void> revokeDiscountFromUser(String userId, String discountId) async {
2025-12-20 18:26:15 +00:00
await (delete(db.discountUserDatas)..where(
(dud) =>
dud.userId.equals(userId) & dud.discountId.equals(discountId),
))
.go();
2025-12-13 13:27:05 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Отозвать несколько скидок у пользователя
2025-12-20 18:26:15 +00:00
Future<void> revokeDiscountsFromUser(
String userId,
List<String> discountIds,
) async {
2025-12-13 14:48:00 +00:00
if (discountIds.isEmpty) return;
2025-12-20 18:26:15 +00:00
await (delete(db.discountUserDatas)..where(
(dud) => dud.userId.equals(userId) & dud.discountId.isIn(discountIds),
))
.go();
2025-12-13 14:48:00 +00:00
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Дать пользователю доступ к нескольким скидкам
2025-12-20 18:26:15 +00:00
Future<void> grantDiscountsToUser(
String userId,
List<String> discountIds,
) async {
2025-12-13 14:48:00 +00:00
if (discountIds.isEmpty) return;
await Future.wait(
2025-12-20 18:26:15 +00:00
discountIds.map((discountId) => grantDiscountToUser(userId, discountId)),
2025-12-13 14:48:00 +00:00
);
}
2025-12-20 18:26:15 +00:00
2025-12-13 13:27:05 +00:00
/// Проверить, есть ли у пользователя доступ к скидке
2025-12-13 20:55:50 +00:00
Future<bool> hasDiscountAccess(String userId, String discountId) async {
2025-12-13 13:27:05 +00:00
final query = select(db.discountUserDatas)
2025-12-20 18:26:15 +00:00
..where(
(dud) => dud.userId.equals(userId) & dud.discountId.equals(discountId),
);
2025-12-13 13:27:05 +00:00
final result = await query.getSingleOrNull();
return result != null;
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
/// Получить активные кампании с учетом тегов и продуктов
Future<List<DiscountCampaign>> getActiveCampaignsForUser({
required List<String> userTags,
String? productType,
String? productId,
}) async {
2025-12-13 23:35:14 +00:00
final now = PgDateTime(DateTime.now());
2025-12-13 14:48:00 +00:00
final query = select(db.discountCampaigns)
..where((c) => c.status.equals('active'))
..where((c) => c.start.isSmallerOrEqualValue(now))
..where((c) => c.finish.isBiggerOrEqualValue(now))
..where((c) => c.isDeleted.equals(false));
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
var campaigns = await query.get();
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
// Фильтруем по тегам если они есть
if (userTags.isNotEmpty) {
campaigns = campaigns.where((campaign) {
final campaignTags = campaign.tags.toSet();
2025-12-20 18:26:15 +00:00
return campaignTags.isEmpty ||
campaignTags.intersection(userTags.toSet()).isNotEmpty;
2025-12-13 14:48:00 +00:00
}).toList();
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
// Фильтруем по продукту если указан
if (productType != null || productId != null) {
// Нужно загрузить скидки для каждой кампании и проверить продукты
final filteredCampaigns = <DiscountCampaign>[];
for (final campaign in campaigns) {
final discounts = await getDiscountsByCampaignId(campaign.id);
final hasMatchingProduct = discounts.any((discount) {
final products = discount.products ?? [];
return products.any((product) {
if (product is! Map<String, dynamic>) return false;
2025-12-20 18:26:15 +00:00
if (productType != null && product['type'] != productType)
return false;
2025-12-13 14:48:00 +00:00
if (productId != null && product['id'] != productId) return false;
return true;
});
});
if (hasMatchingProduct) {
filteredCampaigns.add(campaign);
}
}
campaigns = filteredCampaigns;
}
2025-12-20 18:26:15 +00:00
2025-12-13 14:48:00 +00:00
return campaigns;
}
2025-12-13 13:27:05 +00:00
}