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
302 lines
9.6 KiB
Dart
302 lines
9.6 KiB
Dart
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();
|
||
}
|
||
}
|