Files
yourpart3/backend/services/taxiHighscoreService.js
Torsten Schulz (local) 42349e46c8 Änderung: Hinzufügen von TaxiHighscore-Logik und Verbesserung der API-Integration
Änderungen:
- Implementierung des neuen Routers für TaxiHighscore zur Verwaltung von Highscore-Daten.
- Anpassung der Datenbankmodelle zur Unterstützung von TaxiHighscore-Associations.
- Erweiterung der Vue-Komponenten zur Anzeige und Speicherung von Highscores im Taxi-Spiel.
- Verbesserung der Statusanzeige im AppHeader zur besseren Benutzerinteraktion.

Diese Anpassungen erweitern die Spielmechanik und Benutzererfahrung, indem sie die Verwaltung von Highscores integrieren und die Benutzeroberfläche optimieren.
2025-10-05 00:04:28 +02:00

272 lines
8.0 KiB
JavaScript

import models from '../models/index.js';
import BaseService from './BaseService.js';
const { TaxiHighscore, User, TaxiMap } = models;
class TaxiHighscoreService extends BaseService {
constructor() {
super();
this.model = TaxiHighscore;
}
/**
* Speichert oder aktualisiert einen Highscore-Eintrag
* Jeder User kann nur einen Eintrag pro Map haben (der beste wird gespeichert)
* @param {Object} highscoreData - Die Highscore-Daten
* @param {number} highscoreData.userId - ID des Users
* @param {string} highscoreData.nickname - Nickname des Users
* @param {number} highscoreData.passengersDelivered - Anzahl abgelieferter Passagiere
* @param {number} highscoreData.playtime - Spielzeit in Sekunden
* @param {number} highscoreData.points - Erreichte Punkte
* @param {number} highscoreData.mapId - ID der Map
* @param {string} highscoreData.mapName - Name der Map
* @returns {Promise<Object>} Gespeicherter oder aktualisierter Highscore-Eintrag
*/
async createHighscore(highscoreData) {
try {
// Prüfen ob bereits ein Eintrag für diesen User und diese Map existiert
const existingHighscore = await this.model.findOne({
where: {
userId: highscoreData.userId,
mapId: highscoreData.mapId
}
});
if (existingHighscore) {
// Nur aktualisieren wenn der neue Score besser ist (mehr Punkte)
if (highscoreData.points > existingHighscore.points) {
await existingHighscore.update({
nickname: highscoreData.nickname,
passengersDelivered: highscoreData.passengersDelivered,
playtime: highscoreData.playtime,
points: highscoreData.points,
mapName: highscoreData.mapName
});
return existingHighscore;
} else {
// Neuer Score ist nicht besser, existierenden zurückgeben
return existingHighscore;
}
} else {
// Kein existierender Eintrag, neuen erstellen
const highscore = await this.model.create({
userId: highscoreData.userId,
nickname: highscoreData.nickname,
passengersDelivered: highscoreData.passengersDelivered,
playtime: highscoreData.playtime,
points: highscoreData.points,
mapId: highscoreData.mapId,
mapName: highscoreData.mapName
});
return highscore;
}
} catch (error) {
console.error('Fehler beim Erstellen/Aktualisieren des Highscores:', error);
throw error;
}
}
/**
* Holt die Top-Highscores für eine bestimmte Map
* @param {number} mapId - ID der Map (optional)
* @param {number} limit - Anzahl der Einträge (Standard: 10)
* @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
* @returns {Promise<Array>} Array der Highscore-Einträge
*/
async getTopHighscores(mapId = null, limit = 10, orderBy = 'points') {
try {
const whereClause = mapId ? { mapId } : {};
const highscores = await this.model.findAll({
where: whereClause,
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username'],
required: false // LEFT JOIN, da User gelöscht sein könnte
},
{
model: TaxiMap,
as: 'map',
attributes: ['id', 'name'],
required: false
}
],
order: [[orderBy, 'DESC']],
limit: parseInt(limit)
});
return highscores;
} catch (error) {
console.error('Fehler beim Laden der Highscores:', error);
throw error;
}
}
/**
* Holt die persönlichen Bestleistungen eines Users
* @param {number} userId - ID des Users
* @param {number} mapId - ID der Map (optional)
* @returns {Promise<Object>} Bestleistungen des Users
*/
async getUserBestScores(userId, mapId = null) {
try {
const whereClause = { userId };
if (mapId) {
whereClause.mapId = mapId;
}
const [bestPoints, bestPassengers, bestPlaytime] = await Promise.all([
this.model.findOne({
where: whereClause,
order: [['points', 'DESC']]
}),
this.model.findOne({
where: whereClause,
order: [['passengersDelivered', 'DESC']]
}),
this.model.findOne({
where: whereClause,
order: [['playtime', 'DESC']]
})
]);
return {
bestPoints,
bestPassengers,
bestPlaytime
};
} catch (error) {
console.error('Fehler beim Laden der User-Bestleistungen:', error);
throw error;
}
}
/**
* Holt alle Highscores eines Users
* @param {number} userId - ID des Users
* @param {number} limit - Anzahl der Einträge (Standard: 20)
* @returns {Promise<Array>} Array der Highscore-Einträge des Users
*/
async getUserHighscores(userId, limit = 20) {
try {
const highscores = await this.model.findAll({
where: { userId },
include: [
{
model: TaxiMap,
as: 'map',
attributes: ['id', 'name']
}
],
order: [['createdAt', 'DESC']],
limit: parseInt(limit)
});
return highscores;
} catch (error) {
console.error('Fehler beim Laden der User-Highscores:', error);
throw error;
}
}
/**
* Holt die Rangliste-Position eines Users für eine bestimmte Map
* @param {number} userId - ID des Users
* @param {number} mapId - ID der Map (optional)
* @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
* @returns {Promise<number>} Rangliste-Position (1-basiert)
*/
async getUserRank(userId, mapId = null, orderBy = 'points') {
try {
const whereClause = mapId ? { mapId } : {};
const userHighscore = await this.model.findOne({
where: { userId, ...whereClause },
order: [[orderBy, 'DESC']]
});
if (!userHighscore) {
return null; // User hat noch keinen Highscore
}
const rank = await this.model.count({
where: {
...whereClause,
[orderBy]: {
[this.model.sequelize.Sequelize.Op.gt]: userHighscore[orderBy]
}
}
});
return rank + 1; // 1-basierte Position
} catch (error) {
console.error('Fehler beim Berechnen der User-Rangliste:', error);
throw error;
}
}
/**
* Löscht alle Highscores eines Users (z.B. bei Account-Löschung)
* @param {number} userId - ID des Users
* @returns {Promise<number>} Anzahl der gelöschten Einträge
*/
async deleteUserHighscores(userId) {
try {
const deletedCount = await this.model.destroy({
where: { userId }
});
return deletedCount;
} catch (error) {
console.error('Fehler beim Löschen der User-Highscores:', error);
throw error;
}
}
/**
* Holt Statistiken über die Highscores
* @returns {Promise<Object>} Statistiken
*/
async getHighscoreStats() {
try {
const [
totalHighscores,
totalPlayers,
averagePoints,
totalPassengersDelivered,
totalPlaytime
] = await Promise.all([
this.model.count(),
this.model.count({
distinct: true,
col: 'userId'
}),
this.model.findOne({
attributes: [
[this.model.sequelize.fn('AVG', this.model.sequelize.col('points')), 'avg']
],
raw: true
}),
this.model.sum('passengersDelivered'),
this.model.sum('playtime')
]);
return {
totalHighscores,
totalPlayers,
averagePoints: averagePoints ? parseFloat(averagePoints.avg).toFixed(2) : 0,
totalPassengersDelivered: totalPassengersDelivered || 0,
totalPlaytime: totalPlaytime || 0
};
} catch (error) {
console.error('Fehler beim Laden der Highscore-Statistiken:', error);
throw error;
}
}
}
export default new TaxiHighscoreService();