package service import ( "context" "net/http" "strings" "wucher/internal/domain/user" ) type UserWriteError struct { Status int Title string Detail string Pointer string } func (e *UserWriteError) Error() string { if e == nil { return "" } return e.Detail } type UserWriteResult struct { Row *user.User PreviousProfileAttachmentID []byte } type UserCreateInput struct { ID []byte Email string Username string FirstName string LastName string MobilePhone string Timezone string RoleID []byte RoleIDs [][]byte ProfileAttachmentID []byte CreatedBy []byte UpdatedBy []byte } type UserUpdateInput struct { ID []byte Email *string FirstName *string LastName *string MobilePhone *string Timezone *string ProfileAttachmentSet bool ProfileAttachmentID []byte ActorUserID []byte } func (s *UserService) CreateDetailed(ctx context.Context, in UserCreateInput) (*UserWriteResult, error) { if s == nil || s.repo == nil { return nil, &UserWriteError{Status: http.StatusInternalServerError, Title: "Create failed", Detail: "user service is not configured"} } email := strings.TrimSpace(in.Email) username := strings.TrimSpace(in.Username) firstName := strings.TrimSpace(in.FirstName) lastName := strings.TrimSpace(in.LastName) if email == "" || username == "" || firstName == "" || lastName == "" { return nil, &UserWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "email, username, first_name, and last_name are required", Pointer: "/data/attributes"} } row := &user.User{ ID: append([]byte(nil), in.ID...), Email: email, Username: username, FirstName: firstName, LastName: lastName, MobilePhone: strings.TrimSpace(in.MobilePhone), Timezone: strings.TrimSpace(in.Timezone), RoleID: append([]byte(nil), in.RoleID...), RoleIDs: appendUniqueIDs(in.RoleIDs), ProfileAttachmentID: append([]byte(nil), in.ProfileAttachmentID...), ImageAttachmentID: append([]byte(nil), in.ProfileAttachmentID...), CreatedBy: append([]byte(nil), in.CreatedBy...), UpdatedBy: append([]byte(nil), in.UpdatedBy...), } if err := s.Create(ctx, row); err != nil { return nil, &UserWriteError{Status: http.StatusBadRequest, Title: "Create failed", Detail: err.Error()} } created, err := s.GetByID(ctx, row.ID) if err != nil { return nil, &UserWriteError{Status: http.StatusBadRequest, Title: "Create failed", Detail: err.Error()} } if created == nil { created = row } return &UserWriteResult{Row: created}, nil } func (s *UserService) UpdateDetailed(ctx context.Context, in UserUpdateInput) (*UserWriteResult, error) { if s == nil || s.repo == nil { return nil, &UserWriteError{Status: http.StatusInternalServerError, Title: "Update failed", Detail: "user service is not configured"} } if len(in.ID) != 16 { return nil, &UserWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "uuid is invalid UUID", Pointer: "/path/uuid"} } existing, err := s.GetByID(ctx, in.ID) if err != nil || existing == nil { return nil, &UserWriteError{Status: http.StatusNotFound, Title: "Not found", Detail: "user not found"} } prevProfileAttachmentID := append([]byte(nil), existing.ProfileAttachmentID...) if in.Email != nil { email := strings.TrimSpace(*in.Email) if email == "" { return nil, &UserWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "email cannot be empty", Pointer: "/data/attributes/email"} } existing.Email = email } if in.FirstName != nil { firstName := strings.TrimSpace(*in.FirstName) if firstName == "" { return nil, &UserWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "first_name cannot be empty", Pointer: "/data/attributes/first_name"} } existing.FirstName = firstName } if in.LastName != nil { lastName := strings.TrimSpace(*in.LastName) if lastName == "" { return nil, &UserWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "last_name cannot be empty", Pointer: "/data/attributes/last_name"} } existing.LastName = lastName } if in.MobilePhone != nil { existing.MobilePhone = strings.TrimSpace(*in.MobilePhone) } if in.Timezone != nil { existing.Timezone = strings.TrimSpace(*in.Timezone) } if in.ProfileAttachmentSet { if len(in.ProfileAttachmentID) == 0 { existing.ProfileAttachmentID = nil existing.ImageAttachmentID = nil } else { existing.ProfileAttachmentID = append([]byte(nil), in.ProfileAttachmentID...) existing.ImageAttachmentID = append([]byte(nil), in.ProfileAttachmentID...) } existing.ProfileAttachmentPatchSet = true } existing.UpdatedBy = append([]byte(nil), in.ActorUserID...) if err := s.Update(ctx, existing); err != nil { return nil, &UserWriteError{Status: http.StatusBadRequest, Title: "Update failed", Detail: err.Error()} } updated, err := s.GetByID(ctx, existing.ID) if err != nil { return nil, &UserWriteError{Status: http.StatusBadRequest, Title: "Update failed", Detail: err.Error()} } if updated == nil { updated = existing } return &UserWriteResult{Row: updated, PreviousProfileAttachmentID: prevProfileAttachmentID}, nil }