sto-k-odnomu/backend/src/rooms/rooms.service.ts
2026-01-08 20:56:00 +03:00

274 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Injectable, Inject, forwardRef } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { customAlphabet } from 'nanoid';
import { RoomEventsService } from '../game/room-events.service';
import { RoomPackService } from '../room-pack/room-pack.service';
const nanoid = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 6);
@Injectable()
export class RoomsService {
constructor(
private prisma: PrismaService,
@Inject(forwardRef(() => RoomEventsService))
private roomEventsService: RoomEventsService,
private roomPackService: RoomPackService,
) {}
async createRoom(hostId: string, questionPackId?: string, settings?: any) {
const code = nanoid();
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
// Remove undefined values from settings and ensure questionPackId is handled correctly
const cleanSettings = settings ? { ...settings } : {};
if ('questionPackId' in cleanSettings) {
delete cleanSettings.questionPackId;
}
const room = await this.prisma.room.create({
data: {
code,
hostId,
expiresAt,
...cleanSettings,
questionPackId: questionPackId || null,
},
include: {
host: true,
questionPack: true,
},
});
await this.prisma.participant.create({
data: {
userId: hostId,
roomId: room.id,
name: room.host.name || 'Host',
role: 'HOST',
},
});
// Create RoomPack (always, even if empty)
await this.roomPackService.create(room.id, questionPackId);
// Return room with roomPack
return this.getRoomByCode(room.code);
}
async getRoomByCode(code: string) {
return this.prisma.room.findUnique({
where: { code },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
roomPack: true,
},
});
}
async joinRoom(roomId: string, userId: string, name: string, role: 'PLAYER' | 'SPECTATOR') {
const participant = await this.prisma.participant.create({
data: {
userId,
roomId,
name,
role,
},
});
// Получаем обновленную комнату со всеми участниками
const room = await this.prisma.room.findUnique({
where: { id: roomId },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
// Отправляем событие roomUpdate всем клиентам в комнате
if (room) {
this.roomEventsService.emitRoomUpdate(room.code, room);
}
return participant;
}
async updateRoomStatus(roomId: string, status: 'WAITING' | 'PLAYING' | 'FINISHED') {
return this.prisma.room.update({
where: { id: roomId },
data: { status },
});
}
async updateParticipantScore(participantId: string, score: number) {
return this.prisma.participant.update({
where: { id: participantId },
data: { score },
});
}
async updateQuestionPack(roomId: string, questionPackId: string) {
return this.prisma.room.update({
where: { id: roomId },
data: {
questionPackId,
currentQuestionIndex: 0,
revealedAnswers: {},
},
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
}
async updateCustomQuestions(roomId: string, questions: any) {
// DEPRECATED: Use updateRoomPack instead
return this.updateRoomPack(roomId, questions);
}
async updateRoomPack(roomId: string, questions: any[]) {
await this.roomPackService.updateQuestions(roomId, questions);
const room = await this.prisma.room.findUnique({
where: { id: roomId },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
roomPack: true,
},
});
this.roomEventsService.emitRoomPackUpdated(room.code, room);
return room;
}
async getEffectiveQuestions(roomId: string) {
const room = await this.prisma.room.findUnique({
where: { id: roomId },
include: { roomPack: true, questionPack: true },
});
if (!room) {
return null;
}
// Priority 1: RoomPack questions
if (room.roomPack && room.roomPack.questions) {
return room.roomPack.questions;
}
// Priority 2: QuestionPack (fallback for legacy rooms)
if (room.questionPack) {
return room.questionPack.questions;
}
return null;
}
async updateRoomSettings(roomId: string, settings: any) {
const room = await this.prisma.room.update({
where: { id: roomId },
data: settings,
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
this.roomEventsService.emitRoomUpdate(room.code, room);
return room;
}
async restartGame(roomId: string) {
await this.prisma.room.update({
where: { id: roomId },
data: {
status: 'WAITING',
currentQuestionIndex: 0,
revealedAnswers: {},
currentPlayerId: null,
isGameOver: false,
answeredQuestions: 0,
},
});
await this.prisma.participant.updateMany({
where: { roomId },
data: { score: 0 },
});
const room = await this.prisma.room.findUnique({
where: { id: roomId },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
if (room) {
this.roomEventsService.emitGameRestarted(room.code, room);
}
return room;
}
async setCurrentPlayer(roomId: string, playerId: string) {
const room = await this.prisma.room.update({
where: { id: roomId },
data: { currentPlayerId: playerId },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
this.roomEventsService.emitCurrentPlayerChanged(room.code, { playerId });
return room;
}
async kickPlayer(roomId: string, participantId: string) {
await this.prisma.participant.update({
where: { id: participantId },
data: { isActive: false },
});
const room = await this.prisma.room.findUnique({
where: { id: roomId },
include: {
host: true,
participants: {
include: { user: true },
},
questionPack: true,
},
});
if (room) {
this.roomEventsService.emitPlayerKicked(room.code, { participantId, room });
}
return room;
}
}