mnemo_cards/mnemo_cards_backend/CRITICAL_FIXES_TODO.md
2026-01-03 16:14:27 +03:00

11 KiB
Raw Permalink Blame History

🔥 Критичные исправления для завершения этапов 1-6

Приоритет: ВЫСОКИЙ
Блокирует: Компиляцию проекта (88 ошибок)


1. Исправить SoftDeleteMixin

Файл: lib/database/daos/mixins/soft_delete_mixin.dart

Проблема: Метод table.companion() не существует в Drift

Текущий код (строки 40-44):

.write(
  table.companion(
    isDeleted: const Value(true),
    deletedAt: Value(now),
    updatedAt: Value(now),
  ) as UpdateCompanion<D>,
);

Исправление: Нужно создавать Companion вручную для каждой таблицы. SoftDeleteMixin не может быть универсальным для создания Companion.

Решение А (рекомендуемое): Упростить миксин - убрать методы softDelete() и restore(), оставить только selectActive() и getActiveById()

Решение Б: В каждом DAO реализовывать softDelete() вручную


2. Исправить WordStatisticsDao

Файл: lib/database/daos/word_statistics_dao.dart

Проблема 2.1: Метод update() конфликтует с базовым (строка 55)

Текущий код:

Future<void> update({
  required String id,
  required int correctAnswers,
  required int incorrectAnswers,
  required DateTime lastReviewed,
}) async {

Исправление: Переименовать метод

Future<void> updateStatistics({
  required String id,
  required int correctAnswers,
  required int incorrectAnswers,
  required DateTime lastReviewed,
}) async {

Проблема 2.2: Неверные типы в create() (строки 42-45)

Текущий код:

correctAnswers: correctAnswers,
incorrectAnswers: incorrectAnswers,
mastery: mastery,
lastReviewed: Value(lastReviewed),

Исправление:

correctAnswers: Value(correctAnswers),
incorrectAnswers: Value(incorrectAnswers),
mastery: Value(mastery),
lastReviewed: Value(PgDateTime(lastReviewed)),

Проблема 2.3: Ошибки в вызове update (строки 64-71)

Текущий код:

await (update(wordStatistics)..where((w) => w.id.equals(id)))
  .write(
    WordStatisticsCompanion(
      correctAnswers: Value(correctAnswers),
      incorrectAnswers: Value(incorrectAnswers),
      mastery: Value(mastery),
      lastReviewed: Value(lastReviewed),
      updatedAt: Value(DateTime.now()),
    ),
  );

Исправление:

await (update(wordStatistics)..where((w) => w.id.equals(id)))
  .write(
    WordStatisticsCompanion(
      correctAnswers: Value(correctAnswers),
      incorrectAnswers: Value(incorrectAnswers),
      mastery: Value(mastery),
      lastReviewed: Value(PgDateTime(lastReviewed)),
      updatedAt: Value(PgDateTime(DateTime.now())),
    ),
  );

Проблема 2.4: Обновить вызовы метода update

Файл: lib/statistics/word_statistics_manager.dart (строка 45)

Текущий код:

await _db.wordStatisticsDao.update(
  id: existing.id,
  correctAnswers: newCorrect,
  incorrectAnswers: newIncorrect,
  lastReviewed: DateTime.now(),
);

Исправление:

await _db.wordStatisticsDao.updateStatistics(
  id: existing.id,
  correctAnswers: newCorrect,
  incorrectAnswers: newIncorrect,
  lastReviewed: DateTime.now(),
);

3. Исправить AuditLogs.tableName

Файл: lib/database/tables/audit.dart

Проблема: tableName должен возвращать String?, а не Column

Текущий код (строка 21):

TextColumn get tableName => text()();

Исправление: Переименовать поле, чтобы не конфликтовать с Table.tableName

TextColumn get table => text()();

Также нужно обновить:

  • lib/database/daos/audit_dao.dart - заменить tableName на table во всех местах

4. Удалить использование card.packId

4.1 Файл: lib/api/v2/admin_cards_api_v2.dart

Места использования:

  • Строка 101: 'packId': card.packId,
  • Строка 164: 'packId': card.packId,
  • Строка 263: 'packId': card.packId ?? '',
  • Строка 280: packId: requestData['packId'],
  • Строка 311: 'packId': card.packId,

Решение:

  1. Для чтения packId: получить через JOIN с CardPackCards
  2. Для записи packId: использовать CardPackCards.insert

Пример исправления (для чтения):

// Вместо:
'packId': card.packId,

// Использовать:
final packs = await _db.packDao.getPacksForCard(card.id);
'packId': packs.isNotEmpty ? packs.first.id : null,

Нужно добавить метод в PackDao:

Future<List<CardPack>> getPacksForCard(String cardId) async {
  final query = select(cardPacks).join([
    innerJoin(
      cardPackCards,
      cardPackCards.packId.equalsExp(cardPacks.id),
    ),
  ])..where(cardPackCards.cardId.equals(cardId));
  
  final rows = await query.get();
  return rows.map((row) => row.readTable(cardPacks)).toList();
}

4.2 Файл: lib/api/v2/telegram_bot_api_v2.dart

Место использования:

  • Строка 83: 'packId': card.packId,

Исправление: Аналогично - использовать JOIN через CardPackCards


5. Исправить DateTime → PgDateTime

Затронутые файлы (найдено анализатором):

  1. lib/database/daos/discount_dao.dart:83 - updatedAt
  2. lib/database/daos/discount_dao.dart:110 - updatedAt
  3. lib/database/daos/promo_code_dao.dart:125 - updatedAt
  4. lib/database/daos/promo_code_dao.dart:135 - updatedAt
  5. lib/database/daos/test_dao.dart:99 - updatedAt
  6. lib/database/daos/user_dao.dart:210, 219, 230, 282, 300 - различные поля

Общее правило исправления:

// Было:
updatedAt: Value(DateTime.now()),

// Стало:
updatedAt: Value(PgDateTime(DateTime.now())),

Автоматизация: Можно использовать поиск и замену:

Найти: Value\(DateTime\.now\(\)\)
Заменить: Value(PgDateTime(DateTime.now()))

6. Реализовать недостающие методы

6.1 UserDao.deleteToken()

Файл: lib/database/daos/user_dao.dart

Использование:

  • lib/user/user_manager_drift.dart:51
  • lib/user/user_manager_drift.dart:72

Добавить метод:

/// Удалить токен
Future<void> deleteToken(String token) async {
  await (delete(tokens)..where((t) => t.token.equals(token))).go();
}

6.2 UserDao.deleteExpiredRefreshTokens()

Файл: lib/database/daos/user_dao.dart

Использование:

  • lib/api/v2/jwt_service.dart:281

Добавить метод:

/// Удалить истекшие refresh токены
Future<void> deleteExpiredRefreshTokens() async {
  final now = PgDateTime(DateTime.now());
  await (delete(refreshTokens)
    ..where((rt) => rt.expiresAt.isSmallerThanValue(now))
  ).go();
}

6.3 DiscountDao.deleteCampaign()

Файл: lib/database/daos/discount_dao.dart

Использование:

  • lib/discounts/discounts_manager.dart:161

Добавить метод:

/// Удалить кампанию (soft delete)
Future<void> deleteCampaign(String campaignId) async {
  await (update(discountCampaigns)
    ..where((c) => c.id.equals(campaignId)))
  .write(DiscountCampaignsCompanion(
    isDeleted: const Value(true),
    deletedAt: Value(PgDateTime(DateTime.now())),
    updatedAt: Value(PgDateTime(DateTime.now())),
  ));
}

6.4 PaymentDao.countPaymentsByUserId() - дубликат

Файл: lib/database/daos/payment_dao.dart

Проблема: Метод определен дважды (строки 96 и 164)

Исправление: Удалить одно из определений


7. Исправить card_pack_drift_extension.dart

Файл: lib/packs/card_pack_drift_extension.dart:86

Проблема: Используется несуществующий параметр packId в GameCardsCompanion

Текущий код:

packId: Value(pack.id),

Исправление: Удалить эту строку (packId больше нет в GameCards)


Порядок выполнения исправлений

Фаза 1: Простые исправления (30 минут)

  1. Исправить AuditLogs.tableName → table
  2. Исправить DateTime → PgDateTime (массовая замена)
  3. Удалить дубликат метода в PaymentDao
  4. Удалить packId из card_pack_drift_extension.dart

Фаза 2: Методы DAO (30 минут)

  1. Добавить deleteToken() в UserDao
  2. Добавить deleteExpiredRefreshTokens() в UserDao
  3. Добавить deleteCampaign() в DiscountDao

Фаза 3: WordStatisticsDao (20 минут)

  1. Переименовать update() → updateStatistics()
  2. Исправить типы в create()
  3. Обновить вызовы в WordStatisticsManager

Фаза 4: SoftDeleteMixin (30 минут)

  1. Упростить миксин (убрать softDelete и restore)
  2. Или реализовать softDelete вручную в PaymentDao, StatisticsDao

Фаза 5: card.packId (1 час)

  1. Добавить getPacksForCard() в PackDao
  2. Исправить admin_cards_api_v2.dart (5 мест)
  3. Исправить telegram_bot_api_v2.dart (1 место)

Фаза 6: Проверка (30 минут)

  1. Запустить dart analyze
  2. Запустить dart run build_runner build
  3. Проверить что нет ошибок

После исправлений

После всех исправлений:

  1. Запустить компиляцию: dart analyze
  2. Убедиться что 0 ошибок
  3. Запустить build_runner: dart run build_runner build --delete-conflicting-outputs
  4. Проверить что backend запускается: dart run bin/server.dart
  5. Запустить тесты: dart test

Только после этого можно переходить к Этапу 7: Тестирование.


Оценка времени

  • Фаза 1-3 (простые): ~1.5 часа
  • Фаза 4-5 (сложные): ~1.5 часа
  • Фаза 6 (проверка): ~0.5 часа

Итого: ~3.5 часа работы

После этого этапы 1-6 будут завершены на 100%.