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,35 +219,41 @@ 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(
|
||||
id: Value(taskData.id), // Предопределенный UUID для идемпотентности seeder'а
|
||||
title: taskModel.title,
|
||||
description: taskModel.description,
|
||||
type: taskModel.type,
|
||||
difficulty: taskModel.difficulty,
|
||||
status: taskModel.status,
|
||||
rewards: Value(rewardsJson),
|
||||
createdAt: Value(PgDateTime(taskModel.createdAt)),
|
||||
expiresAt: PgDateTime(taskModel.expiresAt),
|
||||
completedAt: Value(taskModel.completedAt?.let(PgDateTime.new)),
|
||||
proofUrl: Value(taskModel.proofUrl),
|
||||
instructions: Value(taskModel.instructions),
|
||||
tags: Value(taskModel.tags),
|
||||
imageUrl: Value(taskModel.imageUrl),
|
||||
),
|
||||
mode: InsertMode.insertOrIgnore, // ON CONFLICT DO NOTHING
|
||||
final companion = UserTasksCompanion.insert(
|
||||
id: Value(taskData.id), // Предопределенный UUID для идемпотентности seeder'а
|
||||
title: taskModel.title,
|
||||
description: taskModel.description,
|
||||
type: taskModel.type,
|
||||
difficulty: taskModel.difficulty,
|
||||
status: taskModel.status,
|
||||
rewards: Value(rewardsJson),
|
||||
createdAt: Value(PgDateTime(taskModel.createdAt)),
|
||||
expiresAt: PgDateTime(taskModel.expiresAt),
|
||||
completedAt: Value(taskModel.completedAt?.let(PgDateTime.new)),
|
||||
proofUrl: Value(taskModel.proofUrl),
|
||||
instructions: Value(taskModel.instructions),
|
||||
tags: Value(taskModel.tags),
|
||||
imageUrl: Value(taskModel.imageUrl),
|
||||
);
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,39 @@ class TaskDao extends DatabaseAccessor<AppDatabase> with _$TaskDaoMixin {
|
|||
final inserted = await into(db.userTasks).insertReturning(task);
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue