2025-12-13 13:27:05 +00:00
|
|
|
|
import 'package:drift/drift.dart';
|
2025-12-13 23:35:14 +00:00
|
|
|
|
import 'package:drift_postgres/drift_postgres.dart';
|
2025-12-13 13:27:05 +00:00
|
|
|
|
import '../database.dart';
|
|
|
|
|
|
import '../tables/packs.dart';
|
|
|
|
|
|
import '../tables/relations.dart';
|
|
|
|
|
|
|
|
|
|
|
|
part 'pack_dao.g.dart';
|
|
|
|
|
|
|
2025-12-20 18:26:15 +00:00
|
|
|
|
@DriftAccessor(
|
|
|
|
|
|
tables: [
|
|
|
|
|
|
CardPacks,
|
|
|
|
|
|
GameCards,
|
|
|
|
|
|
VoiceModels,
|
|
|
|
|
|
PreviewCards,
|
|
|
|
|
|
CardPackCards,
|
|
|
|
|
|
CardVoices,
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2025-12-13 13:27:05 +00:00
|
|
|
|
class PackDao extends DatabaseAccessor<AppDatabase> with _$PackDaoMixin {
|
|
|
|
|
|
PackDao(super.db);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// ==================== CardPacks ====================
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить пак по ID
|
2026-01-03 14:48:57 +00:00
|
|
|
|
Future<CardPack?> getPackById(String id) async {
|
|
|
|
|
|
final query = select(cardPacks)
|
|
|
|
|
|
..where((p) => p.id.equals(id))
|
|
|
|
|
|
..limit(1);
|
|
|
|
|
|
|
|
|
|
|
|
final results = await query.get();
|
|
|
|
|
|
return results.isNotEmpty ? results.first : null;
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить все паки
|
|
|
|
|
|
Future<List<CardPack>> getAllPacks({
|
|
|
|
|
|
bool enabledOnly = false,
|
|
|
|
|
|
String? orderByField,
|
|
|
|
|
|
bool orderDesc = false,
|
|
|
|
|
|
}) {
|
|
|
|
|
|
final query = select(cardPacks);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
if (enabledOnly) {
|
|
|
|
|
|
query.where((p) => p.enabled.equals(true));
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
query.where((p) => p.isDeleted.equals(false));
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
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)]);
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
return query.get();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Создать пак
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<String> createPack(CardPacksCompanion pack) async {
|
|
|
|
|
|
final inserted = await into(cardPacks).insertReturning(pack);
|
|
|
|
|
|
return inserted.id;
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Обновить пак
|
|
|
|
|
|
Future<bool> updatePack(CardPack pack) {
|
|
|
|
|
|
return update(cardPacks).replace(pack);
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Обновить пак частично
|
|
|
|
|
|
Future<void> updatePackPartial(CardPacksCompanion updates) {
|
2025-12-18 21:31:51 +00:00
|
|
|
|
if (!updates.id.present) throw ArgumentError('Pack ID is required');
|
2025-12-13 13:27:05 +00:00
|
|
|
|
final packId = updates.id.value;
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
|
|
|
|
|
return (update(cardPacks)..where((p) => p.id.equals(packId))).write(
|
|
|
|
|
|
updates.copyWith(updatedAt: Value(PgDateTime(DateTime.now()))),
|
|
|
|
|
|
);
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Удалить пак (soft delete)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> softDeletePack(String packId) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
return (update(cardPacks)..where((p) => p.id.equals(packId))).write(
|
|
|
|
|
|
CardPacksCompanion(
|
|
|
|
|
|
isDeleted: const Value(true),
|
|
|
|
|
|
updatedAt: Value(PgDateTime(DateTime.now())),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Подсчитать паки
|
|
|
|
|
|
Future<int> countPacks({bool enabledOnly = false}) async {
|
|
|
|
|
|
final countExpr = cardPacks.id.count();
|
|
|
|
|
|
final query = selectOnly(cardPacks)..addColumns([countExpr]);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
if (enabledOnly) {
|
|
|
|
|
|
query.where(cardPacks.enabled.equals(true));
|
|
|
|
|
|
}
|
|
|
|
|
|
query.where(cardPacks.isDeleted.equals(false));
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
return await query.map((row) => row.read(countExpr)!).getSingle();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// ==================== GameCards ====================
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-17 00:41:47 +00:00
|
|
|
|
/// Получить карточку по ID (только активные)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<GameCard?> getCardById(String id) {
|
2025-12-17 00:41:47 +00:00
|
|
|
|
return (select(gameCards)
|
2025-12-20 18:26:15 +00:00
|
|
|
|
..where((c) => c.id.equals(id) & c.isDeleted.equals(false)))
|
|
|
|
|
|
.getSingleOrNull();
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-14 21:10:06 +00:00
|
|
|
|
/// Получить паки для карточки
|
|
|
|
|
|
/// Связь через CardPackCards (packId удален из GameCards)
|
|
|
|
|
|
Future<List<CardPack>> getPacksForCard(String cardId) async {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
final query =
|
|
|
|
|
|
select(cardPacks).join([
|
|
|
|
|
|
innerJoin(
|
|
|
|
|
|
cardPackCards,
|
|
|
|
|
|
cardPackCards.packId.equalsExp(cardPacks.id),
|
|
|
|
|
|
),
|
|
|
|
|
|
])..where(
|
|
|
|
|
|
cardPackCards.cardId.equals(cardId) &
|
|
|
|
|
|
cardPacks.isDeleted.equals(false),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-14 21:10:06 +00:00
|
|
|
|
final rows = await query.get();
|
|
|
|
|
|
return rows.map((row) => row.readTable(cardPacks)).toList();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить все карточки пака
|
2025-12-14 20:42:38 +00:00
|
|
|
|
/// Связь теперь только через CardPackCards (packId удален из GameCards)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<List<GameCard>> getPackCards(String packId) async {
|
2025-12-13 14:48:00 +00:00
|
|
|
|
final query = select(gameCards).join([
|
2025-12-14 20:42:38 +00:00
|
|
|
|
innerJoin(
|
2025-12-13 14:48:00 +00:00
|
|
|
|
cardPackCards,
|
|
|
|
|
|
cardPackCards.cardId.equalsExp(gameCards.id) &
|
2025-12-20 18:26:15 +00:00
|
|
|
|
cardPackCards.packId.equals(packId),
|
2025-12-13 14:48:00 +00:00
|
|
|
|
),
|
2025-12-14 20:42:38 +00:00
|
|
|
|
])..where(gameCards.isDeleted.equals(false));
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 14:48:00 +00:00
|
|
|
|
final results = await query.get();
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 14:48:00 +00:00
|
|
|
|
// Сортируем по 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);
|
|
|
|
|
|
});
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 14:48:00 +00:00
|
|
|
|
return results.map((row) => row.readTable(gameCards)).toList();
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить карточки по списку ID
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<List<GameCard>> getCardsByIds(List<String> ids) {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
if (ids.isEmpty) return Future.value([]);
|
|
|
|
|
|
return (select(gameCards)..where((c) => c.id.isIn(ids))).get();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-14 20:45:27 +00:00
|
|
|
|
/// Найти карточки по original (слову)
|
|
|
|
|
|
/// Возвращает все карточки с указанным original (может быть несколько)
|
|
|
|
|
|
Future<List<GameCard>> searchCardsByOriginal(String original) {
|
|
|
|
|
|
return (select(gameCards)
|
2025-12-20 18:26:15 +00:00
|
|
|
|
..where(
|
|
|
|
|
|
(c) => c.original.equals(original) & c.isDeleted.equals(false),
|
|
|
|
|
|
)
|
|
|
|
|
|
..limit(10) // Ограничиваем для производительности
|
|
|
|
|
|
)
|
|
|
|
|
|
.get();
|
2025-12-14 20:45:27 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Создать карточку
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<String> createCard(GameCardsCompanion card) async {
|
|
|
|
|
|
final inserted = await into(gameCards).insertReturning(card);
|
|
|
|
|
|
return inserted.id;
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Обновить карточку
|
|
|
|
|
|
Future<bool> updateCard(GameCard card) {
|
|
|
|
|
|
return update(gameCards).replace(card);
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Удалить карточку (soft delete)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> softDeleteCard(String cardId) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
return (update(gameCards)..where((c) => c.id.equals(cardId))).write(
|
|
|
|
|
|
GameCardsCompanion(
|
|
|
|
|
|
isDeleted: const Value(true),
|
|
|
|
|
|
updatedAt: Value(PgDateTime(DateTime.now())),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Удалить карточку (hard delete)
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> deleteCard(String cardId) {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Добавить карточку в пак
|
2025-12-20 15:38:55 +00:00
|
|
|
|
/// Если связь уже существует, метод завершится успешно без ошибки
|
2025-12-13 13:27:05 +00:00
|
|
|
|
Future<void> addCardToPack({
|
2025-12-13 20:55:50 +00:00
|
|
|
|
required String packId,
|
|
|
|
|
|
required String cardId,
|
2025-12-13 13:27:05 +00:00
|
|
|
|
int order = 0,
|
|
|
|
|
|
}) async {
|
2025-12-20 15:38:55 +00:00
|
|
|
|
// Проверить, существует ли уже связь
|
2025-12-20 18:26:15 +00:00
|
|
|
|
final existing =
|
|
|
|
|
|
await (select(cardPackCards)..where(
|
|
|
|
|
|
(cpc) => cpc.packId.equals(packId) & cpc.cardId.equals(cardId),
|
|
|
|
|
|
))
|
|
|
|
|
|
.getSingleOrNull();
|
|
|
|
|
|
|
2025-12-20 15:38:55 +00:00
|
|
|
|
// Если связь уже существует, ничего не делаем
|
|
|
|
|
|
if (existing != null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-20 15:38:55 +00:00
|
|
|
|
// Иначе создаем новую связь
|
2025-12-13 13:27:05 +00:00
|
|
|
|
await into(cardPackCards).insert(
|
|
|
|
|
|
CardPackCardsCompanion.insert(
|
|
|
|
|
|
packId: packId,
|
|
|
|
|
|
cardId: cardId,
|
|
|
|
|
|
order: Value(order),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Удалить карточку из пака
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> removeCardFromPack(String packId, String cardId) async {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
await (delete(cardPackCards)..where(
|
|
|
|
|
|
(cpc) => cpc.packId.equals(packId) & cpc.cardId.equals(cardId),
|
|
|
|
|
|
))
|
|
|
|
|
|
.go();
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Обновить порядок карточек в паке
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> updatePackCardsOrder(String packId, List<String> cardIds) async {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
await transaction(() async {
|
|
|
|
|
|
// Удалить старые связи
|
2025-12-20 18:26:15 +00:00
|
|
|
|
await (delete(
|
|
|
|
|
|
cardPackCards,
|
|
|
|
|
|
)..where((cpc) => cpc.packId.equals(packId))).go();
|
|
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// Создать новые связи с порядком
|
|
|
|
|
|
for (var i = 0; i < cardIds.length; i++) {
|
|
|
|
|
|
await into(cardPackCards).insert(
|
|
|
|
|
|
CardPackCardsCompanion.insert(
|
|
|
|
|
|
packId: packId,
|
|
|
|
|
|
cardId: cardIds[i],
|
|
|
|
|
|
order: Value(i),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// ==================== PreviewCards ====================
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить preview карточки пака
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<List<GameCard>> getPreviewCards(String packId) async {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
final query = select(gameCards).join([
|
|
|
|
|
|
innerJoin(
|
|
|
|
|
|
db.previewCards,
|
|
|
|
|
|
db.previewCards.cardId.equalsExp(gameCards.id) &
|
2025-12-20 18:26:15 +00:00
|
|
|
|
db.previewCards.packId.equals(packId),
|
2025-12-13 13:27:05 +00:00
|
|
|
|
),
|
|
|
|
|
|
]);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
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);
|
|
|
|
|
|
});
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
return results.map((row) => row.readTable(gameCards)).toList();
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Установить preview карточки для пака
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> setPreviewCards(String packId, List<String> cardIds) async {
|
2025-12-13 13:27:05 +00:00
|
|
|
|
await transaction(() async {
|
|
|
|
|
|
// Удалить старые preview карточки
|
2025-12-20 18:26:15 +00:00
|
|
|
|
await (delete(
|
|
|
|
|
|
previewCards,
|
|
|
|
|
|
)..where((pc) => pc.packId.equals(packId))).go();
|
|
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// Добавить новые
|
|
|
|
|
|
for (var i = 0; i < cardIds.length; i++) {
|
|
|
|
|
|
await into(previewCards).insert(
|
|
|
|
|
|
PreviewCardsCompanion.insert(
|
|
|
|
|
|
packId: packId,
|
|
|
|
|
|
cardId: cardIds[i],
|
|
|
|
|
|
order: Value(i),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
// ==================== VoiceModels ====================
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить голосовые модели карточки
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<List<VoiceModel>> getCardVoices(String cardId) async {
|
2025-12-19 02:42:08 +00:00
|
|
|
|
print('🔍 DAO getCardVoices: Querying voices for card $cardId');
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// First, check if there are any voices in VoiceModels table with direct cardId link
|
|
|
|
|
|
final directVoicesQuery = select(voiceModels)
|
|
|
|
|
|
..where((v) => v.cardId.equals(cardId));
|
|
|
|
|
|
final directVoices = await directVoicesQuery.get();
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'🔍 DAO getCardVoices: Found ${directVoices.length} voices in VoiceModels with direct cardId link',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Check if there are any voices in CardVoices table for this card
|
2025-12-19 02:42:08 +00:00
|
|
|
|
final countExpr = cardVoices.cardId.count();
|
|
|
|
|
|
final countQuery = selectOnly(cardVoices)
|
|
|
|
|
|
..addColumns([countExpr])
|
|
|
|
|
|
..where(cardVoices.cardId.equals(cardId));
|
|
|
|
|
|
final countResult = await countQuery.getSingle();
|
|
|
|
|
|
final count = countResult.read(countExpr) ?? 0;
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'🔍 DAO getCardVoices: Found $count entries in CardVoices junction table for card $cardId',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Query through junction table
|
2025-12-13 13:27:05 +00:00
|
|
|
|
final query = select(voiceModels).join([
|
|
|
|
|
|
innerJoin(
|
|
|
|
|
|
cardVoices,
|
|
|
|
|
|
cardVoices.voiceId.equalsExp(voiceModels.id) &
|
2025-12-20 18:26:15 +00:00
|
|
|
|
cardVoices.cardId.equals(cardId),
|
2025-12-13 13:27:05 +00:00
|
|
|
|
),
|
|
|
|
|
|
]);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-19 02:42:08 +00:00
|
|
|
|
final result = await query.map((row) => row.readTable(voiceModels)).get();
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'🔍 DAO getCardVoices: Query through junction table returned ${result.length} voices',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// If junction table query returns nothing but direct link exists, return direct voices
|
|
|
|
|
|
if (result.isEmpty && directVoices.isNotEmpty) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'⚠️ DAO getCardVoices: Junction table is empty but direct link exists! Using direct voices.',
|
|
|
|
|
|
);
|
2025-12-19 03:20:21 +00:00
|
|
|
|
return directVoices;
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-19 02:42:08 +00:00
|
|
|
|
return result;
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Добавить голосовую модель к карточке
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> addVoiceToCard(String cardId, String voiceId) async {
|
2025-12-19 02:42:08 +00:00
|
|
|
|
print('🔍 DAO addVoiceToCard: Linking voice $voiceId to card $cardId');
|
|
|
|
|
|
try {
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Check if voice exists
|
|
|
|
|
|
final voice = await getVoiceById(voiceId);
|
|
|
|
|
|
if (voice == null) {
|
|
|
|
|
|
print('❌ DAO addVoiceToCard: Voice $voiceId does not exist!');
|
|
|
|
|
|
throw Exception('Voice $voiceId does not exist');
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'✅ DAO addVoiceToCard: Voice $voiceId exists with cardId=${voice.cardId}',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Check if card exists
|
|
|
|
|
|
final card = await getCardById(cardId);
|
|
|
|
|
|
if (card == null) {
|
|
|
|
|
|
print('❌ DAO addVoiceToCard: Card $cardId does not exist!');
|
|
|
|
|
|
throw Exception('Card $cardId does not exist');
|
|
|
|
|
|
}
|
|
|
|
|
|
print('✅ DAO addVoiceToCard: Card $cardId exists');
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Insert into junction table
|
|
|
|
|
|
final result = await into(cardVoices).insert(
|
2025-12-20 18:26:15 +00:00
|
|
|
|
CardVoicesCompanion.insert(cardId: cardId, voiceId: voiceId),
|
2025-12-19 02:42:08 +00:00
|
|
|
|
mode: InsertMode.insertOrIgnore,
|
|
|
|
|
|
);
|
2025-12-19 03:20:21 +00:00
|
|
|
|
print('✅ DAO addVoiceToCard: Insert result: $result');
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-19 03:20:21 +00:00
|
|
|
|
// Verify the insert
|
|
|
|
|
|
final verifyQuery = select(cardVoices)
|
|
|
|
|
|
..where((cv) => cv.cardId.equals(cardId) & cv.voiceId.equals(voiceId));
|
|
|
|
|
|
final verifyResult = await verifyQuery.getSingleOrNull();
|
|
|
|
|
|
if (verifyResult == null) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'❌ DAO addVoiceToCard: Verification failed! Link was not created.',
|
|
|
|
|
|
);
|
2025-12-19 03:20:21 +00:00
|
|
|
|
} else {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'✅ DAO addVoiceToCard: Successfully linked and verified voice $voiceId to card $cardId',
|
|
|
|
|
|
);
|
2025-12-19 03:20:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e, stackTrace) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'❌ DAO addVoiceToCard: Error linking voice $voiceId to card $cardId: $e',
|
|
|
|
|
|
);
|
2025-12-19 03:20:21 +00:00
|
|
|
|
print('Stack trace: $stackTrace');
|
2025-12-19 02:42:08 +00:00
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Удалить голосовую модель из карточки
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<void> removeVoiceFromCard(String cardId, String voiceId) async {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
await (delete(
|
|
|
|
|
|
cardVoices,
|
|
|
|
|
|
)..where((cv) => cv.cardId.equals(cardId) & cv.voiceId.equals(voiceId)))
|
|
|
|
|
|
.go();
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Создать голосовую модель
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<String> createVoice(VoiceModelsCompanion voice) async {
|
|
|
|
|
|
final inserted = await into(voiceModels).insertReturning(voice);
|
|
|
|
|
|
return inserted.id;
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 13:27:05 +00:00
|
|
|
|
/// Получить голосовую модель по ID
|
2025-12-13 20:55:50 +00:00
|
|
|
|
Future<VoiceModel?> getVoiceById(String id) {
|
2025-12-20 18:26:15 +00:00
|
|
|
|
return (select(
|
|
|
|
|
|
voiceModels,
|
|
|
|
|
|
)..where((v) => v.id.equals(id))).getSingleOrNull();
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|
2025-12-18 21:31:51 +00:00
|
|
|
|
|
|
|
|
|
|
/// Обновить путь/URL аудиофайла голосовой модели
|
|
|
|
|
|
Future<void> updateVoiceUrl(String voiceId, String voiceUrl) async {
|
|
|
|
|
|
await (update(voiceModels)..where((v) => v.id.equals(voiceId))).write(
|
|
|
|
|
|
VoiceModelsCompanion(voiceUrl: Value(voiceUrl)),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-14 18:49:59 +00:00
|
|
|
|
/// Удалить голосовую модель
|
|
|
|
|
|
Future<void> deleteVoice(String voiceId) async {
|
|
|
|
|
|
await (delete(voiceModels)..where((v) => v.id.equals(voiceId))).go();
|
|
|
|
|
|
}
|
2025-12-13 13:27:05 +00:00
|
|
|
|
}
|