189 lines
7.3 KiB
Go
189 lines
7.3 KiB
Go
package ports
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"galaxy/user/internal/domain/common"
|
|
"galaxy/user/internal/domain/policy"
|
|
)
|
|
|
|
// SanctionStore persists sanction history records and later remove-state
|
|
// updates.
|
|
type SanctionStore interface {
|
|
// Create stores one new sanction history record. Implementations must wrap
|
|
// ErrConflict when record.RecordID already exists.
|
|
Create(ctx context.Context, record policy.SanctionRecord) error
|
|
|
|
// GetByRecordID returns the sanction history record identified by recordID.
|
|
GetByRecordID(ctx context.Context, recordID policy.SanctionRecordID) (policy.SanctionRecord, error)
|
|
|
|
// ListByUserID returns every sanction history record owned by userID.
|
|
ListByUserID(ctx context.Context, userID common.UserID) ([]policy.SanctionRecord, error)
|
|
|
|
// Update replaces one stored sanction history record.
|
|
Update(ctx context.Context, record policy.SanctionRecord) error
|
|
}
|
|
|
|
// LimitStore persists user-specific limit history records and later
|
|
// remove-state updates.
|
|
type LimitStore interface {
|
|
// Create stores one new limit history record. Implementations must wrap
|
|
// ErrConflict when record.RecordID already exists.
|
|
Create(ctx context.Context, record policy.LimitRecord) error
|
|
|
|
// GetByRecordID returns the limit history record identified by recordID.
|
|
GetByRecordID(ctx context.Context, recordID policy.LimitRecordID) (policy.LimitRecord, error)
|
|
|
|
// ListByUserID returns every limit history record owned by userID.
|
|
ListByUserID(ctx context.Context, userID common.UserID) ([]policy.LimitRecord, error)
|
|
|
|
// Update replaces one stored limit history record.
|
|
Update(ctx context.Context, record policy.LimitRecord) error
|
|
}
|
|
|
|
// ApplySanctionInput stores one atomic creation of a new active sanction.
|
|
type ApplySanctionInput struct {
|
|
// NewRecord stores the sanction history record that must become active.
|
|
NewRecord policy.SanctionRecord
|
|
}
|
|
|
|
// Validate reports whether ApplySanctionInput is structurally complete.
|
|
func (input ApplySanctionInput) Validate() error {
|
|
if err := input.NewRecord.Validate(); err != nil {
|
|
return fmt.Errorf("apply sanction input new record: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveSanctionInput stores one atomic removal of the current active
|
|
// sanction for one `user_id + sanction_code`.
|
|
type RemoveSanctionInput struct {
|
|
// ExpectedActiveRecord stores the exact sanction record that must still be
|
|
// active before the mutation commits.
|
|
ExpectedActiveRecord policy.SanctionRecord
|
|
|
|
// UpdatedRecord stores ExpectedActiveRecord after remove metadata is
|
|
// applied.
|
|
UpdatedRecord policy.SanctionRecord
|
|
}
|
|
|
|
// Validate reports whether RemoveSanctionInput is structurally complete.
|
|
func (input RemoveSanctionInput) Validate() error {
|
|
if err := input.ExpectedActiveRecord.Validate(); err != nil {
|
|
return fmt.Errorf("remove sanction input expected active record: %w", err)
|
|
}
|
|
if err := input.UpdatedRecord.Validate(); err != nil {
|
|
return fmt.Errorf("remove sanction input updated record: %w", err)
|
|
}
|
|
if input.ExpectedActiveRecord.RecordID != input.UpdatedRecord.RecordID {
|
|
return fmt.Errorf("remove sanction input updated record must preserve record id")
|
|
}
|
|
if input.ExpectedActiveRecord.UserID != input.UpdatedRecord.UserID {
|
|
return fmt.Errorf("remove sanction input records must belong to the same user id")
|
|
}
|
|
if input.ExpectedActiveRecord.SanctionCode != input.UpdatedRecord.SanctionCode {
|
|
return fmt.Errorf("remove sanction input records must preserve sanction code")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetLimitInput stores one atomic creation or replacement of the current
|
|
// active limit for one `user_id + limit_code`.
|
|
type SetLimitInput struct {
|
|
// ExpectedActiveRecord stores the currently active limit that must still be
|
|
// active before replacement commits. It stays nil when no active limit
|
|
// exists yet.
|
|
ExpectedActiveRecord *policy.LimitRecord
|
|
|
|
// UpdatedActiveRecord stores ExpectedActiveRecord after remove metadata is
|
|
// applied. It stays nil when no active limit exists yet.
|
|
UpdatedActiveRecord *policy.LimitRecord
|
|
|
|
// NewRecord stores the limit history record that must become active.
|
|
NewRecord policy.LimitRecord
|
|
}
|
|
|
|
// Validate reports whether SetLimitInput is structurally complete.
|
|
func (input SetLimitInput) Validate() error {
|
|
if err := input.NewRecord.Validate(); err != nil {
|
|
return fmt.Errorf("set limit input new record: %w", err)
|
|
}
|
|
switch {
|
|
case input.ExpectedActiveRecord == nil && input.UpdatedActiveRecord == nil:
|
|
return nil
|
|
case input.ExpectedActiveRecord == nil || input.UpdatedActiveRecord == nil:
|
|
return fmt.Errorf("set limit input active replacement records must both be present or absent")
|
|
}
|
|
if err := input.ExpectedActiveRecord.Validate(); err != nil {
|
|
return fmt.Errorf("set limit input expected active record: %w", err)
|
|
}
|
|
if err := input.UpdatedActiveRecord.Validate(); err != nil {
|
|
return fmt.Errorf("set limit input updated active record: %w", err)
|
|
}
|
|
if input.ExpectedActiveRecord.RecordID != input.UpdatedActiveRecord.RecordID {
|
|
return fmt.Errorf("set limit input updated active record must preserve record id")
|
|
}
|
|
if input.ExpectedActiveRecord.UserID != input.UpdatedActiveRecord.UserID ||
|
|
input.ExpectedActiveRecord.UserID != input.NewRecord.UserID {
|
|
return fmt.Errorf("set limit input records must belong to the same user id")
|
|
}
|
|
if input.ExpectedActiveRecord.LimitCode != input.UpdatedActiveRecord.LimitCode ||
|
|
input.ExpectedActiveRecord.LimitCode != input.NewRecord.LimitCode {
|
|
return fmt.Errorf("set limit input records must preserve limit code")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveLimitInput stores one atomic removal of the current active limit for
|
|
// one `user_id + limit_code`.
|
|
type RemoveLimitInput struct {
|
|
// ExpectedActiveRecord stores the exact limit record that must still be
|
|
// active before the mutation commits.
|
|
ExpectedActiveRecord policy.LimitRecord
|
|
|
|
// UpdatedRecord stores ExpectedActiveRecord after remove metadata is
|
|
// applied.
|
|
UpdatedRecord policy.LimitRecord
|
|
}
|
|
|
|
// Validate reports whether RemoveLimitInput is structurally complete.
|
|
func (input RemoveLimitInput) Validate() error {
|
|
if err := input.ExpectedActiveRecord.Validate(); err != nil {
|
|
return fmt.Errorf("remove limit input expected active record: %w", err)
|
|
}
|
|
if err := input.UpdatedRecord.Validate(); err != nil {
|
|
return fmt.Errorf("remove limit input updated record: %w", err)
|
|
}
|
|
if input.ExpectedActiveRecord.RecordID != input.UpdatedRecord.RecordID {
|
|
return fmt.Errorf("remove limit input updated record must preserve record id")
|
|
}
|
|
if input.ExpectedActiveRecord.UserID != input.UpdatedRecord.UserID {
|
|
return fmt.Errorf("remove limit input records must belong to the same user id")
|
|
}
|
|
if input.ExpectedActiveRecord.LimitCode != input.UpdatedRecord.LimitCode {
|
|
return fmt.Errorf("remove limit input records must preserve limit code")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PolicyLifecycleStore persists atomic sanction and limit transitions that
|
|
// must keep history and active-slot state consistent.
|
|
type PolicyLifecycleStore interface {
|
|
// ApplySanction atomically creates one new active sanction record.
|
|
ApplySanction(ctx context.Context, input ApplySanctionInput) error
|
|
|
|
// RemoveSanction atomically removes one active sanction record.
|
|
RemoveSanction(ctx context.Context, input RemoveSanctionInput) error
|
|
|
|
// SetLimit atomically creates or replaces one active limit record.
|
|
SetLimit(ctx context.Context, input SetLimitInput) error
|
|
|
|
// RemoveLimit atomically removes one active limit record.
|
|
RemoveLimit(ctx context.Context, input RemoveLimitInput) error
|
|
}
|