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