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
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:
parent
f66d66ae5c
commit
10f827534c
4 changed files with 76 additions and 38 deletions
|
|
@ -219,15 +219,16 @@ class TasksSeederTask with cron_task.Task {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Convert UserTaskModel to UserTasksCompanion and insert
|
// Convert UserTaskModel to UserTasksCompanion and insert через DAO
|
||||||
// PostgreSQL автоматически генерирует UUID через DEFAULT gen_random_uuid()
|
// Используем предопределенные UUID для идемпотентности seeder'а
|
||||||
// Используем InsertMode.insertOrIgnore с предопределенными UUID для идемпотентности
|
int insertedCount = 0;
|
||||||
|
int skippedCount = 0;
|
||||||
|
|
||||||
for (final taskData in tasksWithIds) {
|
for (final taskData in tasksWithIds) {
|
||||||
final taskModel = taskData.task;
|
final taskModel = taskData.task;
|
||||||
final rewardsJson = taskModel.rewards.map((r) => r.toJson()).toList();
|
final rewardsJson = taskModel.rewards.map((r) => r.toJson()).toList();
|
||||||
|
|
||||||
await _db.into(_db.userTasks).insert(
|
final companion = UserTasksCompanion.insert(
|
||||||
UserTasksCompanion.insert(
|
|
||||||
id: Value(taskData.id), // Предопределенный UUID для идемпотентности seeder'а
|
id: Value(taskData.id), // Предопределенный UUID для идемпотентности seeder'а
|
||||||
title: taskModel.title,
|
title: taskModel.title,
|
||||||
description: taskModel.description,
|
description: taskModel.description,
|
||||||
|
|
@ -242,12 +243,17 @@ class TasksSeederTask with cron_task.Task {
|
||||||
instructions: Value(taskModel.instructions),
|
instructions: Value(taskModel.instructions),
|
||||||
tags: Value(taskModel.tags),
|
tags: Value(taskModel.tags),
|
||||||
imageUrl: Value(taskModel.imageUrl),
|
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) {
|
} catch (e) {
|
||||||
print('Error seeding tasks: $e');
|
print('Error seeding tasks: $e');
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,39 @@ class TaskDao extends DatabaseAccessor<AppDatabase> with _$TaskDaoMixin {
|
||||||
return inserted.id;
|
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) {
|
Future<bool> updateUserTask(UserTask task) {
|
||||||
return update(db.userTasks).replace(task);
|
return update(db.userTasks).replace(task);
|
||||||
|
|
|
||||||
|
|
@ -179,14 +179,6 @@ class AppDatabase extends _$AppDatabase {
|
||||||
final result = await customSelect('SELECT 1 as test').getSingle();
|
final result = await customSelect('SELECT 1 as test').getSingle();
|
||||||
print('Database connection successful: ${result.data}');
|
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
|
// Включить foreign key constraints
|
||||||
await customStatement('SET CONSTRAINTS ALL IMMEDIATE');
|
await customStatement('SET CONSTRAINTS ALL IMMEDIATE');
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -125,21 +125,28 @@ class UserManager {
|
||||||
|
|
||||||
print('Creating new user $name $email');
|
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(
|
final userCompanion = UsersCompanion.insert(
|
||||||
externalUserId: externalId,
|
externalUserId: externalId,
|
||||||
name: drift.Value(name),
|
name: drift.Value(name),
|
||||||
email: drift.Value(email),
|
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
|
final userId = await _db.userDao.createUserWithData(
|
||||||
await _db.userDao.createUserData(
|
user: userCompanion,
|
||||||
UserDatasCompanion.insert(
|
userData: userDataCompanion,
|
||||||
userId: userId,
|
|
||||||
registrationDate: drift.Value(PgDateTime(DateTime.now())),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final user = await _db.userDao.getUserById(userId);
|
final user = await _db.userDao.getUserById(userId);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue