mnemo_cards/mnemo_cards_backend/lib/database/daos/pack_dao.dart
Dmitry 306b2fdca7
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
Deploy Telegram Bot / Deploy Telegram Bot (push) Waiting to run
fixes
2025-12-13 17:48:00 +03:00

293 lines
9.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:drift/drift.dart';
import '../database.dart';
import '../tables/packs.dart';
import '../tables/relations.dart';
part 'pack_dao.g.dart';
@DriftAccessor(tables: [CardPacks, GameCards, VoiceModels, PreviewCards, CardPackCards, CardVoices])
class PackDao extends DatabaseAccessor<AppDatabase> with _$PackDaoMixin {
PackDao(super.db);
// ==================== CardPacks ====================
/// Получить пак по ID
Future<CardPack?> getPackById(int id) {
return (select(cardPacks)..where((p) => p.id.equals(id))).getSingleOrNull();
}
/// Получить все паки
Future<List<CardPack>> getAllPacks({
bool enabledOnly = false,
String? orderByField,
bool orderDesc = false,
}) {
final query = select(cardPacks);
if (enabledOnly) {
query.where((p) => p.enabled.equals(true));
}
query.where((p) => p.isDeleted.equals(false));
if (orderByField == 'order') {
if (orderDesc) {
query.orderBy([(p) => OrderingTerm.desc(p.order)]);
} else {
query.orderBy([(p) => OrderingTerm.asc(p.order)]);
}
} else {
query.orderBy([(p) => OrderingTerm.asc(p.order)]);
}
return query.get();
}
/// Создать пак
Future<int> createPack(CardPacksCompanion pack) {
return into(cardPacks).insert(pack);
}
/// Обновить пак
Future<bool> updatePack(CardPack pack) {
return update(cardPacks).replace(pack);
}
/// Обновить пак частично
Future<void> updatePackPartial(CardPacksCompanion updates) {
final packId = updates.id.value;
if (packId == null) throw ArgumentError('Pack ID is required');
return (update(cardPacks)..where((p) => p.id.equals(packId)))
.write(updates.copyWith(updatedAt: Value(DateTime.now())));
}
/// Удалить пак (soft delete)
Future<void> softDeletePack(int packId) {
return (update(cardPacks)..where((p) => p.id.equals(packId)))
.write(CardPacksCompanion(
isDeleted: const Value(true),
updatedAt: Value(DateTime.now()),
));
}
/// Подсчитать паки
Future<int> countPacks({bool enabledOnly = false}) async {
final countExpr = cardPacks.id.count();
final query = selectOnly(cardPacks)..addColumns([countExpr]);
if (enabledOnly) {
query.where(cardPacks.enabled.equals(true));
}
query.where(cardPacks.isDeleted.equals(false));
return await query.map((row) => row.read(countExpr)!).getSingle();
}
// ==================== GameCards ====================
/// Получить карточку по ID
Future<GameCard?> getCardById(int id) {
return (select(gameCards)..where((c) => c.id.equals(id))).getSingleOrNull();
}
/// Получить все карточки пака
Future<List<GameCard>> getPackCards(int packId) async {
final query = select(gameCards).join([
leftOuterJoin(
cardPackCards,
cardPackCards.cardId.equalsExp(gameCards.id) &
cardPackCards.packId.equals(packId),
),
])..where(gameCards.packId.equals(packId));
final results = await query.get();
// Сортируем по order из junction table, если есть, иначе по ID
results.sort((a, b) {
final orderA = a.read(cardPackCards.order) ?? 999999;
final orderB = a.read(cardPackCards.order) ?? 999999;
if (orderA != orderB) return orderA.compareTo(orderB);
return a.readTable(gameCards).id.compareTo(b.readTable(gameCards).id);
});
return results.map((row) => row.readTable(gameCards)).toList();
}
/// Получить карточки по списку ID
Future<List<GameCard>> getCardsByIds(List<int> ids) {
if (ids.isEmpty) return Future.value([]);
return (select(gameCards)..where((c) => c.id.isIn(ids))).get();
}
/// Создать карточку
Future<int> createCard(GameCardsCompanion card) {
return into(gameCards).insert(card);
}
/// Обновить карточку
Future<bool> updateCard(GameCard card) {
return update(gameCards).replace(card);
}
/// Удалить карточку (soft delete)
Future<void> softDeleteCard(int cardId) {
return (update(gameCards)..where((c) => c.id.equals(cardId)))
.write(GameCardsCompanion(
isDeleted: const Value(true),
updatedAt: Value(DateTime.now()),
));
}
/// Удалить карточку (hard delete)
Future<void> deleteCard(int cardId) {
return (delete(gameCards)..where((c) => c.id.equals(cardId))).go();
}
/// Получить все карточки с пагинацией
Future<List<GameCard>> getAllCards({int? limit, int? offset}) {
final query = select(gameCards);
if (limit != null) {
query.limit(limit, offset: offset);
}
return query.get();
}
/// Подсчитать карточки
Future<int> countCards() async {
final countExpr = gameCards.id.count();
final query = selectOnly(gameCards)..addColumns([countExpr]);
return await query.map((row) => row.read(countExpr)!).getSingle();
}
/// Добавить карточку в пак
Future<void> addCardToPack({
required int packId,
required int cardId,
int order = 0,
}) async {
await into(cardPackCards).insert(
CardPackCardsCompanion.insert(
packId: packId,
cardId: cardId,
order: Value(order),
),
mode: InsertMode.insertOrIgnore,
);
}
/// Удалить карточку из пака
Future<void> removeCardFromPack(int packId, int cardId) async {
await (delete(cardPackCards)
..where((cpc) => cpc.packId.equals(packId) & cpc.cardId.equals(cardId))
).go();
}
/// Обновить порядок карточек в паке
Future<void> updatePackCardsOrder(int packId, List<int> cardIds) async {
await transaction(() async {
// Удалить старые связи
await (delete(cardPackCards)
..where((cpc) => cpc.packId.equals(packId))
).go();
// Создать новые связи с порядком
for (var i = 0; i < cardIds.length; i++) {
await into(cardPackCards).insert(
CardPackCardsCompanion.insert(
packId: packId,
cardId: cardIds[i],
order: Value(i),
),
);
}
});
}
// ==================== PreviewCards ====================
/// Получить preview карточки пака
Future<List<GameCard>> getPreviewCards(int packId) async {
final query = select(gameCards).join([
innerJoin(
db.previewCards,
db.previewCards.cardId.equalsExp(gameCards.id) &
db.previewCards.packId.equals(packId),
),
]);
final results = await query.get();
// Сортируем вручную по order из junction table
results.sort((a, b) {
final orderA = a.read(db.previewCards.order) ?? 0;
final orderB = b.read(db.previewCards.order) ?? 0;
return orderA.compareTo(orderB);
});
return results.map((row) => row.readTable(gameCards)).toList();
}
/// Установить preview карточки для пака
Future<void> setPreviewCards(int packId, List<int> cardIds) async {
await transaction(() async {
// Удалить старые preview карточки
await (delete(previewCards)
..where((pc) => pc.packId.equals(packId))
).go();
// Добавить новые
for (var i = 0; i < cardIds.length; i++) {
await into(previewCards).insert(
PreviewCardsCompanion.insert(
packId: packId,
cardId: cardIds[i],
order: Value(i),
),
);
}
});
}
// ==================== VoiceModels ====================
/// Получить голосовые модели карточки
Future<List<VoiceModel>> getCardVoices(int cardId) async {
final query = select(voiceModels).join([
innerJoin(
cardVoices,
cardVoices.voiceId.equalsExp(voiceModels.id) &
cardVoices.cardId.equals(cardId),
),
]);
return query.map((row) => row.readTable(voiceModels)).get();
}
/// Добавить голосовую модель к карточке
Future<void> addVoiceToCard(int cardId, int voiceId) async {
await into(cardVoices).insert(
CardVoicesCompanion.insert(
cardId: cardId,
voiceId: voiceId,
),
mode: InsertMode.insertOrIgnore,
);
}
/// Удалить голосовую модель из карточки
Future<void> removeVoiceFromCard(int cardId, int voiceId) async {
await (delete(cardVoices)
..where((cv) => cv.cardId.equals(cardId) & cv.voiceId.equals(voiceId))
).go();
}
/// Создать голосовую модель
Future<int> createVoice(VoiceModelsCompanion voice) {
return into(voiceModels).insert(voice);
}
/// Получить голосовую модель по ID
Future<VoiceModel?> getVoiceById(int id) {
return (select(voiceModels)..where((v) => v.id.equals(id))).getSingleOrNull();
}
}