user
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2025-12-14 03:13:49 +03:00
parent f66d66ae5c
commit 10f827534c
4 changed files with 76 additions and 38 deletions

View file

@ -219,15 +219,16 @@ class TasksSeederTask with cron_task.Task {
),
];
// Convert UserTaskModel to UserTasksCompanion and insert
// PostgreSQL автоматически генерирует UUID через DEFAULT gen_random_uuid()
// Используем InsertMode.insertOrIgnore с предопределенными UUID для идемпотентности
// Convert UserTaskModel to UserTasksCompanion and insert через DAO
// Используем предопределенные UUID для идемпотентности seeder'а
int insertedCount = 0;
int skippedCount = 0;
for (final taskData in tasksWithIds) {
final taskModel = taskData.task;
final rewardsJson = taskModel.rewards.map((r) => r.toJson()).toList();
await _db.into(_db.userTasks).insert(
UserTasksCompanion.insert(
final companion = UserTasksCompanion.insert(
id: Value(taskData.id), // Предопределенный UUID для идемпотентности seeder'а
title: taskModel.title,
description: taskModel.description,
@ -242,12 +243,17 @@ class TasksSeederTask with cron_task.Task {
instructions: Value(taskModel.instructions),
tags: Value(taskModel.tags),
imageUrl: Value(taskModel.imageUrl),
),
mode: InsertMode.insertOrIgnore, // ON CONFLICT DO NOTHING
);
final wasCreated = await _db.taskDao.createUserTaskOrIgnore(companion);
if (wasCreated) {
insertedCount++;
} else {
skippedCount++;
}
}
print('Successfully seeded ${tasksWithIds.length} user tasks (duplicates ignored)');
print('Successfully seeded user tasks: $insertedCount inserted, $skippedCount skipped (already exist)');
} catch (e) {
print('Error seeding tasks: $e');
rethrow;

View file

@ -83,6 +83,39 @@ class TaskDao extends DatabaseAccessor<AppDatabase> with _$TaskDaoMixin {
return inserted.id;
}
/// Создать задачу пользователя или игнорировать, если уже существует
/// Возвращает true, если задача была создана, false если уже существовала
///
/// Если task.id.present == true, проверяет существование перед вставкой
Future<bool> createUserTaskOrIgnore(UserTasksCompanion task) async {
// Если ID указан, проверяем существование перед вставкой
if (task.id.present) {
final existing = await getUserTaskById(task.id.value);
if (existing != null) {
return false; // Задача уже существует
}
}
try {
await into(db.userTasks).insert(
task,
mode: InsertMode.insertOrIgnore,
);
return true; // Задача была создана
} catch (e) {
// PostgreSQL error code 23505 = unique_violation (duplicate key)
// Это может произойти в редких случаях race condition
final errorString = e.toString();
if (errorString.contains('23505') ||
errorString.contains('duplicate key') ||
errorString.contains('unique constraint')) {
return false; // Задача уже существует
}
// Пробрасываем другие ошибки
rethrow;
}
}
/// Обновить задачу пользователя
Future<bool> updateUserTask(UserTask task) {
return update(db.userTasks).replace(task);

View file

@ -179,14 +179,6 @@ class AppDatabase extends _$AppDatabase {
final result = await customSelect('SELECT 1 as test').getSingle();
print('Database connection successful: ${result.data}');
// Удалить constraint valid_email если он существует
try {
await customStatement('ALTER TABLE users DROP CONSTRAINT IF EXISTS valid_email');
print('Removed valid_email constraint if it existed');
} catch (e) {
print('Warning: Could not remove valid_email constraint: $e');
}
// Включить foreign key constraints
await customStatement('SET CONSTRAINTS ALL IMMEDIATE');
},

View file

@ -125,21 +125,28 @@ class UserManager {
print('Creating new user $name $email');
// Create new user
// Create new user with user data in transaction
final now = DateTime.now();
final userCompanion = UsersCompanion.insert(
externalUserId: externalId,
name: drift.Value(name),
email: drift.Value(email),
// Explicitly set fields with default values to ensure they're returned by insertReturning
admin: drift.Value(false),
purchases: drift.Value([]),
createdAt: drift.Value(PgDateTime(now)),
updatedAt: drift.Value(PgDateTime(now)),
isDeleted: drift.Value(false),
);
final userId = await _db.userDao.createUser(userCompanion);
final userDataCompanion = UserDatasCompanion.insert(
userId: '', // Will be set by createUserWithData via copyWith
registrationDate: drift.Value(PgDateTime(now)),
);
// Create user data
await _db.userDao.createUserData(
UserDatasCompanion.insert(
userId: userId,
registrationDate: drift.Value(PgDateTime(DateTime.now())),
),
final userId = await _db.userDao.createUserWithData(
user: userCompanion,
userData: userDataCompanion,
);
final user = await _db.userDao.getUserById(userId);