package repository import ( dbgen "Yimaru-Backend/gen/db" "Yimaru-Backend/internal/domain" "context" "errors" "time" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) // type Store struct { // db *pgxpool.Pool // queries *dbgen.Queries // } // func NewStore(db *pgxpool.Pool) *Store { // return &Store{ // db: db, // queries: dbgen.New(db), // } // } // CreateUser inserts a new user into the database func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) { // Optional: mark OTP as used if usedOtpId > 0 { err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{ ID: usedOtpId, UsedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, }) if err != nil { return domain.User{}, err } } userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{ FirstName: user.FirstName, LastName: user.LastName, NickName: pgtype.Text{String: user.NickName}, Email: pgtype.Text{String: user.Email, Valid: user.Email != ""}, PhoneNumber: pgtype.Text{String: user.PhoneNumber, Valid: user.PhoneNumber != ""}, Role: string(user.Role), Password: user.Password, Age: pgtype.Int4{Int32: int32(user.Age), Valid: user.Age > 0}, EducationLevel: pgtype.Text{String: user.EducationLevel, Valid: user.EducationLevel != ""}, Country: pgtype.Text{String: user.Country, Valid: user.Country != ""}, Region: pgtype.Text{String: user.Region, Valid: user.Region != ""}, EmailVerified: user.EmailVerified, PhoneVerified: user.PhoneVerified, Suspended: user.Suspended, SuspendedAt: pgtype.Timestamptz{Time: user.SuspendedAt, Valid: !user.SuspendedAt.IsZero()}, OrganizationID: pgtype.Int8{Int64: user.OrganizationID.Value, Valid: user.OrganizationID.Valid}, CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, }) if err != nil { return domain.User{}, err } return domain.User{ ID: userRes.ID, FirstName: userRes.FirstName, LastName: userRes.LastName, NickName: userRes.NickName.String, Email: userRes.Email.String, PhoneNumber: userRes.PhoneNumber.String, Role: domain.Role(userRes.Role), Age: int(userRes.Age.Int32), EducationLevel: userRes.EducationLevel.String, Country: userRes.Country.String, Region: userRes.Region.String, EmailVerified: userRes.EmailVerified, PhoneVerified: userRes.PhoneVerified, Suspended: userRes.Suspended, SuspendedAt: userRes.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, CreatedAt: userRes.CreatedAt.Time, UpdatedAt: userRes.UpdatedAt.Time, }, nil } // GetUserByID retrieves a user by ID func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) { userRes, err := s.queries.GetUserByID(ctx, id) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.User{}, domain.ErrUserNotFound } return domain.User{}, err } return domain.User{ ID: userRes.ID, FirstName: userRes.FirstName, LastName: userRes.LastName, NickName: userRes.NickName.String, Email: userRes.Email.String, PhoneNumber: userRes.PhoneNumber.String, Role: domain.Role(userRes.Role), Age: int(userRes.Age.Int32), EducationLevel: userRes.EducationLevel.String, Country: userRes.Country.String, Region: userRes.Region.String, EmailVerified: userRes.EmailVerified, PhoneVerified: userRes.PhoneVerified, Suspended: userRes.Suspended, SuspendedAt: userRes.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, CreatedAt: userRes.CreatedAt.Time, UpdatedAt: userRes.UpdatedAt.Time, }, nil } // GetAllUsers retrieves users with optional filters func (s *Store) GetAllUsers(ctx context.Context, role *string, organizationID *int64, query *string, createdBefore, createdAfter *time.Time, limit, offset int32) ([]domain.User, error) { rows, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{ Role: *role, OrganizationID: pgtype.Int8{Int64: *organizationID}, Query: pgtype.Text{String: *query}, CreatedBefore: pgtype.Timestamptz{Time: *createdBefore}, CreatedAfter: pgtype.Timestamptz{Time: *createdAfter}, Limit: pgtype.Int4{Int32: limit}, Offset: pgtype.Int4{Int32: offset}, }) if err != nil { return nil, err } users := make([]domain.User, len(rows)) for i, u := range rows { users[i] = domain.User{ ID: u.ID, FirstName: u.FirstName, LastName: u.LastName, NickName: u.NickName.String, Email: u.Email.String, PhoneNumber: u.PhoneNumber.String, Role: domain.Role(u.Role), Age: int(u.Age.Int32), EducationLevel: u.EducationLevel.String, Country: u.Country.String, Region: u.Region.String, EmailVerified: u.EmailVerified, PhoneVerified: u.PhoneVerified, Suspended: u.Suspended, SuspendedAt: u.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, CreatedAt: u.CreatedAt.Time, UpdatedAt: u.UpdatedAt.Time, } } return users, nil } // GetTotalUsers counts users with optional filters func (s *Store) GetTotalUsers(ctx context.Context, role *string, organizationID *int64) (int64, error) { count, err := s.queries.GetTotalUsers(ctx, dbgen.GetTotalUsersParams{ Role: *role, OrganizationID: pgtype.Int8{Int64: *organizationID}, }) if err != nil { return 0, err } return count, nil } // SearchUserByNameOrPhone searches users by name or phone func (s *Store) SearchUserByNameOrPhone(ctx context.Context, search string, organizationID *int64, role *string) ([]domain.User, error) { rows, err := s.queries.SearchUserByNameOrPhone(ctx, dbgen.SearchUserByNameOrPhoneParams{ Column1: pgtype.Text{String: search}, OrganizationID: pgtype.Int8{Int64: *organizationID}, Role: pgtype.Text{String: *role}, }) if err != nil { return nil, err } users := make([]domain.User, len(rows)) for i, u := range rows { users[i] = domain.User{ ID: u.ID, FirstName: u.FirstName, LastName: u.LastName, NickName: u.NickName.String, Email: u.Email.String, PhoneNumber: u.PhoneNumber.String, Role: domain.Role(u.Role), Age: int(u.Age.Int32), EducationLevel: u.EducationLevel.String, Country: u.Country.String, Region: u.Region.String, EmailVerified: u.EmailVerified, PhoneVerified: u.PhoneVerified, Suspended: u.Suspended, SuspendedAt: u.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, CreatedAt: u.CreatedAt.Time, UpdatedAt: u.UpdatedAt.Time, } } return users, nil } // UpdateUser updates basic user info func (s *Store) UpdateUser(ctx context.Context, user domain.User) error { return s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{ FirstName: user.FirstName, LastName: user.LastName, Suspended: user.Suspended, ID: user.ID, }) } // UpdateUserOrganization updates a user's organization func (s *Store) UpdateUserOrganization(ctx context.Context, userID, organizationID int64) error { return s.queries.UpdateUserOrganization(ctx, dbgen.UpdateUserOrganizationParams{ OrganizationID: pgtype.Int8{Int64: organizationID, Valid: true}, ID: userID, }) } // SuspendUser suspends a user func (s *Store) SuspendUser(ctx context.Context, userID int64, suspended bool, suspendedAt time.Time) error { return s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{ Suspended: suspended, SuspendedAt: pgtype.Timestamptz{Time: suspendedAt, Valid: true}, ID: userID, }) } // DeleteUser removes a user func (s *Store) DeleteUser(ctx context.Context, userID int64) error { return s.queries.DeleteUser(ctx, userID) } // CheckPhoneEmailExist checks if phone or email exists in an organization func (s *Store) CheckPhoneEmailExist(ctx context.Context, phone, email string, organizationID int64) (phoneExists, emailExists bool, err error) { res, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{ PhoneNumber: pgtype.Text{String: phone}, Email: pgtype.Text{String: email}, OrganizationID: pgtype.Int8{Int64: organizationID}, }) if err != nil { return false, false, err } return res.PhoneExists, res.EmailExists, nil } // GetUserByEmail retrieves a user by email and organization func (s *Store) GetUserByEmail(ctx context.Context, email string, organizationID int64) (domain.User, error) { userRes, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{ Email: pgtype.Text{String: email}, OrganizationID: pgtype.Int8{Int64: organizationID}, }) if err != nil { return domain.User{}, err } return domain.User{ ID: userRes.ID, FirstName: userRes.FirstName, LastName: userRes.LastName, NickName: userRes.NickName.String, Email: userRes.Email.String, PhoneNumber: userRes.PhoneNumber.String, Role: domain.Role(userRes.Role), Age: int(userRes.Age.Int32), EducationLevel: userRes.EducationLevel.String, Country: userRes.Country.String, Region: userRes.Region.String, EmailVerified: userRes.EmailVerified, PhoneVerified: userRes.PhoneVerified, Suspended: userRes.Suspended, SuspendedAt: userRes.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, CreatedAt: userRes.CreatedAt.Time, UpdatedAt: userRes.UpdatedAt.Time, }, nil } // GetUserByPhone retrieves a user by phone and organization func (s *Store) GetUserByPhone(ctx context.Context, phone string, organizationID int64) (domain.User, error) { userRes, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{ PhoneNumber: pgtype.Text{String: phone}, OrganizationID: pgtype.Int8{Int64: organizationID}, }) if err != nil { return domain.User{}, err } return domain.User{ ID: userRes.ID, FirstName: userRes.FirstName, LastName: userRes.LastName, NickName: userRes.NickName.String, Email: userRes.Email.String, PhoneNumber: userRes.PhoneNumber.String, Role: domain.Role(userRes.Role), Age: int(userRes.Age.Int32), EducationLevel: userRes.EducationLevel.String, Country: userRes.Country.String, Region: userRes.Region.String, EmailVerified: userRes.EmailVerified, PhoneVerified: userRes.PhoneVerified, Suspended: userRes.Suspended, SuspendedAt: userRes.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, CreatedAt: userRes.CreatedAt.Time, UpdatedAt: userRes.UpdatedAt.Time, }, nil } // UpdatePassword updates a user's password func (s *Store) UpdatePassword(ctx context.Context, password, email, phone string, organizationID int64, updatedAt time.Time) error { return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{ Password: []byte(password), Email: pgtype.Text{String: email}, PhoneNumber: pgtype.Text{String: phone}, UpdatedAt: pgtype.Timestamptz{Time: updatedAt}, OrganizationID: pgtype.Int8{Int64: organizationID}, }) } // GetOwnerByOrganizationID retrieves the owner user of an organization func (s *Store) GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error) { userRes, err := s.queries.GetOwnerByOrganizationID(ctx, organizationID) if err != nil { return domain.User{}, err } return mapUser(userRes), nil } // mapUser converts dbgen.User to domain.User func mapUser(u dbgen.User) domain.User { return domain.User{ ID: u.ID, FirstName: u.FirstName, LastName: u.LastName, NickName: u.NickName.String, Email: u.Email.String, PhoneNumber: u.PhoneNumber.String, Role: domain.Role(u.Role), Age: int(u.Age.Int32), EducationLevel: u.EducationLevel.String, Country: u.Country.String, Region: u.Region.String, EmailVerified: u.EmailVerified, PhoneVerified: u.PhoneVerified, Suspended: u.Suspended, SuspendedAt: u.SuspendedAt.Time, OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, CreatedAt: u.CreatedAt.Time, UpdatedAt: u.UpdatedAt.Time, } }