diff --git a/server/prisma/migrations/20241212195253_active_challenge/migration.sql b/server/prisma/migrations/20241212195253_active_challenge/migration.sql new file mode 100644 index 0000000..b01fd2b --- /dev/null +++ b/server/prisma/migrations/20241212195253_active_challenge/migration.sql @@ -0,0 +1,18 @@ +/* + Warnings: + + - You are about to drop the column `active` on the `ChallengeAction` table. All the data in the column will be lost. + - A unique constraint covering the columns `[activeChallengeId]` on the table `Player` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "ChallengeAction" DROP COLUMN "active"; + +-- AlterTable +ALTER TABLE "Player" ADD COLUMN "activeChallengeId" INTEGER; + +-- CreateIndex +CREATE UNIQUE INDEX "Player_activeChallengeId_key" ON "Player"("activeChallengeId"); + +-- AddForeignKey +ALTER TABLE "Player" ADD CONSTRAINT "Player_activeChallengeId_fkey" FOREIGN KEY ("activeChallengeId") REFERENCES "ChallengeAction"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index ad59b4d..8b2570f 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -8,15 +8,17 @@ datasource db { } model Player { - id Int @id @default(autoincrement()) - name String @unique - password String - money Int @default(0) - actions ChallengeAction[] - geolocations Geolocation[] - moneyUpdates MoneyUpdate[] - trips TrainTrip[] - runs PlayerRun[] + id Int @id @default(autoincrement()) + name String @unique + password String + money Int @default(0) + activeChallenge ChallengeAction? @relation("ActiveChallenge", fields: [activeChallengeId], references: [id]) + activeChallengeId Int? @unique + actions ChallengeAction[] + geolocations Geolocation[] + moneyUpdates MoneyUpdate[] + trips TrainTrip[] + runs PlayerRun[] } model Game { @@ -68,7 +70,6 @@ model ChallengeAction { playerId Int challenge Challenge @relation(fields: [challengeId], references: [id]) challengeId Int @unique - active Boolean @default(false) success Boolean @default(false) start DateTime @default(now()) @db.Timestamptz(3) end DateTime? @db.Timestamptz(3) @@ -76,6 +77,7 @@ model ChallengeAction { penaltyEnd DateTime? @db.Timestamptz(3) run PlayerRun @relation(fields: [runId], references: [id]) runId Int + activePlayer Player? @relation("ActiveChallenge") moneyUpdate MoneyUpdate? } diff --git a/server/src/challenge-actions/challenge-actions.service.ts b/server/src/challenge-actions/challenge-actions.service.ts index d75dd57..3a2c85e 100644 --- a/server/src/challenge-actions/challenge-actions.service.ts +++ b/server/src/challenge-actions/challenge-actions.service.ts @@ -20,11 +20,15 @@ export class ChallengeActionsService { }) } - async findAll(queryPagination: QueryPaginationDto, filterChallengeActions: FilterChallengeActionsDto): Promise<[ChallengeAction[], number]> { + async findAll(queryPagination: QueryPaginationDto, { playerId, challengeId, success }: FilterChallengeActionsDto): Promise<[ChallengeAction[], number]> { return [ await this.prisma.challengeAction.findMany({ ...paginate(queryPagination), - where: filterChallengeActions, + where: { + playerId: playerId, + challengeId: challengeId, + success: success, + } }), await this.prisma.challengeAction.count(), ] @@ -54,20 +58,14 @@ export class ChallengeActionsService { } async endCurrentChallenge(player: Player, success: boolean): Promise { - const challengeAction = await this.prisma.challengeAction.findFirst({ - where: { - playerId: player.id, - active: true, - } - }) - if (!challengeAction) + if (!player.activeChallengeId) throw new BadRequestException("Aucun défi n'est en cours") + const challengeAction = await this.prisma.challengeAction.findUnique({ where: { id: player.activeChallengeId } }) let data const now = new Date() if (success) { data = { success: success, - active: false, end: now, } @@ -85,12 +83,15 @@ export class ChallengeActionsService { else { data = { success: success, - active: false, end: now, penaltyStart: now, penaltyEnd: new Date(now.getTime() + Constants.PENALTY_TIME * 60 * 1000), } } + await this.prisma.player.update({ + where: { id: player.id }, + data: { activeChallengeId: null }, + }) return await this.prisma.challengeAction.update({ where: { id: challengeAction.id, diff --git a/server/src/challenge-actions/dto/create-challenge-action.dto.ts b/server/src/challenge-actions/dto/create-challenge-action.dto.ts index 35a4b43..a3ccc63 100644 --- a/server/src/challenge-actions/dto/create-challenge-action.dto.ts +++ b/server/src/challenge-actions/dto/create-challenge-action.dto.ts @@ -9,12 +9,6 @@ export class CreateChallengeActionDto { @ApiProperty({ description: "Identifiant du défi rattaché à l'action" }) challengeId: number - @IsOptional() - @IsBoolean() - @BooleanTransform() - @ApiProperty({ description: "Est-ce que le défi est actuellement en train d'être réalisé", default: true }) - active: boolean = true - @IsOptional() @IsBoolean() @BooleanTransform() diff --git a/server/src/challenge-actions/dto/filter-challenge-action.dto.ts b/server/src/challenge-actions/dto/filter-challenge-action.dto.ts index f5fcf06..16fa477 100644 --- a/server/src/challenge-actions/dto/filter-challenge-action.dto.ts +++ b/server/src/challenge-actions/dto/filter-challenge-action.dto.ts @@ -16,12 +16,6 @@ export class FilterChallengeActionsDto { @ApiProperty({ description: "Identifiant du défi attaché à cette action", required: false }) challengeId?: number - @IsOptional() - @IsBoolean() - @BooleanTransform() - @ApiProperty({ description: "Défi en train d'être accompli", required: false }) - active?: boolean - @IsOptional() @IsBoolean() @BooleanTransform() diff --git a/server/src/challenge-actions/entities/challenge-action.entity.ts b/server/src/challenge-actions/entities/challenge-action.entity.ts index 650c68d..65f5d9b 100644 --- a/server/src/challenge-actions/entities/challenge-action.entity.ts +++ b/server/src/challenge-actions/entities/challenge-action.entity.ts @@ -22,11 +22,6 @@ export class ChallengeActionEntity implements ChallengeAction { */ challengeId: number - /** - * Est-ce que le défi est actuellement en train d'être réalisé - */ - active: boolean - /** * Est-ce que le défi a été réussi */ diff --git a/server/src/challenges/challenges.service.ts b/server/src/challenges/challenges.service.ts index a552e31..125e59c 100644 --- a/server/src/challenges/challenges.service.ts +++ b/server/src/challenges/challenges.service.ts @@ -64,13 +64,7 @@ export class ChallengesService { const game = await this.prisma.game.findUnique({ where: { id: 1 }, include: { currentRun: true } }) if (game.currentRun?.runnerId !== player.id) throw new ConflictException("Vous n'êtes pas en course, ce n'est pas à vous de tirer un défi.") - const currentChallengeAction = await this.prisma.challengeAction.findFirst({ - where: { - playerId: player.id, - active: true, - } - }) - if (currentChallengeAction) + if (player.activeChallengeId) throw new ConflictException("Un défi est déjà en cours d'accomplissement") const remaningChallenges = await this.prisma.challenge.count({ where: { @@ -92,10 +86,13 @@ export class ChallengesService { playerId: player.id, challengeId: challenge.id, runId: game.currentRunId, - active: true, success: false, } }) + await this.prisma.player.update({ + where: { id: player.id }, + data: { activeChallengeId: action.id }, + }) challengeEntity.action = action return challengeEntity } diff --git a/server/src/game/game.service.ts b/server/src/game/game.service.ts index 07bd6c8..9a4661c 100644 --- a/server/src/game/game.service.ts +++ b/server/src/game/game.service.ts @@ -58,17 +58,17 @@ export class GameService { throw new ConflictException("La partie n'a pas encore démarré.") // Clôture de l'éventuel défi en cours, qui n'a alors pas été réussi - await this.prisma.challengeAction.updateMany({ - where: { - playerId: game.currentRun.runnerId, - runId: game.currentRunId, - active: true, - }, - data: { - active: false, - success: false, - }, - }) + const currentRunner = await this.prisma.player.findUnique({ where: { id: game.currentRun.runnerId } }) + if (currentRunner.activeChallengeId) { + await this.prisma.challengeAction.update({ + where: { id: currentRunner.activeChallengeId }, + data: { success: false }, + }) + await this.prisma.player.update({ + where: { id: currentRunner.id }, + data: { activeChallengeId: null }, + }) + } await this.prisma.playerRun.update({ where: { id: game.currentRunId }, @@ -173,7 +173,7 @@ export class GameService { const orpanChallengeMoneyUpdates = await this.prisma.moneyUpdate.findMany({ where: { reason: MoneyUpdateType.WIN_CHALLENGE, actionId: null } }) await this.prisma.moneyUpdate.deleteMany({ where: { reason: MoneyUpdateType.WIN_CHALLENGE, actionId: null } }) - deleted.push(...orpanTrainMoneyUpdates) + deleted.push(...orpanChallengeMoneyUpdates) return { added: added, deleted: deleted } } diff --git a/server/src/players/entities/player.entity.ts b/server/src/players/entities/player.entity.ts index bb112f8..6f0f397 100644 --- a/server/src/players/entities/player.entity.ts +++ b/server/src/players/entities/player.entity.ts @@ -1,21 +1,37 @@ import { ApiProperty } from "@nestjs/swagger" import { Player } from "@prisma/client" import { Exclude } from 'class-transformer' +import { IsOptional } from "class-validator" export class PlayerEntity implements Player { constructor(partial: Partial) { Object.assign(this, partial) } - @ApiProperty({description: "Identifiant unique"}) + /** + * Identifiant unique + */ id: number - @ApiProperty({description: "Nom de læ joueur⋅se"}) + /** + * Nom de læ joueur⋅se + */ name: string + /** + * Mot de passe hashé + */ @Exclude() password: string - @ApiProperty({description: "Nombre de jetons dont dispose actuellement læ joueur⋅se"}) + /** + * Nombre de jetons dont dispose actuellement læ joueur⋅se + */ money: number + + /** + * Identifiant du défi en cours d'accomplissement + */ + @IsOptional() + activeChallengeId: number | null }