feat(enakgame): spin as an EnakGame game; remove the old game flow
EnakGame phase 10 of docs/tasks-enakgame.md (EG-1001 to EG-1003). Spin (EG-1001) - PROBABILITY entries take an optional label (a wheel segment). The customer game list shows a PROBABILITY game's prizes (entry, label, amount, never weights), and completing returns the drawn prize, so the client can draw the wheel and stop it on the server's draw. - docs/enakgame-spin.md: the admin steps to set up spin per organization (no seeder) and the customer app flow. An HTTP test plays it end to end. Old game flow removed (EG-1002) - Routes POST /customer/spin, GET /customer/games, GET /customer/ferris-wheel, and admin /marketing/games, /marketing/game-prizes, /marketing/rewards, with their handlers, services, processors, repositories, validators, models, contracts, mappers and tests (GamePlayProcessor, SpinGameService, rewards, ...). This also closes RFC §15 findings 1 and 2 (double charge, spinning another org's game). - Tables games, game_prizes, game_plays and rewards stay for ledger history. entities.StringSlice moves to its own file; the omset tracker (unrouted) keeps game_id but no longer embeds the old game response. games.is_active dropped (EG-1003) - Migration 000115; nothing reads metadata.coin_cost any more. The EnakPoint integration docs now point at /customer/enakgame. The Postgres tests were not run: no test database here. Migration 000115 has not been run anywhere. The customer app must stop calling the removed endpoints before this is deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
296708244e
commit
18e87398bb
@@ -58,274 +58,6 @@ func PaginatedTiersResponseToContract(model *models.PaginatedResponse[models.Tie
|
||||
}
|
||||
}
|
||||
|
||||
// Game Transformers
|
||||
func CreateGameRequestToModel(req *contract.CreateGameRequest) *models.CreateGameRequest {
|
||||
return &models.CreateGameRequest{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
IsActive: req.IsActive,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateGameRequestToModel(req *contract.UpdateGameRequest) *models.UpdateGameRequest {
|
||||
return &models.UpdateGameRequest{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
IsActive: req.IsActive,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func ListGamesRequestToModel(req *contract.ListGamesRequest) *models.ListGamesQuery {
|
||||
return &models.ListGamesQuery{
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
Search: req.Search,
|
||||
Type: req.Type,
|
||||
IsActive: req.IsActive,
|
||||
SortBy: req.SortBy,
|
||||
SortOrder: req.SortOrder,
|
||||
}
|
||||
}
|
||||
|
||||
func GameModelToResponse(model *models.GameResponse) *contract.GameResponse {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &contract.GameResponse{
|
||||
ID: model.ID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
IsActive: model.IsActive,
|
||||
Metadata: model.Metadata,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func GameModelsToResponses(models []models.GameResponse) []contract.GameResponse {
|
||||
responses := make([]contract.GameResponse, len(models))
|
||||
for i, model := range models {
|
||||
responses[i] = *GameModelToResponse(&model)
|
||||
}
|
||||
return responses
|
||||
}
|
||||
|
||||
func PaginatedGamesResponseToContract(model *models.PaginatedResponse[models.GameResponse]) *contract.PaginatedGamesResponse {
|
||||
responses := make([]contract.GameResponse, len(model.Data))
|
||||
for i, item := range model.Data {
|
||||
responses[i] = *GameModelToResponse(&item)
|
||||
}
|
||||
|
||||
return &contract.PaginatedGamesResponse{
|
||||
Data: responses,
|
||||
TotalCount: int(model.Pagination.Total),
|
||||
Page: model.Pagination.Page,
|
||||
Limit: model.Pagination.Limit,
|
||||
TotalPages: model.Pagination.TotalPages,
|
||||
}
|
||||
}
|
||||
|
||||
// Game Prize Transformers
|
||||
func CreateGamePrizeRequestToModel(req *contract.CreateGamePrizeRequest) *models.CreateGamePrizeRequest {
|
||||
return &models.CreateGamePrizeRequest{
|
||||
GameID: req.GameID,
|
||||
Name: req.Name,
|
||||
Weight: req.Weight,
|
||||
Stock: req.Stock,
|
||||
MaxStock: req.MaxStock,
|
||||
Threshold: req.Threshold,
|
||||
FallbackPrizeID: req.FallbackPrizeID,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateGamePrizeRequestToModel(req *contract.UpdateGamePrizeRequest) *models.UpdateGamePrizeRequest {
|
||||
return &models.UpdateGamePrizeRequest{
|
||||
Name: req.Name,
|
||||
Weight: req.Weight,
|
||||
Stock: req.Stock,
|
||||
MaxStock: req.MaxStock,
|
||||
Threshold: req.Threshold,
|
||||
FallbackPrizeID: req.FallbackPrizeID,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func ListGamePrizesRequestToModel(req *contract.ListGamePrizesRequest) *models.ListGamePrizesQuery {
|
||||
return &models.ListGamePrizesQuery{
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
Search: req.Search,
|
||||
GameID: req.GameID,
|
||||
SortBy: req.SortBy,
|
||||
SortOrder: req.SortOrder,
|
||||
}
|
||||
}
|
||||
|
||||
func GamePrizeModelToResponse(model *models.GamePrizeResponse) *contract.GamePrizeResponse {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var game *contract.GameResponse
|
||||
if model.Game != nil {
|
||||
game = GameModelToResponse(model.Game)
|
||||
}
|
||||
|
||||
var fallbackPrize *contract.GamePrizeResponse
|
||||
if model.FallbackPrize != nil {
|
||||
fallbackPrize = GamePrizeModelToResponse(model.FallbackPrize)
|
||||
}
|
||||
|
||||
return &contract.GamePrizeResponse{
|
||||
ID: model.ID,
|
||||
GameID: model.GameID,
|
||||
Name: model.Name,
|
||||
Weight: model.Weight,
|
||||
Stock: model.Stock,
|
||||
MaxStock: model.MaxStock,
|
||||
Threshold: model.Threshold,
|
||||
FallbackPrizeID: model.FallbackPrizeID,
|
||||
Metadata: model.Metadata,
|
||||
Game: game,
|
||||
FallbackPrize: fallbackPrize,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func GamePrizeModelsToResponses(models []models.GamePrizeResponse) []contract.GamePrizeResponse {
|
||||
responses := make([]contract.GamePrizeResponse, len(models))
|
||||
for i, model := range models {
|
||||
responses[i] = *GamePrizeModelToResponse(&model)
|
||||
}
|
||||
return responses
|
||||
}
|
||||
|
||||
func PaginatedGamePrizesResponseToContract(model *models.PaginatedResponse[models.GamePrizeResponse]) *contract.PaginatedGamePrizesResponse {
|
||||
responses := make([]contract.GamePrizeResponse, len(model.Data))
|
||||
for i, item := range model.Data {
|
||||
responses[i] = *GamePrizeModelToResponse(&item)
|
||||
}
|
||||
|
||||
return &contract.PaginatedGamePrizesResponse{
|
||||
Data: responses,
|
||||
TotalCount: int(model.Pagination.Total),
|
||||
Page: model.Pagination.Page,
|
||||
Limit: model.Pagination.Limit,
|
||||
TotalPages: model.Pagination.TotalPages,
|
||||
}
|
||||
}
|
||||
|
||||
// Game Play Transformers
|
||||
func CreateGamePlayRequestToModel(req *contract.CreateGamePlayRequest) *models.CreateGamePlayRequest {
|
||||
return &models.CreateGamePlayRequest{
|
||||
GameID: req.GameID,
|
||||
CustomerID: req.CustomerID,
|
||||
CoinsUsed: req.CoinsUsed,
|
||||
RandomSeed: req.RandomSeed,
|
||||
}
|
||||
}
|
||||
|
||||
func PlayGameRequestToModel(req *contract.PlayGameRequest) *models.PlayGameRequest {
|
||||
return &models.PlayGameRequest{
|
||||
GameID: req.GameID,
|
||||
CustomerID: req.CustomerID,
|
||||
}
|
||||
}
|
||||
|
||||
func ListGamePlaysRequestToModel(req *contract.ListGamePlaysRequest) *models.ListGamePlaysQuery {
|
||||
return &models.ListGamePlaysQuery{
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
Search: req.Search,
|
||||
GameID: req.GameID,
|
||||
CustomerID: req.CustomerID,
|
||||
PrizeID: req.PrizeID,
|
||||
SortBy: req.SortBy,
|
||||
SortOrder: req.SortOrder,
|
||||
}
|
||||
}
|
||||
|
||||
func GamePlayModelToResponse(model *models.GamePlayResponse) *contract.GamePlayResponse {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var game *contract.GameResponse
|
||||
if model.Game != nil {
|
||||
game = GameModelToResponse(model.Game)
|
||||
}
|
||||
|
||||
var customer *contract.CustomerResponse
|
||||
if model.Customer != nil {
|
||||
customer = CustomerModelToResponse(model.Customer)
|
||||
}
|
||||
|
||||
var prize *contract.GamePrizeResponse
|
||||
if model.Prize != nil {
|
||||
prize = GamePrizeModelToResponse(model.Prize)
|
||||
}
|
||||
|
||||
return &contract.GamePlayResponse{
|
||||
ID: model.ID,
|
||||
GameID: model.GameID,
|
||||
CustomerID: model.CustomerID,
|
||||
PrizeID: model.PrizeID,
|
||||
CoinsUsed: model.CoinsUsed,
|
||||
RandomSeed: model.RandomSeed,
|
||||
CreatedAt: model.CreatedAt,
|
||||
Game: game,
|
||||
Customer: customer,
|
||||
Prize: prize,
|
||||
}
|
||||
}
|
||||
|
||||
func PlayGameModelToResponse(model *models.PlayGameResponse) *contract.PlayGameResponse {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var gamePlay *contract.GamePlayResponse
|
||||
if &model.GamePlay != nil {
|
||||
gamePlay = GamePlayModelToResponse(&model.GamePlay)
|
||||
}
|
||||
|
||||
var prizeWon *contract.GamePrizeResponse
|
||||
if model.PrizeWon != nil {
|
||||
prizeWon = GamePrizeModelToResponse(model.PrizeWon)
|
||||
}
|
||||
|
||||
var gamePlayValue contract.GamePlayResponse
|
||||
if gamePlay != nil {
|
||||
gamePlayValue = *gamePlay
|
||||
}
|
||||
|
||||
return &contract.PlayGameResponse{
|
||||
GamePlay: gamePlayValue,
|
||||
PrizeWon: prizeWon,
|
||||
CoinsRemaining: model.CoinsRemaining,
|
||||
}
|
||||
}
|
||||
|
||||
func PaginatedGamePlaysResponseToContract(model *models.PaginatedResponse[models.GamePlayResponse]) *contract.PaginatedGamePlaysResponse {
|
||||
responses := make([]contract.GamePlayResponse, len(model.Data))
|
||||
for i, item := range model.Data {
|
||||
responses[i] = *GamePlayModelToResponse(&item)
|
||||
}
|
||||
|
||||
return &contract.PaginatedGamePlaysResponse{
|
||||
Data: responses,
|
||||
TotalCount: int(model.Pagination.Total),
|
||||
Page: model.Pagination.Page,
|
||||
Limit: model.Pagination.Limit,
|
||||
TotalPages: model.Pagination.TotalPages,
|
||||
}
|
||||
}
|
||||
|
||||
// Omset Tracker Transformers
|
||||
func CreateOmsetTrackerRequestToModel(req *contract.CreateOmsetTrackerRequest) *models.CreateOmsetTrackerRequest {
|
||||
return &models.CreateOmsetTrackerRequest{
|
||||
@@ -366,11 +98,6 @@ func OmsetTrackerModelToResponse(model *models.OmsetTrackerResponse) *contract.O
|
||||
return nil
|
||||
}
|
||||
|
||||
var game *contract.GameResponse
|
||||
if model.Game != nil {
|
||||
game = GameModelToResponse(model.Game)
|
||||
}
|
||||
|
||||
return &contract.OmsetTrackerResponse{
|
||||
ID: model.ID,
|
||||
PeriodType: model.PeriodType,
|
||||
@@ -378,7 +105,6 @@ func OmsetTrackerModelToResponse(model *models.OmsetTrackerResponse) *contract.O
|
||||
PeriodEnd: model.PeriodEnd,
|
||||
Total: model.Total,
|
||||
GameID: model.GameID,
|
||||
Game: game,
|
||||
CreatedAt: model.CreatedAt,
|
||||
UpdatedAt: model.UpdatedAt,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user