cab-backend/internal/handlers/leaderboards.go
2026-03-30 21:00:35 +03:00

749 lines
21 KiB
Go

package handlers
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"game-admin/internal/models"
"github.com/gin-gonic/gin"
"github.com/uptrace/bun"
)
func (h *GameHandler) ListLeaderboards(c *gin.Context) {
gameID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, gameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
var leaderboards []models.Leaderboard
err = h.db.NewSelect().
Model(&leaderboards).
Relation("Groups", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.OrderExpr("lbg.id ASC")
}).
Where("lb.game_id = ?", gameID).
OrderExpr("lb.id DESC").
Scan(c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, leaderboards)
}
func (h *GameHandler) GetLeaderboard(c *gin.Context) {
gameID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"})
return
}
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, gameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
leaderboard, err := h.getLeaderboard(c, gameID, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
c.JSON(http.StatusOK, leaderboard)
}
func (h *GameHandler) CreateLeaderboard(c *gin.Context) {
gameID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"})
return
}
var req models.UpsertLeaderboardRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateUpsertLeaderboardRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, gameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
isActive := true
if req.IsActive != nil {
isActive = *req.IsActive
}
leaderboard := &models.Leaderboard{
GameID: gameID,
Key: strings.TrimSpace(req.Key),
Name: strings.TrimSpace(req.Name),
SortOrder: req.SortOrder,
PeriodType: req.PeriodType,
IsActive: isActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
_, err = h.db.NewInsert().Model(leaderboard).Exec(c)
if err != nil {
c.JSON(http.StatusConflict, gin.H{"error": "Leaderboard key already exists"})
return
}
created, err := h.getLeaderboard(c, gameID, leaderboard.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, created)
}
func (h *GameHandler) UpdateLeaderboard(c *gin.Context) {
gameID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"})
return
}
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
var req models.UpsertLeaderboardRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateUpsertLeaderboardRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, gameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
current, err := h.getLeaderboard(c, gameID, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
isActive := current.IsActive
if req.IsActive != nil {
isActive = *req.IsActive
}
_, err = h.db.NewUpdate().
Model((*models.Leaderboard)(nil)).
Set(`"key" = ?`, strings.TrimSpace(req.Key)).
Set("name = ?", strings.TrimSpace(req.Name)).
Set("sort_order = ?", req.SortOrder).
Set("period_type = ?", req.PeriodType).
Set("is_active = ?", isActive).
Set("updated_at = ?", time.Now()).
Where("id = ? AND game_id = ?", leaderboardID, gameID).
Exec(c)
if err != nil {
c.JSON(http.StatusConflict, gin.H{"error": "Could not update leaderboard"})
return
}
updated, err := h.getLeaderboard(c, gameID, leaderboardID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, updated)
}
func (h *GameHandler) DeleteLeaderboard(c *gin.Context) {
gameID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"})
return
}
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, gameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
if _, err := h.getLeaderboard(c, gameID, leaderboardID); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
tx, err := h.db.BeginTx(c, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
var groups []models.LeaderboardGroup
if err := tx.NewSelect().Model(&groups).Where("leaderboard_id = ?", leaderboardID).Scan(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if len(groups) > 0 {
groupIDs := make([]int64, 0, len(groups))
for _, group := range groups {
groupIDs = append(groupIDs, group.ID)
}
if _, err := tx.NewDelete().Model((*models.LeaderboardGroupMember)(nil)).Where("group_id IN (?)", bun.In(groupIDs)).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
}
if _, err := tx.NewDelete().Model((*models.LeaderboardGroup)(nil)).Where("leaderboard_id = ?", leaderboardID).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if _, err := tx.NewDelete().Model((*models.LeaderboardScore)(nil)).Where("leaderboard_id = ?", leaderboardID).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if _, err := tx.NewDelete().Model((*models.Leaderboard)(nil)).Where("id = ? AND game_id = ?", leaderboardID, gameID).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := tx.Commit(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
committed = true
c.JSON(http.StatusOK, gin.H{"message": "Leaderboard deleted"})
}
func (h *GameHandler) ListLeaderboardGroups(c *gin.Context) {
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
leaderboard, err := h.getLeaderboardByID(c, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
var groups []models.LeaderboardGroup
err = h.db.NewSelect().
Model(&groups).
Relation("Members", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.OrderExpr("lbgm.id ASC")
}).
Where("lbg.leaderboard_id = ?", leaderboardID).
OrderExpr("lbg.id ASC").
Scan(c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, groups)
}
func (h *GameHandler) CreateLeaderboardGroup(c *gin.Context) {
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
leaderboard, err := h.getLeaderboardByID(c, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
var req models.UpsertLeaderboardGroupRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateUpsertLeaderboardGroupRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
isDefault := false
if req.IsDefault != nil {
isDefault = *req.IsDefault
}
group := &models.LeaderboardGroup{
LeaderboardID: leaderboardID,
Key: strings.TrimSpace(req.Key),
Name: strings.TrimSpace(req.Name),
IsDefault: isDefault,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
_, err = h.db.NewInsert().Model(group).Exec(c)
if err != nil {
c.JSON(http.StatusConflict, gin.H{"error": "Leaderboard group key already exists"})
return
}
c.JSON(http.StatusCreated, group)
}
func (h *GameHandler) UpdateLeaderboardGroup(c *gin.Context) {
groupID, err := strconv.ParseInt(c.Param("group_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group id"})
return
}
group, leaderboard, err := h.getLeaderboardGroup(c, groupID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard group not found"})
return
}
var req models.UpsertLeaderboardGroupRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := validateUpsertLeaderboardGroupRequest(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
isDefault := group.IsDefault
if req.IsDefault != nil {
isDefault = *req.IsDefault
}
_, err = h.db.NewUpdate().
Model((*models.LeaderboardGroup)(nil)).
Set(`"key" = ?`, strings.TrimSpace(req.Key)).
Set("name = ?", strings.TrimSpace(req.Name)).
Set("is_default = ?", isDefault).
Set("updated_at = ?", time.Now()).
Where("id = ?", groupID).
Exec(c)
if err != nil {
c.JSON(http.StatusConflict, gin.H{"error": "Could not update leaderboard group"})
return
}
updated, _, err := h.getLeaderboardGroup(c, groupID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, updated)
}
func (h *GameHandler) DeleteLeaderboardGroup(c *gin.Context) {
groupID, err := strconv.ParseInt(c.Param("group_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group id"})
return
}
_, leaderboard, err := h.getLeaderboardGroup(c, groupID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard group not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
tx, err := h.db.BeginTx(c, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
committed := false
defer func() {
if !committed {
_ = tx.Rollback()
}
}()
if _, err := tx.NewDelete().Model((*models.LeaderboardGroupMember)(nil)).Where("group_id = ?", groupID).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if _, err := tx.NewDelete().Model((*models.LeaderboardGroup)(nil)).Where("id = ?", groupID).Exec(c); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := tx.Commit(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
committed = true
c.JSON(http.StatusOK, gin.H{"message": "Leaderboard group deleted"})
}
func (h *GameHandler) AddLeaderboardGroupMember(c *gin.Context) {
groupID, err := strconv.ParseInt(c.Param("group_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group id"})
return
}
group, leaderboard, err := h.getLeaderboardGroup(c, groupID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard group not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
var req models.AddLeaderboardGroupMemberRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureUserBelongsToGame(c, req.UserID, leaderboard.GameID); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
member := &models.LeaderboardGroupMember{
GroupID: group.ID,
UserID: req.UserID,
CreatedAt: time.Now(),
}
_, err = h.db.NewInsert().Model(member).Exec(c)
if err != nil {
c.JSON(http.StatusConflict, gin.H{"error": "User already in group"})
return
}
c.JSON(http.StatusCreated, member)
}
func (h *GameHandler) DeleteLeaderboardGroupMember(c *gin.Context) {
groupID, err := strconv.ParseInt(c.Param("group_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group id"})
return
}
userID, err := strconv.ParseInt(c.Param("user_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user id"})
return
}
_, leaderboard, err := h.getLeaderboardGroup(c, groupID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard group not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
res, err := h.db.NewDelete().Model((*models.LeaderboardGroupMember)(nil)).Where("group_id = ? AND user_id = ?", groupID, userID).Exec(c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if affected, _ := res.RowsAffected(); affected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "Group member not found"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Group member deleted"})
}
func (h *GameHandler) UpsertLeaderboardScore(c *gin.Context) {
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
leaderboard, err := h.getLeaderboardByID(c, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
var req models.UpsertLeaderboardScoreRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.ensureUserGameBelongsToGame(c, req.UserGameID, leaderboard.GameID); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
now := time.Now()
score := &models.LeaderboardScore{
LeaderboardID: leaderboardID,
UserGameID: req.UserGameID,
Score: req.Score,
CreatedAt: now,
UpdatedAt: now,
}
_, err = h.db.NewInsert().Model(score).
On("CONFLICT (leaderboard_id, user_game_id) DO UPDATE").
Set("score = EXCLUDED.score").
Set("updated_at = EXCLUDED.updated_at").
Exec(c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Leaderboard score saved"})
}
func (h *GameHandler) GetLeaderboardRankings(c *gin.Context) {
leaderboardID, err := strconv.ParseInt(c.Param("leaderboard_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid leaderboard id"})
return
}
leaderboard, err := h.getLeaderboardByID(c, leaderboardID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard not found"})
return
}
if err := h.ensureLeaderboardModuleEnabled(c, leaderboard.GameID); err != nil {
renderLeaderboardModuleError(c, err)
return
}
limit := 50
if rawLimit := c.Query("limit"); rawLimit != "" {
parsed, err := strconv.Atoi(rawLimit)
if err != nil || parsed <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid limit"})
return
}
if parsed > 200 {
parsed = 200
}
limit = parsed
}
query := h.db.NewSelect().
TableExpr("leaderboard_scores AS lbs").
ColumnExpr("lbs.user_game_id AS user_game_id").
ColumnExpr("ug.user_id AS user_id").
ColumnExpr("u.username AS username").
ColumnExpr("lbs.score AS score").
Join("JOIN user_games AS ug ON ug.id = lbs.user_game_id").
Join("JOIN users AS u ON u.id = ug.user_id").
Where("lbs.leaderboard_id = ?", leaderboardID)
if rawGroupID := c.Query("group_id"); rawGroupID != "" {
groupID, err := strconv.ParseInt(rawGroupID, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid group_id"})
return
}
group, _, err := h.getLeaderboardGroup(c, groupID)
if err != nil || group.LeaderboardID != leaderboardID {
c.JSON(http.StatusNotFound, gin.H{"error": "Leaderboard group not found"})
return
}
query = query.Join("JOIN leaderboard_group_members AS lbgm ON lbgm.user_id = ug.user_id").
Where("lbgm.group_id = ?", groupID)
}
switch leaderboard.SortOrder {
case models.LeaderboardSortOrderAsc:
query = query.OrderExpr("lbs.score ASC, lbs.updated_at ASC")
default:
query = query.OrderExpr("lbs.score DESC, lbs.updated_at ASC")
}
var rows []models.LeaderboardRankItem
if err := query.Limit(limit).Scan(c, &rows); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
for i := range rows {
rows[i].Rank = int64(i + 1)
}
c.JSON(http.StatusOK, gin.H{
"leaderboard": leaderboard,
"items": rows,
})
}
func (h *GameHandler) ensureLeaderboardModuleEnabled(c *gin.Context, gameID int64) error {
exists, err := h.gameExists(c, gameID)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("game_not_found")
}
hasModule, err := h.gameHasModule(c, gameID, "leaderboard")
if err != nil {
return err
}
if !hasModule {
return fmt.Errorf("leaderboard_module_disabled")
}
return nil
}
func renderLeaderboardModuleError(c *gin.Context, err error) {
switch err.Error() {
case "game_not_found":
c.JSON(http.StatusNotFound, gin.H{"error": "Game not found"})
case "leaderboard_module_disabled":
c.JSON(http.StatusForbidden, gin.H{"error": "Leaderboard module is not enabled for this game"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
}
func validateUpsertLeaderboardRequest(req *models.UpsertLeaderboardRequest) error {
req.Key = strings.TrimSpace(req.Key)
req.Name = strings.TrimSpace(req.Name)
if req.Key == "" {
return fmt.Errorf("key is required")
}
if req.Name == "" {
return fmt.Errorf("name is required")
}
if req.SortOrder != models.LeaderboardSortOrderAsc && req.SortOrder != models.LeaderboardSortOrderDesc {
return fmt.Errorf("sort_order must be asc or desc")
}
switch req.PeriodType {
case models.LeaderboardPeriodAllTime, models.LeaderboardPeriodDaily, models.LeaderboardPeriodWeekly, models.LeaderboardPeriodMonthly:
default:
return fmt.Errorf("unsupported period_type")
}
return nil
}
func validateUpsertLeaderboardGroupRequest(req *models.UpsertLeaderboardGroupRequest) error {
req.Key = strings.TrimSpace(req.Key)
req.Name = strings.TrimSpace(req.Name)
if req.Key == "" {
return fmt.Errorf("key is required")
}
if req.Name == "" {
return fmt.Errorf("name is required")
}
return nil
}
func (h *GameHandler) getLeaderboard(c *gin.Context, gameID, leaderboardID int64) (*models.Leaderboard, error) {
leaderboard := new(models.Leaderboard)
err := h.db.NewSelect().
Model(leaderboard).
Relation("Groups", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.OrderExpr("lbg.id ASC")
}).
Where("lb.id = ? AND lb.game_id = ?", leaderboardID, gameID).
Scan(c)
if err != nil {
return nil, err
}
return leaderboard, nil
}
func (h *GameHandler) getLeaderboardByID(c *gin.Context, leaderboardID int64) (*models.Leaderboard, error) {
leaderboard := new(models.Leaderboard)
err := h.db.NewSelect().
Model(leaderboard).
Where("id = ?", leaderboardID).
Scan(c)
if err != nil {
return nil, err
}
return leaderboard, nil
}
func (h *GameHandler) getLeaderboardGroup(c *gin.Context, groupID int64) (*models.LeaderboardGroup, *models.Leaderboard, error) {
group := new(models.LeaderboardGroup)
err := h.db.NewSelect().Model(group).Where("id = ?", groupID).Scan(c)
if err != nil {
return nil, nil, err
}
leaderboard, err := h.getLeaderboardByID(c, group.LeaderboardID)
if err != nil {
return nil, nil, err
}
return group, leaderboard, nil
}
func (h *GameHandler) ensureUserBelongsToGame(c *gin.Context, userID, gameID int64) error {
exists, err := h.db.NewSelect().
Model((*models.UserGame)(nil)).
Where("user_id = ? AND game_id = ?", userID, gameID).
Exists(c)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("user is not connected to this game")
}
return nil
}
func (h *GameHandler) ensureUserGameBelongsToGame(c *gin.Context, userGameID, gameID int64) error {
exists, err := h.db.NewSelect().
Model((*models.UserGame)(nil)).
Where("id = ? AND game_id = ?", userGameID, gameID).
Exists(c)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("user_game does not belong to this game")
}
return nil
}