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

370 lines
11 KiB
Markdown
Raw Permalink 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.

# 🔥 Критичные исправления для завершения этапов 1-6
**Приоритет:** ВЫСОКИЙ
**Блокирует:** Компиляцию проекта (88 ошибок)
---
## 1. ❌ Исправить SoftDeleteMixin
**Файл:** `lib/database/daos/mixins/soft_delete_mixin.dart`
**Проблема:** Метод `table.companion()` не существует в Drift
**Текущий код (строки 40-44):**
```dart
.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)
**Текущий код:**
```dart
Future<void> update({
required String id,
required int correctAnswers,
required int incorrectAnswers,
required DateTime lastReviewed,
}) async {
```
**Исправление:** Переименовать метод
```dart
Future<void> updateStatistics({
required String id,
required int correctAnswers,
required int incorrectAnswers,
required DateTime lastReviewed,
}) async {
```
### Проблема 2.2: Неверные типы в create() (строки 42-45)
**Текущий код:**
```dart
correctAnswers: correctAnswers,
incorrectAnswers: incorrectAnswers,
mastery: mastery,
lastReviewed: Value(lastReviewed),
```
**Исправление:**
```dart
correctAnswers: Value(correctAnswers),
incorrectAnswers: Value(incorrectAnswers),
mastery: Value(mastery),
lastReviewed: Value(PgDateTime(lastReviewed)),
```
### Проблема 2.3: Ошибки в вызове update (строки 64-71)
**Текущий код:**
```dart
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()),
),
);
```
**Исправление:**
```dart
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)
**Текущий код:**
```dart
await _db.wordStatisticsDao.update(
id: existing.id,
correctAnswers: newCorrect,
incorrectAnswers: newIncorrect,
lastReviewed: DateTime.now(),
);
```
**Исправление:**
```dart
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<String>
**Текущий код (строка 21):**
```dart
TextColumn get tableName => text()();
```
**Исправление:** Переименовать поле, чтобы не конфликтовать с Table.tableName
```dart
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
**Пример исправления (для чтения):**
```dart
// Вместо:
'packId': card.packId,
// Использовать:
final packs = await _db.packDao.getPacksForCard(card.id);
'packId': packs.isNotEmpty ? packs.first.id : null,
```
**Нужно добавить метод в PackDao:**
```dart
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` - различные поля
**Общее правило исправления:**
```dart
// Было:
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`
**Добавить метод:**
```dart
/// Удалить токен
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`
**Добавить метод:**
```dart
/// Удалить истекшие 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`
**Добавить метод:**
```dart
/// Удалить кампанию (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
**Текущий код:**
```dart
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 минут)
5. ✅ Добавить deleteToken() в UserDao
6. ✅ Добавить deleteExpiredRefreshTokens() в UserDao
7. ✅ Добавить deleteCampaign() в DiscountDao
### Фаза 3: WordStatisticsDao (20 минут)
8. ✅ Переименовать update() → updateStatistics()
9. ✅ Исправить типы в create()
10. ✅ Обновить вызовы в WordStatisticsManager
### Фаза 4: SoftDeleteMixin (30 минут)
11. ✅ Упростить миксин (убрать softDelete и restore)
12. ✅ Или реализовать softDelete вручную в PaymentDao, StatisticsDao
### Фаза 5: card.packId (1 час)
13. ✅ Добавить getPacksForCard() в PackDao
14. ✅ Исправить admin_cards_api_v2.dart (5 мест)
15. ✅ Исправить telegram_bot_api_v2.dart (1 место)
### Фаза 6: Проверка (30 минут)
16. ✅ Запустить `dart analyze`
17. ✅ Запустить `dart run build_runner build`
18. ✅ Проверить что нет ошибок
---
## После исправлений
После всех исправлений:
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%**.