EnakGame phases 1-8 of docs/tasks-enakgame.md (EG-101 to EG-803), built on the existing EnakPoint/EnakCoin wallet (docs/rfc-enakgame.md). Foundation (phase 1) - Migrations 000103-000106: games extended with organization, slug, status, entry cost and result rules, old games archived (not deleted); budgets, versioned reward configs, sessions and session rewards; the ledger types GAME_SPEND_REFUND, GAME_REWARD and REWARD_REDEEM_REFUND; audit_logs. - AuditLogger writes in the caller's transaction only. - enakgame.limit.user_daily and global_daily organization settings. Games and sessions (phases 2-4) - Admin /marketing/enakgame: games, reward config versions (immutable but for status, one ACTIVE per game), budgets with non-overlapping global periods and a daily job opening the next month. - Customer /customer/enakgame: start (Idempotency-Key, entry cost and config frozen on the session), complete (result validation, reward engine, max_reward cap, daily limits via game_reward_counters, one GAME_REWARD per budget), automatic refunds for system errors and deactivated games, and a session job. - Reward engine: FIXED, SCORE_BASED, OUTCOME_BASED, PROBABILITY (crypto/rand), rounded down. Vouchers and budgets (phases 5-6) - Migration 000108 and 000107: vouchers, codes, redemptions, cost attribution; Economy Guard counters. - STATIC and CODE_POOL redemption in one transaction with the REDEEM PIN action; realized cost traced through the lots to the budget that paid the reward. - Budget metrics: realized cost, forecast, exposure and status. Migrations 000109-000110 add the wallet_lots indexes they need, built CONCURRENTLY. Events (phase 7) - Migration 000111: game events, each with its own EVENT budget. Event extras stack per PRD §16 defaults, with event and per-customer limits. External vouchers (phase 8) - VoucherProvider contract, two-step PENDING redemption and a recovery job, tested with a fake provider. No provider adapter is registered yet, so EXTERNAL vouchers stay out of the catalog. Not yet decided before release: reward rounding, event stacking, budget exhaustion policy and thresholds (RFC §19.2). Migrations 000103-000111 have not been run on any shared database. Also fixes a leftover PAYMENT filter in a wallet test and a data race in a test PIN fake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
147 lines
3.9 KiB
Go
147 lines
3.9 KiB
Go
package constants
|
|
|
|
// EnakGame values (docs/rfc-enakgame.md §5). Each set matches a CHECK in the
|
|
// migrations.
|
|
|
|
// games.status.
|
|
const (
|
|
GameStatusDraft = "DRAFT"
|
|
GameStatusActive = "ACTIVE"
|
|
GameStatusInactive = "INACTIVE"
|
|
// The old games, and games taken out of EnakGame for good. Never changed again.
|
|
GameStatusArchived = "ARCHIVED"
|
|
)
|
|
|
|
// game_reward_configs.reward_type (§8).
|
|
const (
|
|
GameRewardTypeFixed = "FIXED"
|
|
GameRewardTypeScoreBased = "SCORE_BASED"
|
|
GameRewardTypeOutcomeBased = "OUTCOME_BASED"
|
|
GameRewardTypeProbability = "PROBABILITY"
|
|
)
|
|
|
|
// game_reward_configs.status. Only status ever changes on a config (D7).
|
|
const (
|
|
GameRewardConfigStatusDraft = "DRAFT"
|
|
GameRewardConfigStatusActive = "ACTIVE"
|
|
GameRewardConfigStatusRetired = "RETIRED"
|
|
)
|
|
|
|
// game_sessions.status: STARTED → COMPLETED | REFUNDED | EXPIRED (D4).
|
|
const (
|
|
GameSessionStatusStarted = "STARTED"
|
|
GameSessionStatusCompleted = "COMPLETED"
|
|
GameSessionStatusRefunded = "REFUNDED"
|
|
GameSessionStatusExpired = "EXPIRED"
|
|
)
|
|
|
|
// game_sessions.refund_reason (§7.3).
|
|
const (
|
|
GameSessionRefundSystemError = "SYSTEM_ERROR"
|
|
GameSessionRefundGameDeactivated = "GAME_DEACTIVATED"
|
|
)
|
|
|
|
// game_budgets.scope (§5.6).
|
|
const (
|
|
GameBudgetScopeGlobal = "GLOBAL"
|
|
GameBudgetScopeEvent = "EVENT"
|
|
)
|
|
|
|
// audit_logs.actor_type (§5.9).
|
|
const (
|
|
AuditActorUser = "USER"
|
|
AuditActorSystem = "SYSTEM"
|
|
)
|
|
|
|
// audit_logs.source: where a change came from.
|
|
const (
|
|
AuditSourceAdminAPI = "admin_api"
|
|
AuditSourceBudgetController = "budget_controller"
|
|
AuditSourceSessionJob = "session_job"
|
|
AuditSourceBudgetPeriodJob = "budget_period_job"
|
|
AuditSourceCustomerAPI = "customer_api"
|
|
)
|
|
|
|
// audit_logs.entity_type: what an audit row is about (§13).
|
|
const (
|
|
AuditEntityGame = "GAME"
|
|
AuditEntityGameRewardConfig = "GAME_REWARD_CONFIG"
|
|
AuditEntityGameBudget = "GAME_BUDGET"
|
|
AuditEntityGameEvent = "GAME_EVENT"
|
|
AuditEntityGameSession = "GAME_SESSION"
|
|
AuditEntityVoucher = "VOUCHER"
|
|
)
|
|
|
|
// game_reward_counters.scope_type: what an Economy Guard counter counts for (§5.8).
|
|
const (
|
|
GameRewardScopeUser = "USER"
|
|
GameRewardScopeGame = "GAME"
|
|
GameRewardScopeEvent = "EVENT"
|
|
GameRewardScopeGlobal = "GLOBAL"
|
|
)
|
|
|
|
// vouchers.voucher_type (§5.7).
|
|
const (
|
|
VoucherTypeFixedValue = "FIXED_VALUE"
|
|
VoucherTypePercentage = "PERCENTAGE"
|
|
VoucherTypeFreeItem = "FREE_ITEM"
|
|
VoucherTypeMerchantBenefit = "MERCHANT_BENEFIT"
|
|
)
|
|
|
|
// vouchers.stock_mode: where a redemption's voucher comes from.
|
|
const (
|
|
// A counted stock with nothing to hand out but the redemption itself.
|
|
VoucherStockStatic = "STATIC"
|
|
// One imported code per redemption.
|
|
VoucherStockCodePool = "CODE_POOL"
|
|
// Issued by a provider (§7.5), not before EG-801.
|
|
VoucherStockExternal = "EXTERNAL"
|
|
)
|
|
|
|
// vouchers.status.
|
|
const (
|
|
VoucherStatusDraft = "DRAFT"
|
|
VoucherStatusActive = "ACTIVE"
|
|
VoucherStatusInactive = "INACTIVE"
|
|
VoucherStatusArchived = "ARCHIVED"
|
|
)
|
|
|
|
// voucher_codes.status.
|
|
const (
|
|
VoucherCodeAvailable = "AVAILABLE"
|
|
VoucherCodeReserved = "RESERVED"
|
|
VoucherCodeRedeemed = "REDEEMED"
|
|
VoucherCodeExpired = "EXPIRED"
|
|
VoucherCodeCancelled = "CANCELLED"
|
|
)
|
|
|
|
// voucher_redemptions.status.
|
|
const (
|
|
VoucherRedemptionPending = "PENDING"
|
|
VoucherRedemptionCompleted = "COMPLETED"
|
|
VoucherRedemptionFailed = "FAILED"
|
|
)
|
|
|
|
// Budget statuses (PRD §32), from its utilization and forecast against its thresholds.
|
|
const (
|
|
GameBudgetHealthy = "HEALTHY"
|
|
GameBudgetWarning = "WARNING"
|
|
GameBudgetCritical = "CRITICAL"
|
|
GameBudgetExhausted = "EXHAUSTED"
|
|
)
|
|
|
|
// Thresholds of a budget that sets none, in percent (PRD §8). Pending RFC §19.2 #4.
|
|
const (
|
|
GameBudgetWarningDefault = int64(70)
|
|
GameBudgetCriticalDefault = int64(90)
|
|
)
|
|
|
|
// game_events.status. An ACTIVE event changes rewards only between its start_at and
|
|
// end_at.
|
|
const (
|
|
GameEventStatusDraft = "DRAFT"
|
|
GameEventStatusActive = "ACTIVE"
|
|
GameEventStatusEnded = "ENDED"
|
|
GameEventStatusCancelled = "CANCELLED"
|
|
)
|