Skip to content
Merged

Main #89

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (
"github.com/FOR-GAMERS/GAMERS-BE/internal/contest"
"github.com/FOR-GAMERS/GAMERS-BE/internal/discord"
"github.com/FOR-GAMERS/GAMERS-BE/internal/game"
"github.com/FOR-GAMERS/GAMERS-BE/internal/lol"
"github.com/FOR-GAMERS/GAMERS-BE/internal/global/common/router"
"github.com/FOR-GAMERS/GAMERS-BE/internal/global/config"
"github.com/FOR-GAMERS/GAMERS-BE/internal/global/middleware"
authProvider "github.com/FOR-GAMERS/GAMERS-BE/internal/global/security/jwt"
"github.com/FOR-GAMERS/GAMERS-BE/internal/notification"
"github.com/FOR-GAMERS/GAMERS-BE/internal/oauth2"
"github.com/FOR-GAMERS/GAMERS-BE/internal/point"
"github.com/FOR-GAMERS/GAMERS-BE/internal/storage"
Expand Down Expand Up @@ -136,6 +136,9 @@ func main() {
gameDeps.TeamService.SetContestRepository(contestDeps.ContestRepository)
gameDeps.TournamentResultService.SetContestDBPort(contestDeps.ContestRepository)

// LoL module - provides LoL-specific team balancing and custom match sessions
lolDeps := lol.ProvideLolDependencies(appRouter, db, redisClient, tokenService)

commentDeps := comment.ProvideCommentDependencies(db, appRouter, contestDeps.ContestRepository)

// Point module - provides Valorant score table management
Expand All @@ -160,20 +163,13 @@ func main() {
// Banner module - provides main banner management for homepage
bannerDeps := banner.ProvideBannerDependencies(db, appRouter)

// Notification module - provides SSE real-time notifications
notificationDeps := notification.ProvideNotificationDependencies(db, appRouter)

// Wire score table port for point calculation during contest application
contestDeps.ApplicationService.SetScoreTablePort(pointDeps.ScoreTableRepository)

// Wire notification handler to contest and game services
contestDeps.ApplicationService.SetNotificationHandler(notificationDeps.Service)
gameDeps.TeamService.SetNotificationHandler(notificationDeps.Service)

// Start Team Persistence Consumer for Write-Behind pattern
startTeamPersistenceConsumer(ctx, gameDeps)

setupRouter(appRouter, authDeps, userDeps, oauth2Deps, contestDeps, commentDeps, discordDeps, gameDeps, pointDeps, valorantDeps, storageDeps, bannerDeps, notificationDeps)
setupRouter(appRouter, authDeps, userDeps, oauth2Deps, contestDeps, commentDeps, discordDeps, gameDeps, pointDeps, valorantDeps, storageDeps, bannerDeps, lolDeps)

startServer(appRouter.Engine())
}
Expand Down Expand Up @@ -213,7 +209,7 @@ func setupRouter(
valorantDeps *valorant.Dependencies,
storageDeps *storage.Dependencies,
bannerDeps *banner.Dependencies,
notificationDeps *notification.Dependencies,
lolDeps *lol.Dependencies,
) *router.Router {

appRouter.Engine().Use(middleware.PrometheusMetrics())
Expand Down Expand Up @@ -241,8 +237,8 @@ func setupRouter(
if bannerDeps != nil {
bannerDeps.Controller.RegisterRoutes()
}
// Notification routes are registered in provider
_ = notificationDeps
lolDeps.Controller.RegisterRoutes()
lolDeps.SessionController.RegisterRoutes()

return appRouter
}
Expand Down
2 changes: 2 additions & 0 deletions db/migrations/000026_create_lol_custom_matches_table.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS lol_custom_match_players;
DROP TABLE IF EXISTS lol_custom_matches;
40 changes: 40 additions & 0 deletions db/migrations/000026_create_lol_custom_matches_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
CREATE TABLE lol_custom_matches (
match_id BIGINT AUTO_INCREMENT PRIMARY KEY,
creator_user_id BIGINT NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
winner VARCHAR(8) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

CONSTRAINT fk_lol_matches_creator
FOREIGN KEY (creator_user_id) REFERENCES users(user_id)
ON DELETE CASCADE,

CONSTRAINT chk_lol_match_status
CHECK (status IN ('PENDING', 'FINISHED', 'CANCELLED')),

CONSTRAINT chk_lol_match_winner
CHECK (winner IN ('TEAM_A', 'TEAM_B') OR winner IS NULL),

INDEX idx_lol_match_creator (creator_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE lol_custom_match_players (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
match_id BIGINT NOT NULL,
team VARCHAR(8) NOT NULL,
username VARCHAR(64) NOT NULL,
tag VARCHAR(16) NOT NULL,
rank VARCHAR(32) NOT NULL,
position VARCHAR(8) NOT NULL,
position_preference INT NOT NULL,

CONSTRAINT fk_lol_players_match
FOREIGN KEY (match_id) REFERENCES lol_custom_matches(match_id)
ON DELETE CASCADE,

CONSTRAINT chk_lol_player_team
CHECK (team IN ('TEAM_A', 'TEAM_B')),

INDEX idx_lol_players_match (match_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
180 changes: 179 additions & 1 deletion docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,69 @@ const docTemplate = `{
}
}
},
"/api/contests/lol/temporal": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Accepts exactly 10 players each with their rank and position preferences (all 5 positions in priority order). Returns a balanced 5v5 team assignment using 3-step filtering: team MMR balance → lane matchup balance → position satisfaction. No data is persisted.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contests"
],
"summary": "Balance teams for a temporary LoL custom game",
"parameters": [
{
"description": "10 players with position preferences",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalContestRequestV2"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_global_response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalContestResponseV2"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_global_response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_global_response.Response"
}
}
}
}
},
"/api/contests/me": {
"get": {
"security": [
Expand Down Expand Up @@ -725,7 +788,7 @@ const docTemplate = `{
"BearerAuth": []
}
],
"description": "Apply to participate in a contest",
"description": "Apply to participate in a contest with optional valorant roles and description",
"consumes": [
"application/json"
],
Expand All @@ -743,6 +806,14 @@ const docTemplate = `{
"name": "contestId",
"in": "path",
"required": true
},
{
"description": "Participation request with optional valorant roles and description",
"name": "request",
"in": "body",
"schema": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.RequestParticipateRequest"
}
}
],
"responses": {
Expand Down Expand Up @@ -5838,6 +5909,98 @@ const docTemplate = `{
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalAssignedPlayer": {
"type": "object",
"properties": {
"position": {
"type": "string"
},
"position_preference": {
"description": "1 (most preferred) to 5 (least)",
"type": "integer"
},
"rank": {
"type": "string"
},
"tag": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalContestRequestV2": {
"type": "object",
"required": [
"members"
],
"properties": {
"members": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalPlayerV2"
}
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalContestResponseV2": {
"type": "object",
"properties": {
"team_a": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalAssignedPlayer"
}
},
"team_b": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalAssignedPlayer"
}
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.LolTemporalPlayerV2": {
"type": "object",
"required": [
"positions",
"rank",
"tag",
"username"
],
"properties": {
"positions": {
"type": "array",
"items": {
"type": "string"
}
},
"rank": {
"type": "string"
},
"tag": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.RequestParticipateRequest": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"valorant_roles": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_FOR-GAMERS_GAMERS-BE_internal_contest_domain.ValorantRole"
}
}
}
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_application_dto.ThumbnailUploadResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -5991,6 +6154,21 @@ const docTemplate = `{
"MemberTypeNormal"
]
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_contest_domain.ValorantRole": {
"type": "string",
"enum": [
"DUELIST",
"INITIATOR",
"CONTROLLER",
"SENTINEL"
],
"x-enum-varnames": [
"ValorantRoleDuelist",
"ValorantRoleInitiator",
"ValorantRoleController",
"ValorantRoleSentinel"
]
},
"github_com_FOR-GAMERS_GAMERS-BE_internal_discord_application_dto.DiscordChannel": {
"type": "object",
"properties": {
Expand Down
Loading
Loading