diff --git a/packages/kcms/src/match/adaptor/prisma/mainMatchRepository.ts b/packages/kcms/src/match/adaptor/prisma/mainMatchRepository.ts index d2fbfbf5..f65ca13f 100644 --- a/packages/kcms/src/match/adaptor/prisma/mainMatchRepository.ts +++ b/packages/kcms/src/match/adaptor/prisma/mainMatchRepository.ts @@ -116,6 +116,37 @@ export class PrismaMainMatchRepository implements MainMatchRepository { async update(match: MainMatch): Promise> { try { + const currentRunResults = await this.client.runResult.findMany({ + where: { mainMatchId: match.getId() }, + }); + const currentRunResultIDs = new Set(currentRunResults.map((v) => v.id)); + + // 複数更新と複数作成が同時にできないため、クエリを分ける + const { updatable: updatableRunResults, new: newRunResults } = match + .getRunResults() + .reduce<{ updatable: RunResult[]; new: RunResult[] }>( + (results, runResult) => { + const updateType = currentRunResultIDs.has(runResult.getId()) ? 'updatable' : 'new'; + results[updateType].push(runResult); + return results; + }, + { updatable: [], new: [] } + ); + + await this.client.runResult.createMany({ + data: newRunResults.map((v) => ({ + id: v.getId(), + teamID: v.getTeamId(), + points: v.getPoints(), + // NOTE: Infinity: 2147483647 + goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) + ? v.getGoalTimeSeconds() + : this.INT32MAX, + mainMatchId: match.getId(), + // NOTE: GOAL: 0 , FINISHED: 1 + finishState: v.isGoal() ? 0 : 1, + })), + }); await this.client.mainMatch.update({ where: { id: match.getId(), @@ -128,24 +159,22 @@ export class PrismaMainMatchRepository implements MainMatchRepository { rightTeamId: match.getTeamId2(), winnerTeamId: match.getWinnerId(), runResult: { - updateMany: match.getRunResults().map((v) => { - return { - where: { - id: v.getId(), - }, - data: { - id: v.getId(), - teamID: v.getTeamId(), - points: v.getPoints(), - // NOTE: Infinity: 2147483647 - goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) - ? v.getGoalTimeSeconds() - : this.INT32MAX, - // NOTE: GOAL: 0 , FINISHED: 1 - finishState: v.isGoal() ? 0 : 1, - }, - }; - }), + updateMany: updatableRunResults.map((v) => ({ + where: { + id: v.getId(), + }, + data: { + id: v.getId(), + teamID: v.getTeamId(), + points: v.getPoints(), + // NOTE: Infinity: 2147483647 + goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) + ? v.getGoalTimeSeconds() + : this.INT32MAX, + // NOTE: GOAL: 0 , FINISHED: 1 + finishState: v.isGoal() ? 0 : 1, + }, + })), }, }, }); diff --git a/packages/kcms/src/match/adaptor/prisma/preMatchRepository.ts b/packages/kcms/src/match/adaptor/prisma/preMatchRepository.ts index 1b7b7271..4d0dee1b 100644 --- a/packages/kcms/src/match/adaptor/prisma/preMatchRepository.ts +++ b/packages/kcms/src/match/adaptor/prisma/preMatchRepository.ts @@ -115,72 +115,69 @@ export class PrismaPreMatchRepository implements PreMatchRepository { async update(match: PreMatch): Promise> { try { - const runResults = await this.client.runResult.findMany({ + const currentRunResults = await this.client.runResult.findMany({ where: { preMatchId: match.getId(), }, }); - if (runResults.length === 0) { - await this.client.runResult.createMany({ - data: match.getRunResults().map((v) => { - return { - id: v.getId(), - teamID: v.getTeamId(), - points: v.getPoints(), - // NOTE: Infinity: 2147483647 - goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) - ? v.getGoalTimeSeconds() - : this.INT32MAX, - preMatchId: match.getId(), - // NOTE: GOAL: 0 , FINISHED: 1 - finishState: v.isGoal() ? 0 : 1, - }; - }), - }); - await this.client.preMatch.update({ - where: { - id: match.getId(), - }, - data: { - courseIndex: match.getCourseIndex(), - matchIndex: match.getMatchIndex(), - leftTeamID: match.getTeamId1(), - rightTeamID: match.getTeamId2(), - }, - }); - } else { - await this.client.preMatch.update({ - where: { - id: match.getId(), + const currentRunResultIDs = new Set(currentRunResults.map((v) => v.id)); + + // 複数更新と複数作成が同時にできないため、クエリを分ける + const { updatable: updatableRunResults, new: newRunResults } = match + .getRunResults() + .reduce<{ updatable: RunResult[]; new: RunResult[] }>( + (results, runResult) => { + const updateType = currentRunResultIDs.has(runResult.getId()) ? 'updatable' : 'new'; + results[updateType].push(runResult); + return results; }, - data: { - courseIndex: match.getCourseIndex(), - matchIndex: match.getMatchIndex(), - leftTeamID: match.getTeamId1(), - rightTeamID: match.getTeamId2(), - runResult: { - updateMany: match.getRunResults().map((v) => { - return { - where: { - id: v.getId(), - }, - data: { - id: v.getId(), - teamID: v.getTeamId(), - points: v.getPoints(), - // NOTE: Infinity: 2147483647 - goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) - ? v.getGoalTimeSeconds() - : this.INT32MAX, - // NOTE: GOAL: 0 , FINISHED: 1 - finishState: v.isGoal() ? 0 : 1, - }, - }; - }), - }, + { updatable: [], new: [] } + ); + + await this.client.runResult.createMany({ + data: newRunResults.map((v) => ({ + id: v.getId(), + teamID: v.getTeamId(), + points: v.getPoints(), + // NOTE: Infinity: 2147483647 + goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) + ? v.getGoalTimeSeconds() + : this.INT32MAX, + preMatchId: match.getId(), + // NOTE: GOAL: 0 , FINISHED: 1 + finishState: v.isGoal() ? 0 : 1, + })), + }); + await this.client.preMatch.update({ + where: { + id: match.getId(), + }, + data: { + courseIndex: match.getCourseIndex(), + matchIndex: match.getMatchIndex(), + departmentType: match.getDepartmentType(), + leftTeamID: match.getTeamId1(), + rightTeamID: match.getTeamId2(), + runResult: { + updateMany: updatableRunResults.map((v) => ({ + where: { + id: v.getId(), + }, + data: { + id: v.getId(), + teamID: v.getTeamId(), + points: v.getPoints(), + // NOTE: Infinity: 2147483647 + goalTimeSeconds: isFinite(v.getGoalTimeSeconds()) + ? v.getGoalTimeSeconds() + : this.INT32MAX, + // NOTE: GOAL: 0 , FINISHED: 1 + finishState: v.isGoal() ? 0 : 1, + }, + })), }, - }); - } + }, + }); return Result.ok(undefined); } catch (e) { return Result.err(e as Error); diff --git a/packages/kcms/src/match/model/main.test.ts b/packages/kcms/src/match/model/main.test.ts index 9e7045e5..a7f5da18 100644 --- a/packages/kcms/src/match/model/main.test.ts +++ b/packages/kcms/src/match/model/main.test.ts @@ -28,21 +28,19 @@ describe('MainMatch', () => { }); it('走行結果は0 or 2 or 4になる', () => { - const args = { - id: '1' as MainMatchID, - courseIndex: 1, - matchIndex: 1, - departmentType: config.departmentTypes[0], - teamId1: '2' as TeamID, - teamId2: '3' as TeamID, - winnerId: '2' as TeamID, - runResults: [], - } satisfies CreateMainMatchArgs; - for (let j = 1; j < 100; j++) { - const mainMatch = MainMatch.new(args); + const mainMatch = MainMatch.new({ + id: '1' as MainMatchID, + courseIndex: 1, + matchIndex: 1, + departmentType: config.departmentTypes[0], + teamId1: '2' as TeamID, + teamId2: '3' as TeamID, + winnerId: '2' as TeamID, + runResults: [], + }); // 2か4以外は足せない - if (j == 2 || j == 4) { + if (j == 1 || j == 2 || j == 4) { expect(() => { mainMatch.appendRunResults( [...Array(j)].map((_, i) => { @@ -50,12 +48,13 @@ describe('MainMatch', () => { id: String(i) as RunResultID, goalTimeSeconds: i * 10, points: 10 + i, - teamID: i % 2 == 0 ? args.teamId1 : args.teamId2, + teamID: i % 2 == 0 ? ('2' as TeamID) : ('3' as TeamID), finishState: 'FINISHED', }); }) ); }).not.toThrow(new Error('RunResult length must be 2 or 4')); + expect(mainMatch.getRunResults().length).toBe(j); continue; } expect(() => { @@ -65,12 +64,59 @@ describe('MainMatch', () => { id: String(i) as RunResultID, goalTimeSeconds: i * 10, points: 10 + i, - teamID: i % 2 == 0 ? args.teamId1 : args.teamId2, + teamID: i % 2 == 0 ? ('2' as TeamID) : ('3' as TeamID), finishState: 'FINISHED', }); }) ); }).toThrow(new Error('RunResult length must be 2 or 4')); + expect(mainMatch.getRunResults().length).toBe(0); + } + }); + + it('走行結果を追加できる', () => { + for (let i = 1; i <= 2; i++) { + const mainMatch = MainMatch.new({ + id: '1' as MainMatchID, + courseIndex: 1, + matchIndex: 1, + departmentType: config.departmentTypes[0], + teamId1: '2' as TeamID, + teamId2: '3' as TeamID, + winnerId: '2' as TeamID, + runResults: [], + }); + for (let j = 1; j < 8; j++) { + if (j === 1 || j == 2) { + expect(() => { + mainMatch.appendRunResults( + [...Array(i)].map((_, i) => { + return RunResult.new({ + id: String(i) as RunResultID, + goalTimeSeconds: i * 10, + points: 10 + i, + teamID: i % 2 == 0 ? ('2' as TeamID) : ('3' as TeamID), + finishState: 'FINISHED', + }); + }) + ); + }).not.toThrow(new Error('RunResult length must be 2 or 4')); + } else { + expect(() => { + mainMatch.appendRunResults( + [...Array(i)].map((_, i) => { + return RunResult.new({ + id: String(i) as RunResultID, + goalTimeSeconds: i * 10, + points: 10 + i, + teamID: i % 2 == 0 ? ('2' as TeamID) : ('3' as TeamID), + finishState: 'FINISHED', + }); + }) + ); + }).toThrow(new Error('RunResult length must be 2 or 4')); + } + } } }); diff --git a/packages/kcms/src/match/model/main.ts b/packages/kcms/src/match/model/main.ts index 45308cc9..b7f2814d 100644 --- a/packages/kcms/src/match/model/main.ts +++ b/packages/kcms/src/match/model/main.ts @@ -91,9 +91,9 @@ export class MainMatch { appendRunResults(results: RunResult[]) { // 1チームが2つずつ結果を持つので、2 または 4個 const appendedLength = this.runResults.length + results.length; - if (appendedLength !== 4 && appendedLength !== 2) { + if (appendedLength !== 4 && appendedLength !== 2 && appendedLength !== 1) { throw new Error('RunResult length must be 2 or 4'); } - this.runResults.concat(results); + this.runResults.push(...results); } }