mnemo_cards/mnemo_cards_backend/CRITICAL_FIXES_TODO.md

371 lines
11 KiB
Markdown
Raw Normal View History

2026-01-03 13:14:27 +00:00
# 🔥 Критичные исправления для завершения этапов 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%**.