Sektor-Onboarding & Charaktererstellung (Frontend)
This commit is contained in:
@@ -1,63 +1,69 @@
|
||||
import pool from '../db.js';
|
||||
import * as sectorService from '../services/sector.service.js';
|
||||
|
||||
/**
|
||||
* Liefert alle Sektoren für das Frontend.
|
||||
* Unterteilt in "Meine Sektoren" (wo der User schon spielt)
|
||||
* und "Alle Sektoren" (die generell verfügbar sind)
|
||||
* Übergibt die HTTP-Anfrage an den zuständigen Service und liefert das Ergebnis aus.
|
||||
*/
|
||||
export const getSectorsList = async(req, res) => {
|
||||
export const getSectorsList = async (req, res) => {
|
||||
const accountId = req.user.accountId;
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// "Meine Sektoren" abfragen
|
||||
const mySectorsResult = await client.query(`
|
||||
SELECT
|
||||
s.id, s.name, s.status, s.speed_fleet, s.speed_economy,
|
||||
las.local_username,
|
||||
las.created_at as joined_at,
|
||||
las.updated_at as last_played
|
||||
FROM sectors s
|
||||
JOIN lobby_accounts_sectors las ON s.id = las.sector_id
|
||||
WHERE las.lobby_account_id = $1
|
||||
ORDER BY las.updated_at DESC
|
||||
`, [accountId]);
|
||||
|
||||
// "Neue Sektoren"
|
||||
const newSectorsResult = await client.query(`
|
||||
SELECT
|
||||
id, name, status, speed_fleet, speed_economy
|
||||
FROM sectors
|
||||
WHERE status != 'offline'
|
||||
AND id NOT IN (
|
||||
SELECT sector_id
|
||||
FROM lobby_accounts_sectors
|
||||
WHERE lobby_account_id = $1
|
||||
)
|
||||
ORDER BY created_at ASC
|
||||
`, [accountId]);
|
||||
|
||||
|
||||
// Daten platzhalter
|
||||
const mapSectorData = (sector) => ({
|
||||
...sector,
|
||||
online_players: Math.floor(Math.random() * 150) + 10,
|
||||
planets_count: (Math.floor(Math.random() * 150) + 10) * 12
|
||||
});
|
||||
|
||||
const sectorData = await sectorService.getSectorsList(accountId);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
mySectors: mySectorsResult.rows.map(mapSectorData),
|
||||
newSectors: newSectorsResult.rows.map(mapSectorData)
|
||||
...sectorData
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[SectorController] Fehler beim Abrufen der Sektoren:', error);
|
||||
console.error('[SectorController] Fehler in getSectorsList:', error);
|
||||
|
||||
return res.status(500).json({
|
||||
error: 'ERR_INTERNAL_SERVER'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Controller-Methode für den Sektor-Beitritt (Charaktererstellung).
|
||||
* Nimmt die Request an, validiert grob und leitet an den Service weiter.
|
||||
*/
|
||||
export const joinSector = async (req, res) => {
|
||||
const accountId = req.user.accountId;
|
||||
const { sectorId, localUsername, gender } = req.body;
|
||||
|
||||
if (!sectorId || !localUsername || !gender) {
|
||||
return res.status(400).json({
|
||||
error: 'ERR_MISSING_FIELDS'
|
||||
});
|
||||
}
|
||||
|
||||
if (localUsername.length < 3) {
|
||||
return res.status(400).json({
|
||||
error: 'ERR_USERNAME_TOO_SHORT'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await sectorService.joinSector(accountId, sectorId, localUsername, gender);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
message: 'MSG_SECTOR_JOINED_SUCCESS'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_ALREADY_IN_SECTOR' || error.code === 'ERR_LOCAL_USERNAME_TAKEN') {
|
||||
return res.status(409).json({
|
||||
error: error.code
|
||||
});
|
||||
}
|
||||
|
||||
// Generischer Fallback für echte Systemabstürze
|
||||
console.error('[SectorController] Fehler beim Sektor-Beitritt:', error);
|
||||
return res.status(500).json({
|
||||
error: 'ERR_INTERNAL_SERVER'
|
||||
});
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
@@ -5,5 +5,6 @@ import { requireAuth } from '../middlewares/auth.middleware.js';
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/list', requireAuth, sectorController.getSectorsList);
|
||||
router.post('/join', requireAuth, sectorController.joinSector);
|
||||
|
||||
export default router;
|
||||
@@ -58,11 +58,13 @@ CREATE TABLE IF NOT EXISTS lobby_accounts_sectors (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
lobby_account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE,
|
||||
sector_id UUID REFERENCES sectors(id) ON DELETE CASCADE,
|
||||
local_username VARCHAR(50) UNIQUE NOT NULL,
|
||||
local_username VARCHAR(50) NOT NULL,
|
||||
gender VARCHAR(20) NOT NULL,
|
||||
res_premium INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(lobby_account_id, sector_id)
|
||||
UNIQUE(lobby_account_id, sector_id),
|
||||
UNIQUE(sector_id, local_username)
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import pool from '../db.js';
|
||||
|
||||
/**
|
||||
* Ruft die Sektorlisten für einen bestimmten Account ab.
|
||||
* Unterteilt das Ergebnis in bereits beigetretene Sektoren ("Meine Sektoren")
|
||||
* und noch verfügbare Sektoren ("Neue Sektoren").
|
||||
*
|
||||
* @param {string} accountId - Die UUID des aktuell eingeloggten Spielers
|
||||
*
|
||||
* @returns {Promise<Object>} - Ein Objekt mit den Arrays 'mySectors' und 'newSectors'
|
||||
*/
|
||||
export const getSectorsList = async (accountId) => {
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
const mySectorsResult = await client.query(`
|
||||
SELECT
|
||||
s.id, s.name, s.status, s.speed_fleet, s.speed_economy,
|
||||
las.local_username, las.gender,
|
||||
las.created_at as joined_at,
|
||||
las.updated_at as last_played
|
||||
FROM sectors s
|
||||
JOIN lobby_accounts_sectors las ON s.id = las.sector_id
|
||||
WHERE las.lobby_account_id = $1
|
||||
ORDER BY las.updated_at DESC
|
||||
`, [accountId]);
|
||||
|
||||
const newSectorsResult = await client.query(`
|
||||
SELECT
|
||||
id, name, status, speed_fleet, speed_economy
|
||||
FROM sectors
|
||||
WHERE status != 'offline'
|
||||
AND id NOT IN (
|
||||
SELECT sector_id
|
||||
FROM lobby_accounts_sectors
|
||||
WHERE lobby_account_id = $1
|
||||
)
|
||||
ORDER BY created_at ASC
|
||||
`, [accountId]);
|
||||
|
||||
const mapSectorData = (sector) => ({
|
||||
...sector,
|
||||
online_players: Math.floor(Math.random() * 150) + 10,
|
||||
planets_count: (Math.floor(Math.random() * 150) + 10) * 12
|
||||
});
|
||||
|
||||
return {
|
||||
mySectors: mySectorsResult.rows.map(mapSectorData),
|
||||
newSectors: newSectorsResult.rows.map(mapSectorData)
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('[SectorService] Fehler beim Auslesen der Sektordaten:', error);
|
||||
throw error;
|
||||
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Trägt einen Spieler in einen neuen Sektor ein.
|
||||
* Führt die eigentliche Datenbankoperation durch und wirft spezifische
|
||||
* Fehler-Objekte, falls Constraints (UNIQUE-Regeln) verletzt werden.
|
||||
*
|
||||
* @param {string} accountId - Die UUID des Nutzers
|
||||
* @param {string} sectorId - Die UUID des Sektors
|
||||
* @param {string} localUsername - Der Commander-Name für diesen Sektor
|
||||
* @param {string} gender - Das gewählte Geschlecht ('male' oder 'female')
|
||||
*
|
||||
* @returns {boolean} - true bei Erfolg
|
||||
*/
|
||||
export const joinSector = async (accountId, sectorId, localUsername, gender) => {
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// Wir versuchen, den Datensatz direkt einzufügen.
|
||||
await client.query(`
|
||||
INSERT INTO lobby_accounts_sectors (lobby_account_id, sector_id, local_username, gender)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`, [accountId, sectorId, localUsername, gender]);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
// Fehlercode 23505 = Postgres "unique_violation"
|
||||
if (error.code === '23505') {
|
||||
|
||||
if (error.constraint === 'lobby_accounts_sectors_lobby_account_id_sector_id_key') {
|
||||
const customErr = new Error('Already in sector');
|
||||
customErr.code = 'ERR_ALREADY_IN_SECTOR';
|
||||
throw customErr;
|
||||
}
|
||||
|
||||
if (error.constraint === 'lobby_accounts_sectors_sector_id_local_username_key') {
|
||||
const customErr = new Error('Local username taken');
|
||||
customErr.code = 'ERR_LOCAL_USERNAME_TAKEN';
|
||||
throw customErr;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user