100 lines
3.4 KiB
Go
100 lines
3.4 KiB
Go
package ports
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"galaxy/user/internal/domain/common"
|
|
)
|
|
|
|
// UserLifecycleEventType identifies one user-lifecycle event kind propagated
|
|
// to `Game Lobby` through the dedicated Redis Stream.
|
|
type UserLifecycleEventType string
|
|
|
|
const (
|
|
// UserLifecyclePermanentBlockedEventType identifies the post-commit event
|
|
// emitted when `SanctionCodePermanentBlock` becomes active on an account.
|
|
UserLifecyclePermanentBlockedEventType UserLifecycleEventType = "user.lifecycle.permanent_blocked"
|
|
|
|
// UserLifecycleDeletedEventType identifies the post-commit event emitted
|
|
// when a trusted `DeleteUser` command soft-deletes an account.
|
|
UserLifecycleDeletedEventType UserLifecycleEventType = "user.lifecycle.deleted"
|
|
)
|
|
|
|
// IsKnown reports whether the event type belongs to the frozen vocabulary.
|
|
func (eventType UserLifecycleEventType) IsKnown() bool {
|
|
switch eventType {
|
|
case UserLifecyclePermanentBlockedEventType, UserLifecycleDeletedEventType:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// UserLifecycleEvent stores one post-commit user-lifecycle event envelope
|
|
// published to the `user:lifecycle_events` Redis Stream and consumed by
|
|
// `Game Lobby` for Race Name Directory cascade release.
|
|
type UserLifecycleEvent struct {
|
|
// EventType stores the frozen lifecycle event discriminator.
|
|
EventType UserLifecycleEventType
|
|
|
|
// UserID identifies the regular user whose lifecycle state changed.
|
|
UserID common.UserID
|
|
|
|
// OccurredAt stores the committed mutation timestamp.
|
|
OccurredAt time.Time
|
|
|
|
// Source stores the machine-readable mutation source. For Stage 22 this is
|
|
// always `admin_internal_api`.
|
|
Source common.Source
|
|
|
|
// Actor stores the audit actor metadata attached to the committed
|
|
// mutation.
|
|
Actor common.ActorRef
|
|
|
|
// ReasonCode stores the committed reason_code for the mutation.
|
|
ReasonCode common.ReasonCode
|
|
|
|
// TraceID stores the optional OpenTelemetry trace identifier propagated
|
|
// from the current request context.
|
|
TraceID string
|
|
}
|
|
|
|
// Validate reports whether event is structurally complete.
|
|
func (event UserLifecycleEvent) Validate() error {
|
|
if !event.EventType.IsKnown() {
|
|
return fmt.Errorf("user lifecycle event type %q is unsupported", event.EventType)
|
|
}
|
|
if err := event.UserID.Validate(); err != nil {
|
|
return fmt.Errorf("user lifecycle event user id: %w", err)
|
|
}
|
|
if err := common.ValidateTimestamp("user lifecycle event occurred at", event.OccurredAt); err != nil {
|
|
return err
|
|
}
|
|
if err := event.Source.Validate(); err != nil {
|
|
return fmt.Errorf("user lifecycle event source: %w", err)
|
|
}
|
|
if err := event.Actor.Validate(); err != nil {
|
|
return fmt.Errorf("user lifecycle event actor: %w", err)
|
|
}
|
|
if err := event.ReasonCode.Validate(); err != nil {
|
|
return fmt.Errorf("user lifecycle event reason code: %w", err)
|
|
}
|
|
if event.TraceID != "" && strings.TrimSpace(event.TraceID) != event.TraceID {
|
|
return fmt.Errorf("user lifecycle event trace id must not contain surrounding whitespace")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UserLifecyclePublisher publishes one committed user-lifecycle event to the
|
|
// dedicated `user:lifecycle_events` Redis Stream.
|
|
type UserLifecyclePublisher interface {
|
|
// PublishUserLifecycleEvent propagates one committed lifecycle event. The
|
|
// implementation must validate the event envelope and perform exactly one
|
|
// idempotent append per call.
|
|
PublishUserLifecycleEvent(ctx context.Context, event UserLifecycleEvent) error
|
|
}
|