feat: implement table assignment and distribution for tournament matches
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s

This commit is contained in:
Torsten Schulz (local)
2026-05-17 23:55:39 +02:00
parent 6c7ae6860b
commit 697e67d46e
16 changed files with 426 additions and 22 deletions

View File

@@ -2066,7 +2066,10 @@ class TournamentService {
}
match.isFinished = true;
match.result = `${win}:${lose}`;
// Wenn ein Match abgeschlossen wird, darf es nicht mehr als aktiv gelten
match.isActive = false;
await match.save();
console.log(`[finishMatch] match ${matchId} finished, result=${match.result}, isActive set to false`);
// Platz-3-Spiel (Legacy-KO ohne Stages): erst erzeugen, wenn beide Halbfinals fertig sind.
// Keine Placeholders beim KO-Start.
@@ -2894,6 +2897,80 @@ class TournamentService {
await match.save();
}
async setMatchTable(userToken, clubId, tournamentId, matchId, tableNumber) {
await checkAccess(userToken, clubId);
const match = await TournamentMatch.findOne({ where: { id: matchId, tournamentId } });
if (!match) throw new Error('Match nicht gefunden');
match.tableNumber = (tableNumber == null) ? null : Number(tableNumber);
await match.save();
return match.toJSON ? match.toJSON() : match;
}
async distributeTables(userToken, clubId, tournamentId) {
await checkAccess(userToken, clubId);
const tournament = await Tournament.findByPk(tournamentId);
if (!tournament || tournament.clubId != clubId) throw new Error('Turnier nicht gefunden');
const numTables = tournament.numberOfTables ? Number(tournament.numberOfTables) : 0;
if (!numTables || numTables < 1) throw new Error('Anzahl der Tische nicht gesetzt');
console.log(`[distributeTables] clubId=${clubId}, tournamentId=${tournamentId}, numberOfTables=${numTables}`);
// Ermittle aktuell laufende Matches, damit wir nur Spiele verteilen, bei denen beide Spieler frei sind
const activeMatches = await TournamentMatch.findAll({ where: { tournamentId, isActive: true, isFinished: false } });
const activePlayerIds = new Set();
activeMatches.forEach(am => {
if (am.player1Id) activePlayerIds.add(Number(am.player1Id));
if (am.player2Id) activePlayerIds.add(Number(am.player2Id));
});
console.log(`[distributeTables] activeMatches=${activeMatches.length} activePlayers=${[...activePlayerIds].slice(0,10).join(',')}`);
// Lade nur noch die nicht abgeschlossenen, noch nicht zugewiesenen Matches
const matches = await TournamentMatch.findAll({
where: { tournamentId, isFinished: false, tableNumber: null },
order: [ ['group_round', 'ASC'], ['id', 'ASC'] ]
});
console.log(`[distributeTables] candidateMatches=${matches.length}`);
let idx = 0;
const updated = [];
for (const m of matches) {
// Spiele mit BYE (fehlendem Spieler) überspringen
if (!m.player1Id || !m.player2Id) {
console.log(`[distributeTables] skipping match ${m.id} (BYE or missing player) p1=${m.player1Id} p2=${m.player2Id}`);
continue;
}
// Nur vergeben, wenn beide Spieler aktuell nicht in einem aktiven Match sind
if (activePlayerIds.has(Number(m.player1Id)) || activePlayerIds.has(Number(m.player2Id))) {
console.log(`[distributeTables] skipping match ${m.id} because player active p1=${m.player1Id} p2=${m.player2Id}`);
continue;
}
const assign = (idx % numTables) + 1;
m.tableNumber = assign;
// Markiere das Spiel als gestartet (läuft)
m.isActive = true;
await m.save();
updated.push(m.toJSON ? m.toJSON() : m);
console.log(`[distributeTables] assigned match ${m.id} -> table ${assign}`);
// Spieler gelten nun als aktiv - damit kein weiteres Spiel für sie zugewiesen wird
activePlayerIds.add(Number(m.player1Id));
activePlayerIds.add(Number(m.player2Id));
idx++;
}
console.log(`[distributeTables] finished: assignedCount=${updated.length}`);
return updated;
}
async resetKnockout(userToken, clubId, tournamentId, classId = null) {
await checkAccess(userToken, clubId);
// lösche alle Matches außer Gruppenphase