Files
galaxy-game/user/internal/domain/account/model.go
T
2026-04-25 23:20:55 +02:00

102 lines
3.3 KiB
Go

// Package account defines the logical user-account entities owned directly by
// User Service.
package account
import (
"fmt"
"time"
"galaxy/user/internal/domain/common"
)
// UserAccount stores the current editable account state of one regular user.
type UserAccount struct {
// UserID identifies the durable regular-user account.
UserID common.UserID
// Email stores the normalized login/contact address of the account.
Email common.Email
// UserName stores the immutable auto-generated `player-<suffix>` handle.
UserName common.UserName
// DisplayName stores the optional mutable free-text user-facing label.
DisplayName common.DisplayName
// PreferredLanguage stores the current declared language tag.
PreferredLanguage common.LanguageTag
// TimeZone stores the current declared time-zone name.
TimeZone common.TimeZoneName
// DeclaredCountry stores the latest effective declared-country value. The
// zero value means the geo workflow has not synchronized any country yet.
DeclaredCountry common.CountryCode
// CreatedAt stores the account creation timestamp.
CreatedAt time.Time
// UpdatedAt stores the last account mutation timestamp.
UpdatedAt time.Time
// DeletedAt stores the soft-delete timestamp set by the `DeleteUser`
// command. A nil value means the account is live. A non-nil value marks
// the record as soft-deleted: external auth, self-service, admin-read,
// and lobby-eligibility operations must reject subsequent access with
// `subject_not_found`.
DeletedAt *time.Time
}
// IsDeleted reports whether the account has been soft-deleted through the
// `DeleteUser` command.
func (record UserAccount) IsDeleted() bool {
return record.DeletedAt != nil
}
// Validate reports whether UserAccount satisfies the Stage 21 structural
// invariants, including the Stage 22 soft-delete rules.
func (record UserAccount) Validate() error {
if err := record.UserID.Validate(); err != nil {
return fmt.Errorf("user account user id: %w", err)
}
if err := record.Email.Validate(); err != nil {
return fmt.Errorf("user account email: %w", err)
}
if err := record.UserName.Validate(); err != nil {
return fmt.Errorf("user account user name: %w", err)
}
if err := record.DisplayName.Validate(); err != nil {
return fmt.Errorf("user account display name: %w", err)
}
if err := record.PreferredLanguage.Validate(); err != nil {
return fmt.Errorf("user account preferred language: %w", err)
}
if err := record.TimeZone.Validate(); err != nil {
return fmt.Errorf("user account time zone: %w", err)
}
if !record.DeclaredCountry.IsZero() {
if err := record.DeclaredCountry.Validate(); err != nil {
return fmt.Errorf("user account declared country: %w", err)
}
}
if err := common.ValidateTimestamp("user account created at", record.CreatedAt); err != nil {
return err
}
if err := common.ValidateTimestamp("user account updated at", record.UpdatedAt); err != nil {
return err
}
if record.UpdatedAt.Before(record.CreatedAt) {
return fmt.Errorf("user account updated at must not be before created at")
}
if record.DeletedAt != nil {
if err := common.ValidateTimestamp("user account deleted at", *record.DeletedAt); err != nil {
return err
}
if record.DeletedAt.Before(record.CreatedAt) {
return fmt.Errorf("user account deleted at must not be before created at")
}
}
return nil
}