FASE 3 COMPLETADA: Torneos y Ligas

Implementados 3 módulos con agent swarm:

1. SISTEMA DE TORNEOS
   - Tipos: Eliminación, Round Robin, Suizo, Consolación
   - Categorías: Masculina, Femenina, Mixta
   - Inscripciones con validación de niveles
   - Gestión de pagos y estados

2. CUADROS Y PARTIDOS
   - Generación automática de cuadros
   - Algoritmos: Circle method (Round Robin), Swiss pairing
   - Avance automático de ganadores
   - Asignación de canchas y horarios
   - Registro y confirmación de resultados

3. LIGAS POR EQUIPOS
   - Creación de equipos con capitán
   - Calendario round-robin automático
   - Tabla de clasificación con desempates
   - Estadísticas por equipo

Modelos DB:
- Tournament, TournamentParticipant, TournamentMatch
- League, LeagueTeam, LeagueTeamMember, LeagueMatch, LeagueStanding

Nuevos endpoints:
- /tournaments/* - Gestión de torneos
- /tournaments/:id/draw/* - Cuadros
- /tournaments/:id/matches/* - Partidos de torneo
- /leagues/* - Ligas
- /league-teams/* - Equipos
- /league-schedule/* - Calendario
- /league-standings/* - Clasificación
- /league-matches/* - Partidos de liga

Datos de prueba:
- Torneo de Verano 2024 (Eliminatoria)
- Liga de Invierno (Round Robin)
- Liga de Club 2024
This commit is contained in:
2026-01-31 08:38:54 +00:00
parent e20c5b956b
commit 6494e2b38b
34 changed files with 9036 additions and 3 deletions

View File

@@ -64,9 +64,18 @@ model User {
groupsCreated Group[] @relation("GroupsCreated")
groupMembers GroupMember[] @relation("GroupMemberships")
// Ligas
leaguesCreated League[] @relation("LeaguesCreated") // Ligas creadas por el usuario
teamCaptain LeagueTeam[] @relation("TeamCaptain") // Equipos donde es capitán
leagueTeamMembers LeagueTeamMember[] // Membresías de equipo en liga
// Reservas recurrentes
recurringBookings RecurringBooking[]
// Torneos
tournamentsCreated Tournament[] @relation("TournamentsCreated")
tournamentParticipations TournamentParticipant[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -121,6 +130,8 @@ model Court {
bookings Booking[]
schedules CourtSchedule[]
recurringBookings RecurringBooking[]
leagueMatches LeagueMatch[]
tournamentMatches TournamentMatch[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -381,3 +392,368 @@ model UserStats {
@@index([points])
@@map("user_stats")
}
// Modelo de Torneo
model Tournament {
id String @id @default(uuid())
name String
description String?
// Tipo de torneo
type String // ELIMINATION, ROUND_ROBIN, SWISS, CONSOLATION
// Categoría
category String // MEN, WOMEN, MIXED
// Niveles permitidos (almacenados como JSON string)
allowedLevels String
// Capacidad
maxParticipants Int
// Fechas importantes
registrationStartDate DateTime
registrationEndDate DateTime
startDate DateTime
endDate DateTime
// Canchas asignadas (almacenadas como JSON string de IDs)
courtIds String
// Precio de inscripción (en centavos)
price Int @default(0)
// Estado del torneo
status String @default("DRAFT") // DRAFT, OPEN, CLOSED, IN_PROGRESS, FINISHED, CANCELLED
// Creador (admin)
createdBy User @relation("TournamentsCreated", fields: [createdById], references: [id])
createdById String
// Relaciones
participants TournamentParticipant[] @relation("TournamentParticipants")
matches TournamentMatch[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdById])
@@index([startDate])
@@index([registrationStartDate])
@@map("tournaments")
}
// Modelo de Participante de Torneo
model TournamentParticipant {
id String @id @default(uuid())
// Torneo
tournament Tournament @relation("TournamentParticipants", fields: [tournamentId], references: [id], onDelete: Cascade)
tournamentId String
// Usuario (jugador individual)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Fecha de inscripción
registrationDate DateTime @default(now())
// Estado del pago
paymentStatus String @default("PENDING") // PENDING, PAID, REFUNDED
// Número de cabeza de serie (opcional)
seed Int?
// Estado de la inscripción
status String @default("REGISTERED") // REGISTERED, CONFIRMED, WITHDRAWN
// Timestamps
updatedAt DateTime @updatedAt
// Relaciones con partidos (como jugador individual)
team1Player1Matches TournamentMatch[] @relation("T1P1")
team1Player2Matches TournamentMatch[] @relation("T1P2")
team2Player1Matches TournamentMatch[] @relation("T2P1")
team2Player2Matches TournamentMatch[] @relation("T2P2")
@@unique([tournamentId, userId])
@@index([tournamentId])
@@index([userId])
@@index([paymentStatus])
@@index([status])
@@map("tournament_participants")
}
// Modelo de Partido de Torneo
model TournamentMatch {
id String @id @default(uuid())
// Torneo
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
tournamentId String
// Ronda (1=final, 2=semifinal, etc. o número de ronda en liga)
round Int
// Número de partido en la ronda
matchNumber Int
// Posición en el cuadro
position Int
// Equipo 1 (pareja o individual)
team1Player1 TournamentParticipant? @relation("T1P1", fields: [team1Player1Id], references: [id], onDelete: SetNull)
team1Player1Id String?
team1Player2 TournamentParticipant? @relation("T1P2", fields: [team1Player2Id], references: [id], onDelete: SetNull)
team1Player2Id String?
// Equipo 2 (pareja o individual)
team2Player1 TournamentParticipant? @relation("T2P1", fields: [team2Player1Id], references: [id], onDelete: SetNull)
team2Player1Id String?
team2Player2 TournamentParticipant? @relation("T2P2", fields: [team2Player2Id], references: [id], onDelete: SetNull)
team2Player2Id String?
// Cancha asignada
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
courtId String?
// Fecha y hora programada
scheduledDate DateTime?
scheduledTime String?
// Estado del partido
status String @default("PENDING") // PENDING, SCHEDULED, IN_PROGRESS, FINISHED, CANCELLED, BYE
// Resultado
team1Score Int?
team2Score Int?
winner String? // TEAM1, TEAM2, DRAW
// Avance en cuadro
nextMatch TournamentMatch? @relation("NextMatch", fields: [nextMatchId], references: [id], onDelete: SetNull)
nextMatchId String?
parentMatches TournamentMatch[] @relation("NextMatch")
// Confirmaciones del resultado (JSON array de userIds)
confirmedBy String @default("[]")
// Metadatos adicionales (para sistemas suizo, round robin, etc)
metadata String? // JSON string con datos adicionales
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([tournamentId])
@@index([round])
@@index([status])
@@index([courtId])
@@index([nextMatchId])
@@index([tournamentId, round])
@@map("tournament_matches")
}
// ============================================
// Modelos de Liga por Equipos (Fase 3.3)
// ============================================
// Modelo de Liga
model League {
id String @id @default(uuid())
name String
description String?
// Tipo y formato
type String @default("TEAM_LEAGUE") // TEAM_LEAGUE, INDIVIDUAL_LEAGUE
format String @default("DOUBLE_ROUND_ROBIN") // SINGLE_ROUND_ROBIN, DOUBLE_ROUND_ROBIN, etc.
// Configuración de partidos por jornada
matchesPerMatchday Int @default(2) // Número de partidos entre dos equipos por jornada
// Fechas
startDate DateTime?
endDate DateTime?
// Estado
status String @default("DRAFT") // DRAFT, ACTIVE, FINISHED, CANCELLED
// Creador (admin)
createdBy User @relation("LeaguesCreated", fields: [createdById], references: [id])
createdById String
// Relaciones
teams LeagueTeam[]
matches LeagueMatch[]
standings LeagueStanding[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdById])
@@index([startDate])
@@map("leagues")
}
// Modelo de Equipo en Liga
model LeagueTeam {
id String @id @default(uuid())
name String
description String?
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Capitán del equipo
captain User @relation("TeamCaptain", fields: [captainId], references: [id])
captainId String
// Relaciones
members LeagueTeamMember[]
// Partidos como equipo 1 o 2
matchesAsTeam1 LeagueMatch[] @relation("Team1Matches")
matchesAsTeam2 LeagueMatch[] @relation("Team2Matches")
// Clasificación
standing LeagueStanding?
// Timestamps
createdAt DateTime @default(now())
@@unique([leagueId, name])
@@index([leagueId])
@@index([captainId])
@@map("league_teams")
}
// Modelo de Miembro de Equipo
model LeagueTeamMember {
id String @id @default(uuid())
// Equipo
team LeagueTeam @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String
// Usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Estado
isActive Boolean @default(true)
// Fecha de unión
joinedAt DateTime @default(now())
// Timestamps
updatedAt DateTime @updatedAt
@@unique([teamId, userId])
@@index([teamId])
@@index([userId])
@@map("league_team_members")
}
// Modelo de Partido de Liga
model LeagueMatch {
id String @id @default(uuid())
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Jornada
matchday Int // Número de jornada
// Equipos
team1 LeagueTeam @relation("Team1Matches", fields: [team1Id], references: [id])
team1Id String
team2 LeagueTeam @relation("Team2Matches", fields: [team2Id], references: [id])
team2Id String
// Cancha y horario (opcional)
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
courtId String?
scheduledDate DateTime? // Fecha programada
scheduledTime String? // Hora programada (formato HH:mm)
// Estado
status String @default("SCHEDULED") // SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED, POSTPONED, WALKOVER
// Resultado
team1Score Int? // Sets ganados por equipo 1
team2Score Int? // Sets ganados por equipo 2
// Detalle de sets (almacenado como JSON)
// Ej: [{team1Games: 6, team2Games: 4}, {team1Games: 6, team2Games: 2}]
setDetails String?
// Ganador
winner String? // TEAM1, TEAM2, DRAW
// Fecha de finalización
completedAt DateTime?
// Notas
notes String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([leagueId])
@@index([matchday])
@@index([team1Id])
@@index([team2Id])
@@index([status])
@@index([scheduledDate])
@@map("league_matches")
}
// Modelo de Clasificación
model LeagueStanding {
id String @id @default(uuid())
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Equipo
team LeagueTeam @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String @unique
// Partidos
matchesPlayed Int @default(0)
matchesWon Int @default(0)
matchesLost Int @default(0)
matchesDrawn Int @default(0)
// Sets
setsFor Int @default(0)
setsAgainst Int @default(0)
// Games (opcional)
gamesFor Int @default(0)
gamesAgainst Int @default(0)
// Puntos
points Int @default(0)
// Posición actual
position Int @default(0)
// Timestamps
updatedAt DateTime @updatedAt
@@unique([leagueId, teamId])
@@index([leagueId])
@@index([position])
@@index([points])
@@map("league_standings")
}