Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions api-service/internal/dto/request/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ type UserListRequest struct {
// UserCreateRequest defines the request structure for creating a user.
// Includes user basic information and supports assigning multiple roles via role_ids.
type UserCreateRequest struct {
Username string `json:"username" binding:"required,max=64" example:"johndoe"` // Username, 3-32 characters, letters, numbers, underscore
Username string `json:"username" binding:"required,min=3,max=32" example:"johndoe"` // Username, 3-32 characters, letters, numbers, underscore
Email string `json:"email" binding:"required,email" example:"john@example.com"` // Email address, must be unique
Password string `json:"password" binding:"required" example:"password123"` // Password, 8-32 characters, must contain uppercase
Password string `json:"password" binding:"required,min=8,max=32" example:"Password123"` // Password, 8-32 characters, must contain uppercase
Nickname *string `json:"nickname,omitempty" binding:"omitempty,max=64" example:"John Doe"` // User nickname (optional)
Phone *string `json:"phone,omitempty" binding:"omitempty,max=20" example:"+1234567890"` // Phone number (optional)
Phone *string `json:"phone,omitempty" binding:"omitempty,min=8,max=20" example:"+1234567890"` // Phone number (optional)
Avatar *string `json:"avatar,omitempty" binding:"omitempty,url" example:"https://example.com/avatar.jpg"` // Avatar URL (optional)
Gender *int `json:"gender,omitempty" binding:"omitempty,min=0,max=2" example:"1"` // Gender: 1-male, 2-female, 0-unknown (optional)
Signature *string `json:"signature,omitempty" binding:"omitempty,max=255" example:"This is my signature"` // Personal signature (optional)
Expand All @@ -49,10 +49,10 @@ type UserCreateRequest struct {

// UserUpdateRequest
type UserUpdateRequest struct {
Username *string `json:"username,omitempty" binding:"omitempty,max=64" example:"johndoe"`
Username *string `json:"username,omitempty" binding:"omitempty,min=3,max=32" example:"johndoe"`
Email *string `json:"email,omitempty" binding:"omitempty,email" example:"newemail@example.com"`
Nickname *string `json:"nickname,omitempty" binding:"omitempty,max=64" example:"John Doe"`
Phone *string `json:"phone,omitempty" binding:"omitempty,max=20" example:"+1234567890"`
Phone *string `json:"phone,omitempty" binding:"omitempty,min=8,max=20" example:"+1234567890"`
Avatar *string `json:"avatar,omitempty" binding:"omitempty,url" example:"https://example.com/avatar.jpg"`
Gender *int `json:"gender,omitempty" binding:"omitempty,min=0,max=2" example:"1"`
Signature *string `json:"signature,omitempty" binding:"omitempty,max=255" example:"This is my signature"`
Expand All @@ -68,7 +68,7 @@ type UserUpdateStatusRequest struct {

// UserPasswordUpdateRequest
type UserPasswordUpdateRequest struct {
NewPassword string `json:"new_password" binding:"required,min=6" example:"newpass123"`
NewPassword string `json:"new_password" binding:"required,min=8,max=32" example:"newpass123"`
ConfirmPassword string `json:"confirm_password" binding:"required,eqfield=NewPassword" example:"newpass123"`
}

Expand Down
28 changes: 28 additions & 0 deletions api-service/internal/service/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"api-service/pkg/errors"
"api-service/pkg/logger"
"context"
"strings"
"time"
)

Expand Down Expand Up @@ -163,6 +164,11 @@ func (s *userService) UpdateUser(ctx context.Context, currentUserID, userID uint
}
}

// 2.5. If updating phone, check format
if req.Phone != nil && !validatePhoneFormat(*req.Phone) {
return nil, errors.NewAppError(errors.CodeInvalidPhoneFormat)
}

// 3. Update user info
s.updateUserFromRequest(user, req)

Expand Down Expand Up @@ -335,6 +341,23 @@ func (s *userService) validateEmailUniqueness(ctx context.Context, email string,
}

// validateUserCreation validates user creation

// validatePhoneFormat validates phone number format (must start with + and contain only digits)
func validatePhoneFormat(phone string) bool {
if phone == "" {
return true // Phone is optional
}
if !strings.HasPrefix(phone, "+") {
return false
}
// Check remaining characters are digits
for _, ch := range phone[1:] {
if ch < '0' || ch > '9' {
return false
}
}
return true
}
func (s *userService) validateUserCreation(ctx context.Context, req *request.UserCreateRequest) error {
// Check if username exists
exists, err := s.userRepo.ExistsByUsername(ctx, req.Username)
Expand All @@ -346,6 +369,11 @@ func (s *userService) validateUserCreation(ctx context.Context, req *request.Use
return errors.ErrUserAlreadyExists
}

// Check phone format (only if provided)
if req.Phone != nil && !validatePhoneFormat(*req.Phone) {
return errors.NewAppError(errors.CodeInvalidPhoneFormat)
}

// Check if email exists
exists, err = s.userRepo.ExistsByEmail(ctx, req.Email)
if err != nil {
Expand Down
Loading