Files
app-padel/backend/src/routes/tournamentMatch.routes.ts
Ivan Alcaraz 6494e2b38b 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
2026-01-31 08:38:54 +00:00

147 lines
4.0 KiB
TypeScript

import { Router } from 'express';
import { TournamentMatchController } from '../controllers/tournamentMatch.controller';
import { authenticate, authorize } from '../middleware/auth';
import { validate, validateQuery } from '../middleware/validate';
import { UserRole, TournamentMatchStatus } from '../utils/constants';
import { z } from 'zod';
const router = Router({ mergeParams: true });
// Schema para query params de filtros
const matchFiltersSchema = z.object({
round: z.string().regex(/^\d+$/).optional().transform(Number),
status: z.enum([
TournamentMatchStatus.PENDING,
TournamentMatchStatus.SCHEDULED,
TournamentMatchStatus.IN_PROGRESS,
TournamentMatchStatus.FINISHED,
TournamentMatchStatus.CANCELLED,
TournamentMatchStatus.BYE,
]).optional(),
courtId: z.string().uuid().optional(),
playerId: z.string().uuid().optional(),
fromDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
toDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
});
// Schema para actualizar partido
const updateMatchSchema = z.object({
courtId: z.string().uuid().optional(),
scheduledDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
scheduledTime: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/).optional(),
status: z.enum([
TournamentMatchStatus.PENDING,
TournamentMatchStatus.SCHEDULED,
TournamentMatchStatus.IN_PROGRESS,
TournamentMatchStatus.CANCELLED,
]).optional(),
notes: z.string().optional(),
});
// Schema para asignar cancha
const assignCourtSchema = z.object({
courtId: z.string().uuid('ID de cancha inválido'),
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Fecha debe ser YYYY-MM-DD'),
time: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, 'Hora debe ser HH:MM'),
});
// Schema para registrar resultado
const recordResultSchema = z.object({
team1Score: z.number().int().min(0, 'El puntaje no puede ser negativo'),
team2Score: z.number().int().min(0, 'El puntaje no puede ser negativo'),
});
// Schema para cancelar partido
const cancelMatchSchema = z.object({
reason: z.string().optional(),
});
// Schema para params de IDs
const matchIdSchema = z.object({
matchId: z.string().uuid('ID de partido inválido'),
});
// Listar partidos del torneo
router.get(
'/matches',
authenticate,
validateQuery(matchFiltersSchema),
TournamentMatchController.getMatches
);
// Obtener mis partidos en el torneo
router.get(
'/my-matches',
authenticate,
TournamentMatchController.getMyMatches
);
// Obtener partidos de un participante específico
router.get(
'/participants/:participantId/matches',
authenticate,
TournamentMatchController.getParticipantMatches
);
// Obtener un partido específico
router.get(
'/matches/:matchId',
authenticate,
validate(z.object({ matchId: z.string().uuid() })),
TournamentMatchController.getMatch
);
// Actualizar partido (solo admins)
router.put(
'/matches/:matchId',
authenticate,
authorize(UserRole.ADMIN, UserRole.SUPERADMIN),
validate(updateMatchSchema),
TournamentMatchController.updateMatch
);
// Asignar cancha (solo admins)
router.put(
'/matches/:matchId/assign-court',
authenticate,
authorize(UserRole.ADMIN, UserRole.SUPERADMIN),
validate(assignCourtSchema),
TournamentMatchController.assignCourt
);
// Iniciar partido (solo admins)
router.put(
'/matches/:matchId/start',
authenticate,
authorize(UserRole.ADMIN, UserRole.SUPERADMIN),
validate(matchIdSchema),
TournamentMatchController.startMatch
);
// Cancelar partido (solo admins)
router.put(
'/matches/:matchId/cancel',
authenticate,
authorize(UserRole.ADMIN, UserRole.SUPERADMIN),
validate(cancelMatchSchema),
TournamentMatchController.cancelMatch
);
// Registrar resultado (jugadores o admins)
router.put(
'/matches/:matchId/result',
authenticate,
validate(recordResultSchema),
TournamentMatchController.recordResult
);
// Confirmar resultado (jugadores)
router.put(
'/matches/:matchId/confirm',
authenticate,
validate(matchIdSchema),
TournamentMatchController.confirmResult
);
export default router;