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

Binary file not shown.

View File

@@ -0,0 +1,258 @@
-- CreateTable
CREATE TABLE "tournaments" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"type" TEXT NOT NULL,
"category" TEXT NOT NULL,
"allowedLevels" TEXT NOT NULL,
"maxParticipants" INTEGER NOT NULL,
"registrationStartDate" DATETIME NOT NULL,
"registrationEndDate" DATETIME NOT NULL,
"startDate" DATETIME NOT NULL,
"endDate" DATETIME NOT NULL,
"courtIds" TEXT NOT NULL,
"price" INTEGER NOT NULL DEFAULT 0,
"status" TEXT NOT NULL DEFAULT 'DRAFT',
"createdById" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "tournaments_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "tournament_participants" (
"id" TEXT NOT NULL PRIMARY KEY,
"tournamentId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"registrationDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"paymentStatus" TEXT NOT NULL DEFAULT 'PENDING',
"seed" INTEGER,
"status" TEXT NOT NULL DEFAULT 'REGISTERED',
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "tournament_participants_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "tournaments" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "tournament_participants_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "tournament_matches" (
"id" TEXT NOT NULL PRIMARY KEY,
"tournamentId" TEXT NOT NULL,
"round" INTEGER NOT NULL,
"matchNumber" INTEGER NOT NULL,
"position" INTEGER NOT NULL,
"team1Player1Id" TEXT,
"team1Player2Id" TEXT,
"team2Player1Id" TEXT,
"team2Player2Id" TEXT,
"courtId" TEXT,
"scheduledDate" DATETIME,
"scheduledTime" TEXT,
"status" TEXT NOT NULL DEFAULT 'PENDING',
"team1Score" INTEGER,
"team2Score" INTEGER,
"winner" TEXT,
"nextMatchId" TEXT,
"confirmedBy" TEXT NOT NULL DEFAULT '[]',
"metadata" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "tournament_matches_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "tournaments" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_team1Player1Id_fkey" FOREIGN KEY ("team1Player1Id") REFERENCES "tournament_participants" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_team1Player2Id_fkey" FOREIGN KEY ("team1Player2Id") REFERENCES "tournament_participants" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_team2Player1Id_fkey" FOREIGN KEY ("team2Player1Id") REFERENCES "tournament_participants" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_team2Player2Id_fkey" FOREIGN KEY ("team2Player2Id") REFERENCES "tournament_participants" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_courtId_fkey" FOREIGN KEY ("courtId") REFERENCES "courts" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "tournament_matches_nextMatchId_fkey" FOREIGN KEY ("nextMatchId") REFERENCES "tournament_matches" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "leagues" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"type" TEXT NOT NULL DEFAULT 'TEAM_LEAGUE',
"format" TEXT NOT NULL DEFAULT 'DOUBLE_ROUND_ROBIN',
"matchesPerMatchday" INTEGER NOT NULL DEFAULT 2,
"startDate" DATETIME,
"endDate" DATETIME,
"status" TEXT NOT NULL DEFAULT 'DRAFT',
"createdById" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "leagues_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "league_teams" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"leagueId" TEXT NOT NULL,
"captainId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "league_teams_leagueId_fkey" FOREIGN KEY ("leagueId") REFERENCES "leagues" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "league_teams_captainId_fkey" FOREIGN KEY ("captainId") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "league_team_members" (
"id" TEXT NOT NULL PRIMARY KEY,
"teamId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"joinedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "league_team_members_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "league_teams" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "league_team_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "league_matches" (
"id" TEXT NOT NULL PRIMARY KEY,
"leagueId" TEXT NOT NULL,
"matchday" INTEGER NOT NULL,
"team1Id" TEXT NOT NULL,
"team2Id" TEXT NOT NULL,
"courtId" TEXT,
"scheduledDate" DATETIME,
"scheduledTime" TEXT,
"status" TEXT NOT NULL DEFAULT 'SCHEDULED',
"team1Score" INTEGER,
"team2Score" INTEGER,
"setDetails" TEXT,
"winner" TEXT,
"completedAt" DATETIME,
"notes" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "league_matches_leagueId_fkey" FOREIGN KEY ("leagueId") REFERENCES "leagues" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "league_matches_team1Id_fkey" FOREIGN KEY ("team1Id") REFERENCES "league_teams" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "league_matches_team2Id_fkey" FOREIGN KEY ("team2Id") REFERENCES "league_teams" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "league_matches_courtId_fkey" FOREIGN KEY ("courtId") REFERENCES "courts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "league_standings" (
"id" TEXT NOT NULL PRIMARY KEY,
"leagueId" TEXT NOT NULL,
"teamId" TEXT NOT NULL,
"matchesPlayed" INTEGER NOT NULL DEFAULT 0,
"matchesWon" INTEGER NOT NULL DEFAULT 0,
"matchesLost" INTEGER NOT NULL DEFAULT 0,
"matchesDrawn" INTEGER NOT NULL DEFAULT 0,
"setsFor" INTEGER NOT NULL DEFAULT 0,
"setsAgainst" INTEGER NOT NULL DEFAULT 0,
"gamesFor" INTEGER NOT NULL DEFAULT 0,
"gamesAgainst" INTEGER NOT NULL DEFAULT 0,
"points" INTEGER NOT NULL DEFAULT 0,
"position" INTEGER NOT NULL DEFAULT 0,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "league_standings_leagueId_fkey" FOREIGN KEY ("leagueId") REFERENCES "leagues" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "league_standings_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "league_teams" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "tournaments_status_idx" ON "tournaments"("status");
-- CreateIndex
CREATE INDEX "tournaments_createdById_idx" ON "tournaments"("createdById");
-- CreateIndex
CREATE INDEX "tournaments_startDate_idx" ON "tournaments"("startDate");
-- CreateIndex
CREATE INDEX "tournaments_registrationStartDate_idx" ON "tournaments"("registrationStartDate");
-- CreateIndex
CREATE INDEX "tournament_participants_tournamentId_idx" ON "tournament_participants"("tournamentId");
-- CreateIndex
CREATE INDEX "tournament_participants_userId_idx" ON "tournament_participants"("userId");
-- CreateIndex
CREATE INDEX "tournament_participants_paymentStatus_idx" ON "tournament_participants"("paymentStatus");
-- CreateIndex
CREATE INDEX "tournament_participants_status_idx" ON "tournament_participants"("status");
-- CreateIndex
CREATE UNIQUE INDEX "tournament_participants_tournamentId_userId_key" ON "tournament_participants"("tournamentId", "userId");
-- CreateIndex
CREATE INDEX "tournament_matches_tournamentId_idx" ON "tournament_matches"("tournamentId");
-- CreateIndex
CREATE INDEX "tournament_matches_round_idx" ON "tournament_matches"("round");
-- CreateIndex
CREATE INDEX "tournament_matches_status_idx" ON "tournament_matches"("status");
-- CreateIndex
CREATE INDEX "tournament_matches_courtId_idx" ON "tournament_matches"("courtId");
-- CreateIndex
CREATE INDEX "tournament_matches_nextMatchId_idx" ON "tournament_matches"("nextMatchId");
-- CreateIndex
CREATE INDEX "tournament_matches_tournamentId_round_idx" ON "tournament_matches"("tournamentId", "round");
-- CreateIndex
CREATE INDEX "leagues_status_idx" ON "leagues"("status");
-- CreateIndex
CREATE INDEX "leagues_createdById_idx" ON "leagues"("createdById");
-- CreateIndex
CREATE INDEX "leagues_startDate_idx" ON "leagues"("startDate");
-- CreateIndex
CREATE INDEX "league_teams_leagueId_idx" ON "league_teams"("leagueId");
-- CreateIndex
CREATE INDEX "league_teams_captainId_idx" ON "league_teams"("captainId");
-- CreateIndex
CREATE UNIQUE INDEX "league_teams_leagueId_name_key" ON "league_teams"("leagueId", "name");
-- CreateIndex
CREATE INDEX "league_team_members_teamId_idx" ON "league_team_members"("teamId");
-- CreateIndex
CREATE INDEX "league_team_members_userId_idx" ON "league_team_members"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "league_team_members_teamId_userId_key" ON "league_team_members"("teamId", "userId");
-- CreateIndex
CREATE INDEX "league_matches_leagueId_idx" ON "league_matches"("leagueId");
-- CreateIndex
CREATE INDEX "league_matches_matchday_idx" ON "league_matches"("matchday");
-- CreateIndex
CREATE INDEX "league_matches_team1Id_idx" ON "league_matches"("team1Id");
-- CreateIndex
CREATE INDEX "league_matches_team2Id_idx" ON "league_matches"("team2Id");
-- CreateIndex
CREATE INDEX "league_matches_status_idx" ON "league_matches"("status");
-- CreateIndex
CREATE INDEX "league_matches_scheduledDate_idx" ON "league_matches"("scheduledDate");
-- CreateIndex
CREATE UNIQUE INDEX "league_standings_teamId_key" ON "league_standings"("teamId");
-- CreateIndex
CREATE INDEX "league_standings_leagueId_idx" ON "league_standings"("leagueId");
-- CreateIndex
CREATE INDEX "league_standings_position_idx" ON "league_standings"("position");
-- CreateIndex
CREATE INDEX "league_standings_points_idx" ON "league_standings"("points");
-- CreateIndex
CREATE UNIQUE INDEX "league_standings_leagueId_teamId_key" ON "league_standings"("leagueId", "teamId");

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")
}

View File

@@ -0,0 +1,96 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('🌱 Seeding Fase 3 - Torneos y Ligas...\n');
const admin = await prisma.user.findUnique({ where: { email: 'admin@padel.com' } });
if (!admin) {
console.log('❌ Admin no encontrado. Ejecuta seed.ts primero.');
return;
}
const courts = await prisma.court.findMany({ where: { isActive: true }, take: 2 });
const courtIds = JSON.stringify(courts.map(c => c.id));
// Crear torneo de eliminatoria
const tournament1 = await prisma.tournament.upsert({
where: { id: 'tour-1' },
update: {},
create: {
id: 'tour-1',
name: 'Torneo de Verano 2024',
description: 'Torneo eliminatorio mixto para todos los niveles',
type: 'ELIMINATION',
category: 'MIXED',
allowedLevels: JSON.stringify(['BEGINNER', 'ELEMENTARY', 'INTERMEDIATE']),
maxParticipants: 16,
registrationStartDate: new Date(),
registrationEndDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
startDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
endDate: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000),
courtIds: courtIds,
price: 2000,
status: 'OPEN',
createdById: admin.id,
},
});
console.log(`✅ Torneo creado: ${tournament1.name}`);
// Crear torneo round robin
const tournament2 = await prisma.tournament.upsert({
where: { id: 'tour-2' },
update: {},
create: {
id: 'tour-2',
name: 'Liga de Invierno - Individual',
description: 'Liga todos contra todos',
type: 'ROUND_ROBIN',
category: 'MEN',
allowedLevels: JSON.stringify(['INTERMEDIATE', 'ADVANCED', 'COMPETITION']),
maxParticipants: 8,
registrationStartDate: new Date(),
registrationEndDate: new Date(Date.now() + 10 * 24 * 60 * 60 * 1000),
startDate: new Date(Date.now() + 17 * 24 * 60 * 60 * 1000),
endDate: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000),
courtIds: courtIds,
price: 3000,
status: 'DRAFT',
createdById: admin.id,
},
});
console.log(`✅ Torneo creado: ${tournament2.name}`);
// Crear una liga por equipos
const league = await prisma.league.upsert({
where: { id: 'league-1' },
update: {},
create: {
id: 'league-1',
name: 'Liga de Club 2024',
description: 'Liga interna del club por equipos',
format: 'SINGLE_ROUND_ROBIN',
startDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
endDate: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
status: 'DRAFT',
createdById: admin.id,
},
});
console.log(`✅ Liga creada: ${league.name}`);
console.log('\n🎾 Fase 3 seed completado!');
console.log('\nDatos creados:');
console.log(` - 2 Torneos (Eliminatoria, Round Robin)`);
console.log(` - 1 Liga por equipos`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});