Files
fm_be/internal/service/user_write_service.go
2026-07-16 22:16:45 +07:00

161 lines
5.4 KiB
Go

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
}