370 lines
11 KiB
Markdown
370 lines
11 KiB
Markdown
# 🔥 Критичные исправления для завершения этапов 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%**.
|