package subject import ( "context" "fmt" "strings" "time" "unicode" "unicode/utf8" "github.com/google/uuid" ) const ( maximumSubjectFieldRunes = 128 maximumPageSize = 100 ) type Service struct { repository *Repository } func NewService(repository *Repository) (*Service, error) { if repository == nil || repository.db == nil { return nil, ErrInvalidInput } return &Service{repository: repository}, nil } func (s *Service) Create(ctx context.Context, input UpsertInput) (Subject, error) { if !s.usable(ctx) { return Subject{}, ErrInvalidInput } normalized, err := normalizeInput(input) if err != nil { return Subject{}, err } id, err := uuid.NewRandom() if err != nil { return Subject{}, fmt.Errorf("生成授权对象 UUID v4: %w", err) } now := time.Now().UTC() return s.repository.Create(ctx, Subject{ ID: id, PlatformName: normalized.PlatformName, Workspace: normalized.Workspace, CreatedAt: now, UpdatedAt: now, }) } func (s *Service) Get(ctx context.Context, id uuid.UUID) (Subject, error) { if !s.usable(ctx) || id == uuid.Nil { return Subject{}, ErrInvalidInput } return s.repository.Get(ctx, id) } func (s *Service) List(ctx context.Context, query string, page, pageSize int) ([]Subject, int64, error) { if !s.usable(ctx) || page < 1 || pageSize < 1 || pageSize > maximumPageSize { return nil, 0, ErrInvalidInput } return s.repository.List(ctx, strings.TrimSpace(query), page, pageSize) } func (s *Service) Update(ctx context.Context, id uuid.UUID, input UpsertInput) (Subject, error) { if !s.usable(ctx) || id == uuid.Nil { return Subject{}, ErrInvalidInput } normalized, err := normalizeInput(input) if err != nil { return Subject{}, err } return s.repository.UpdateIfUnsigned(ctx, id, normalized) } func (s *Service) Delete(ctx context.Context, id uuid.UUID) error { if !s.usable(ctx) || id == uuid.Nil { return ErrInvalidInput } return s.repository.DeleteIfUnsigned(ctx, id) } func (s *Service) usable(ctx context.Context) bool { return s != nil && s.repository != nil && s.repository.db != nil && ctx != nil } func normalizeInput(input UpsertInput) (UpsertInput, error) { input.PlatformName = strings.TrimSpace(input.PlatformName) input.Workspace = strings.TrimSpace(input.Workspace) if !validSubjectField(input.PlatformName) || !validSubjectField(input.Workspace) { return UpsertInput{}, ErrInvalidInput } return input, nil } func validSubjectField(value string) bool { if value == "" || !utf8.ValidString(value) || utf8.RuneCountInString(value) > maximumSubjectFieldRunes { return false } for _, character := range value { if unicode.IsControl(character) { return false } } return true }