mnemo_cards/mnemo_cards_backend/lib/database/daos/pack_dao.dart
Dmitry 77b4ce91b5
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
postgress
2025-12-13 16:27:05 +03:00

294 lines
9 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) {
return (select(gameCards)
..where((c) => c.packId.equals(packId))
..orderBy([(c) => OrderingTerm.asc(c.id)]) // TODO: use proper ordering
).get();
}
/// Получить карточки по списку 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();
}
/// Подсчитать все паки
Future<int> countPacks() async {
final countExpr = cardPacks.id.count();
final query = selectOnly(cardPacks)..addColumns([countExpr]);
return await query.map((row) => row.read(countExpr)!).getSingle();
}
/// Подсчитать все карточки
Future<int> countCards() async {
final countExpr = gameCards.id.count();
final query = selectOnly(gameCards)..addColumns([countExpr]);
return await query.map((row) => row.read(countExpr)!).getSingle();
}
}