mnemo_cards/mnemo_cards_backend/lib/database/daos/pack_dao.dart
Dmitry 50e7e75a56
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (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
voice
2025-12-14 21:49:59 +03:00

302 lines
9.6 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 'package:drift_postgres/drift_postgres.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(String 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<String> createPack(CardPacksCompanion pack) async {
final inserted = await into(cardPacks).insertReturning(pack);
return inserted.id;
}
/// Обновить пак
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(PgDateTime(DateTime.now()))));
}
/// Удалить пак (soft delete)
Future<void> softDeletePack(String packId) {
return (update(cardPacks)..where((p) => p.id.equals(packId)))
.write(CardPacksCompanion(
isDeleted: const Value(true),
updatedAt: Value(PgDateTime(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(String id) {
return (select(gameCards)..where((c) => c.id.equals(id))).getSingleOrNull();
}
/// Получить все карточки пака
Future<List<GameCard>> getPackCards(String 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<String> ids) {
if (ids.isEmpty) return Future.value([]);
return (select(gameCards)..where((c) => c.id.isIn(ids))).get();
}
/// Создать карточку
Future<String> createCard(GameCardsCompanion card) async {
final inserted = await into(gameCards).insertReturning(card);
return inserted.id;
}
/// Обновить карточку
Future<bool> updateCard(GameCard card) {
return update(gameCards).replace(card);
}
/// Удалить карточку (soft delete)
Future<void> softDeleteCard(String cardId) {
return (update(gameCards)..where((c) => c.id.equals(cardId)))
.write(GameCardsCompanion(
isDeleted: const Value(true),
updatedAt: Value(PgDateTime(DateTime.now())),
));
}
/// Удалить карточку (hard delete)
Future<void> deleteCard(String 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 String packId,
required String cardId,
int order = 0,
}) async {
await into(cardPackCards).insert(
CardPackCardsCompanion.insert(
packId: packId,
cardId: cardId,
order: Value(order),
),
mode: InsertMode.insertOrIgnore,
);
}
/// Удалить карточку из пака
Future<void> removeCardFromPack(String packId, String cardId) async {
await (delete(cardPackCards)
..where((cpc) => cpc.packId.equals(packId) & cpc.cardId.equals(cardId))
).go();
}
/// Обновить порядок карточек в паке
Future<void> updatePackCardsOrder(String packId, List<String> 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(String 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(String packId, List<String> 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(String 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(String cardId, String voiceId) async {
await into(cardVoices).insert(
CardVoicesCompanion.insert(
cardId: cardId,
voiceId: voiceId,
),
mode: InsertMode.insertOrIgnore,
);
}
/// Удалить голосовую модель из карточки
Future<void> removeVoiceFromCard(String cardId, String voiceId) async {
await (delete(cardVoices)
..where((cv) => cv.cardId.equals(cardId) & cv.voiceId.equals(voiceId))
).go();
}
/// Создать голосовую модель
Future<String> createVoice(VoiceModelsCompanion voice) async {
final inserted = await into(voiceModels).insertReturning(voice);
return inserted.id;
}
/// Получить голосовую модель по ID
Future<VoiceModel?> getVoiceById(String id) {
return (select(voiceModels)..where((v) => v.id.equals(id))).getSingleOrNull();
}
/// Удалить голосовую модель
Future<void> deleteVoice(String voiceId) async {
await (delete(voiceModels)..where((v) => v.id.equals(voiceId))).go();
}
}