// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/repo/user/create.go package user import ( "context" "fmt" "time" "github.com/gocql/gocql" "go.uber.org/zap" dom_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/user" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation" ) func (impl userStorerImpl) Create(ctx context.Context, user *dom_user.User) error { // Ensure we have a valid UUID if user.ID == (gocql.UUID{}) { user.ID = gocql.TimeUUID() } // Set timestamps if not set now := time.Now() if user.CreatedAt.IsZero() { user.CreatedAt = now } if user.ModifiedAt.IsZero() { user.ModifiedAt = now } // Serialize complex data to JSON profileDataJSON, err := impl.serializeProfileData(user.ProfileData) if err != nil { return fmt.Errorf("failed to serialize profile data: %w", err) } securityDataJSON, err := impl.serializeSecurityData(user.SecurityData) if err != nil { return fmt.Errorf("failed to serialize security data: %w", err) } metadataJSON, err := impl.serializeMetadata(user.Metadata) if err != nil { return fmt.Errorf("failed to serialize metadata: %w", err) } // Use a batch for atomic writes across multiple tables batch := impl.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) // 1. Insert into users_by_id (primary table) batch.Query(` INSERT INTO users_by_id ( id, email, first_name, last_name, name, lexical_name, role, status, timezone, created_at, modified_at, profile_data, security_data, metadata ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, user.ID, user.Email, user.FirstName, user.LastName, user.Name, user.LexicalName, user.Role, user.Status, user.Timezone, user.CreatedAt, user.ModifiedAt, profileDataJSON, securityDataJSON, metadataJSON, ) // 2. Insert into users_by_email batch.Query(` INSERT INTO users_by_email ( email, id, first_name, last_name, name, lexical_name, role, status, timezone, created_at, modified_at, profile_data, security_data, metadata ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, user.Email, user.ID, user.FirstName, user.LastName, user.Name, user.LexicalName, user.Role, user.Status, user.Timezone, user.CreatedAt, user.ModifiedAt, profileDataJSON, securityDataJSON, metadataJSON, ) // 3. Insert into users_by_verification_code if verification code exists if user.SecurityData != nil && user.SecurityData.Code != "" { batch.Query(` INSERT INTO users_by_verification_code ( verification_code, id, email, first_name, last_name, name, lexical_name, role, status, timezone, created_at, modified_at, profile_data, security_data, metadata ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, user.SecurityData.Code, user.ID, user.Email, user.FirstName, user.LastName, user.Name, user.LexicalName, user.Role, user.Status, user.Timezone, user.CreatedAt, user.ModifiedAt, profileDataJSON, securityDataJSON, metadataJSON, ) } // 4. Insert into users_by_status_and_date for listing // Skip // 5. If status is active, also insert into active users table if user.Status == dom_user.UserStatusActive { // Skip } // 6. Add to search index (simplified - you might want to use external search) if user.Name != "" || user.Email != "" { // Skip } // Execute the batch if err := impl.session.ExecuteBatch(batch); err != nil { impl.logger.Error("Failed to create user", zap.String("user_id", user.ID.String()), zap.String("email", validation.MaskEmail(user.Email)), zap.Error(err)) return fmt.Errorf("failed to create user: %w", err) } impl.logger.Info("User created successfully", zap.String("user_id", user.ID.String()), zap.String("email", validation.MaskEmail(user.Email))) return nil }