2025-12-13 13:27:05 +00:00
|
|
|
|
import 'package:drift/drift.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<AppDatabase> with _$DiscountDaoMixin {
|
|
|
|
|
|
DiscountDao(super.db);
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== DiscountCampaigns ====================
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить кампанию по ID
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<DiscountCampaign?> getCampaignById(String id) {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
return (select(db.discountCampaigns)..where((c) => c.id.equals(id))).getSingleOrNull();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить все активные кампании
|
|
|
|
|
|
Future<List<DiscountCampaign>> getActiveCampaigns() {
|
|
|
|
|
|
final now = 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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Создать кампанию
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Обновить кампанию
|
|
|
|
|
|
Future<bool> updateCampaign(DiscountCampaign campaign) {
|
|
|
|
|
|
return update(db.discountCampaigns).replace(campaign);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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-13 14:48:00 +00:00
|
|
|
|
return (update(db.discountCampaigns)
|
|
|
|
|
|
..where((c) => c.id.equals(campaignId))
|
|
|
|
|
|
).write(DiscountCampaignsCompanion(
|
|
|
|
|
|
status: Value(status),
|
|
|
|
|
|
updatedAt: Value(DateTime.now()),
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить кампании по статусу
|
|
|
|
|
|
Future<List<DiscountCampaign>> getCampaignsByStatus(String status) {
|
|
|
|
|
|
return (select(db.discountCampaigns)
|
|
|
|
|
|
..where((c) => c.status.equals(status))
|
|
|
|
|
|
..where((c) => c.isDeleted.equals(false))
|
|
|
|
|
|
).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить кампании по статусам (несколько)
|
|
|
|
|
|
Future<List<DiscountCampaign>> getCampaignsByStatuses(List<String> statuses) {
|
|
|
|
|
|
return (select(db.discountCampaigns)
|
|
|
|
|
|
..where((c) => c.status.isIn(statuses))
|
|
|
|
|
|
..where((c) => c.isDeleted.equals(false))
|
|
|
|
|
|
).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить все кампании
|
|
|
|
|
|
Future<List<DiscountCampaign>> getAllCampaigns() {
|
|
|
|
|
|
return (select(db.discountCampaigns)
|
|
|
|
|
|
..where((c) => c.isDeleted.equals(false))
|
|
|
|
|
|
).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Удалить кампанию (soft delete)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> deleteCampaign(String campaignId) {
|
2025-12-13 14:48:00 +00:00
|
|
|
|
return (update(db.discountCampaigns)
|
|
|
|
|
|
..where((c) => c.id.equals(campaignId))
|
|
|
|
|
|
).write(DiscountCampaignsCompanion(
|
|
|
|
|
|
isDeleted: const Value(true),
|
|
|
|
|
|
updatedAt: Value(DateTime.now()),
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// ==================== Discounts ====================
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить скидку по ID
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<Discount?> getDiscountById(String id) {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
return (select(db.discounts)..where((d) => d.id.equals(id))).getSingleOrNull();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить скидки кампании
|
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)
|
|
|
|
|
|
..where((d) => d.campaignId.equals(campaignId))
|
|
|
|
|
|
..where((d) => d.isDeleted.equals(false))
|
|
|
|
|
|
).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Создать скидку
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Обновить скидку
|
|
|
|
|
|
Future<bool> updateDiscount(Discount discount) {
|
|
|
|
|
|
return update(db.discounts).replace(discount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== DiscountUserDatas ====================
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить скидки пользователя
|
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) &
|
|
|
|
|
|
db.discountUserDatas.userId.equals(userId),
|
|
|
|
|
|
),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
return query.map((row) => row.readTable(db.discounts)).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Дать пользователю доступ к скидке
|
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(
|
|
|
|
|
|
DiscountUserDatasCompanion.insert(
|
|
|
|
|
|
userId: userId,
|
|
|
|
|
|
discountId: discountId,
|
|
|
|
|
|
),
|
|
|
|
|
|
mode: InsertMode.insertOrIgnore,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Отозвать скидку у пользователя
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> revokeDiscountFromUser(String userId, String discountId) async {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
await (delete(db.discountUserDatas)
|
|
|
|
|
|
..where((dud) => dud.userId.equals(userId) & dud.discountId.equals(discountId))
|
|
|
|
|
|
).go();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-13 14:48:00 +00:00
|
|
|
|
/// Отозвать несколько скидок у пользователя
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> revokeDiscountsFromUser(String userId, List<String> discountIds) async {
|
2025-12-13 14:48:00 +00:00
|
|
|
|
if (discountIds.isEmpty) return;
|
|
|
|
|
|
await (delete(db.discountUserDatas)
|
|
|
|
|
|
..where((dud) => dud.userId.equals(userId) & dud.discountId.isIn(discountIds))
|
|
|
|
|
|
).go();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Дать пользователю доступ к нескольким скидкам
|
2025-12-13 20:55:50 +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(
|
|
|
|
|
|
discountIds.map((discountId) => grantDiscountToUser(userId, discountId))
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
..where((dud) => dud.userId.equals(userId) & dud.discountId.equals(discountId));
|
|
|
|
|
|
|
|
|
|
|
|
final result = await query.getSingleOrNull();
|
|
|
|
|
|
return result != null;
|
|
|
|
|
|
}
|
2025-12-13 14:48:00 +00:00
|
|
|
|
|
|
|
|
|
|
/// Получить активные кампании с учетом тегов и продуктов
|
|
|
|
|
|
Future<List<DiscountCampaign>> getActiveCampaignsForUser({
|
|
|
|
|
|
required List<String> userTags,
|
|
|
|
|
|
String? productType,
|
|
|
|
|
|
String? productId,
|
|
|
|
|
|
}) async {
|
|
|
|
|
|
final now = 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 = <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;
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|