feat: backend service

This commit is contained in:
Ilia Denisov
2026-05-06 10:14:55 +03:00
committed by GitHub
parent 3e2622757e
commit f446c6a2ac
1486 changed files with 49720 additions and 266401 deletions
+77
View File
@@ -0,0 +1,77 @@
package user
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/google/uuid"
)
// ActiveLimit is the read-side projection of a row in `limit_active`
// joined with the audit columns from the underlying `limit_records`
// row. It mirrors the OpenAPI `ActiveLimit` schema.
type ActiveLimit struct {
LimitCode string
Value int32
ReasonCode string
Actor ActorRef
AppliedAt time.Time
ExpiresAt *time.Time
}
// ApplyLimitInput carries the admin-supplied parameters of
// `POST /api/v1/admin/users/{user_id}/limits`.
type ApplyLimitInput struct {
UserID uuid.UUID
LimitCode string
Value int32
ReasonCode string
Actor ActorRef
ExpiresAt *time.Time
}
// ApplyLimit persists a fresh `limit_records` row and upserts
// `limit_active` in one transaction. The implementation keeps `limit_code` as an
// open string; The implementation may add a CHECK constraint once the closed
// set is locked in.
func (s *Service) ApplyLimit(ctx context.Context, input ApplyLimitInput) (Account, error) {
if input.UserID == uuid.Nil {
return Account{}, ErrAccountNotFound
}
if strings.TrimSpace(input.LimitCode) == "" {
return Account{}, fmt.Errorf("%w: limit_code must be non-empty", ErrInvalidInput)
}
if err := input.Actor.Validate(); err != nil {
return Account{}, err
}
if strings.TrimSpace(input.ReasonCode) == "" {
return Account{}, fmt.Errorf("%w: reason_code must be non-empty", ErrInvalidInput)
}
now := s.deps.Now().UTC()
expiresAt := input.ExpiresAt
if expiresAt != nil {
t := expiresAt.UTC()
expiresAt = &t
}
if err := s.deps.Store.ApplyLimitTx(ctx, limitInsert{
UserID: input.UserID,
LimitCode: input.LimitCode,
Value: input.Value,
ReasonCode: input.ReasonCode,
ActorType: input.Actor.Type,
ActorID: input.Actor.ID,
AppliedAt: now,
ExpiresAt: expiresAt,
}); err != nil {
if errors.Is(err, ErrAccountNotFound) {
return Account{}, err
}
return Account{}, fmt.Errorf("user apply limit: %w", err)
}
return s.GetAccount(ctx, input.UserID)
}