diff --git a/app/game-api/src/router/nation/endpoints/getChiefCenter.ts b/app/game-api/src/router/nation/endpoints/getChiefCenter.ts index 15f3e401..b5215211 100644 --- a/app/game-api/src/router/nation/endpoints/getChiefCenter.ts +++ b/app/game-api/src/router/nation/endpoints/getChiefCenter.ts @@ -3,7 +3,7 @@ import { TRPCError } from '@trpc/server'; import { accessAuthedProcedure } from '../../../trpc.js'; import { getMyGeneral } from '../../shared/general.js'; import { resolveSecretPermission } from '../../shared/secretPermission.js'; -import { MAX_NATION_TURNS, getNationTurnSnapshot } from '../../../turns/reservedTurns.js'; +import { MAX_NATION_TURNS, getNationTurnSnapshots } from '../../../turns/reservedTurns.js'; import { assertNationAccess } from '../shared.js'; export const getChiefCenter = accessAuthedProcedure.query(async ({ ctx }) => { @@ -56,17 +56,18 @@ export const getChiefCenter = accessAuthedProcedure.query(async ({ ctx }) => { const chiefLevels = [12, 10, 8, 6, 11, 9, 7, 5]; const generalByLevel = new Map(nationGenerals.map((general) => [general.officerLevel, general])); - const turnsByLevel = await Promise.all(chiefLevels.map((level) => getNationTurnSnapshot(ctx.db, nation.id, level))); + const turnsByLevel = await getNationTurnSnapshots(ctx.db, nation.id, chiefLevels); - const chiefs = chiefLevels.map((level, idx) => { + const chiefs = chiefLevels.map((level) => { const entry = generalByLevel.get(level); + const snapshot = turnsByLevel.get(level); return { officerLevel: level, name: entry?.name ?? null, npcState: entry?.npcState ?? null, turnTime: entry?.turnTime ? entry.turnTime.toISOString() : null, - revision: turnsByLevel[idx]?.revision ?? 0, - turns: turnsByLevel[idx]?.turns ?? [], + revision: snapshot?.revision ?? 0, + turns: snapshot?.turns ?? [], }; }); diff --git a/app/game-api/src/turns/reservedTurns.ts b/app/game-api/src/turns/reservedTurns.ts index 77e9863a..b133cea0 100644 --- a/app/game-api/src/turns/reservedTurns.ts +++ b/app/game-api/src/turns/reservedTurns.ts @@ -218,6 +218,42 @@ export const getNationTurnSnapshot = async ( }; }; +export const getNationTurnSnapshots = async ( + db: DatabaseClient, + nationId: number, + officerLevels: readonly number[] +): Promise> => { + const levels = [...new Set(officerLevels)]; + if (levels.length === 0) return new Map(); + const [turnRows, revisionRows] = await Promise.all([ + db.nationTurn.findMany({ + where: { nationId, officerLevel: { in: levels } }, + orderBy: [{ officerLevel: 'asc' }, { turnIdx: 'asc' }], + }), + db.nationTurnRevision.findMany({ + where: { nationId, officerLevel: { in: levels } }, + }), + ]); + const turnsByLevel = new Map(); + for (const row of turnRows) { + const rows = turnsByLevel.get(row.officerLevel) ?? []; + rows.push(row); + turnsByLevel.set(row.officerLevel, rows); + } + const revisionByLevel = new Map(revisionRows.map((row) => [row.officerLevel, row.revision])); + return new Map( + levels.map((level) => [ + level, + { + revision: revisionByLevel.get(level) ?? 0, + turns: serializeTurnList( + buildTurnListFromRows(turnsByLevel.get(level) ?? [], MAX_NATION_TURNS) + ), + }, + ]) + ); +}; + const claimGeneralRevision = async ( db: DatabaseClient, generalId: number, diff --git a/app/game-api/test/reservedTurns.test.ts b/app/game-api/test/reservedTurns.test.ts index 1ca8cf02..833d2b33 100644 --- a/app/game-api/test/reservedTurns.test.ts +++ b/app/game-api/test/reservedTurns.test.ts @@ -5,6 +5,7 @@ import { MAX_GENERAL_TURNS, MAX_NATION_TURNS, expandGeneralTurnIndices, + getNationTurnSnapshots, repeatGeneralTurns, repeatNationTurns, setGeneralTurn, @@ -201,6 +202,52 @@ const buildDb = (autorunLimit: number | null = null) => { }; describe('reservedTurns', () => { + it('loads multiple nation officer queues with two batched queries and preserves defaults', async () => { + const findTurns = vi.fn(async () => [ + { + id: 1, + nationId: 4, + officerLevel: 12, + turnIdx: 1, + actionCode: 'che_징병', + arg: { amount: 100 }, + createdAt: new Date(), + }, + { + id: 2, + nationId: 4, + officerLevel: 10, + turnIdx: 0, + actionCode: 'che_훈련', + arg: {}, + createdAt: new Date(), + }, + ] satisfies NationTurnRow[]); + const findRevisions = vi.fn(async () => [ + { nationId: 4, officerLevel: 12, revision: 7, updatedAt: new Date() }, + ]); + const db = { + nationTurn: { findMany: findTurns }, + nationTurnRevision: { findMany: findRevisions }, + } as unknown as DatabaseClient; + + const snapshots = await getNationTurnSnapshots(db, 4, [12, 10, 8, 12]); + + expect(findTurns).toHaveBeenCalledOnce(); + expect(findRevisions).toHaveBeenCalledOnce(); + expect(snapshots.size).toBe(3); + expect(snapshots.get(12)?.revision).toBe(7); + expect(snapshots.get(12)?.turns.slice(0, 2)).toEqual([ + { index: 0, action: '휴식', args: {} }, + { index: 1, action: 'che_징병', args: { amount: 100 } }, + ]); + expect(snapshots.get(10)?.revision).toBe(0); + expect(snapshots.get(10)?.turns[0]).toEqual({ index: 0, action: 'che_훈련', args: {} }); + expect(snapshots.get(8)?.revision).toBe(0); + expect(snapshots.get(8)?.turns[0]).toEqual({ index: 0, action: '휴식', args: {} }); + expect(snapshots.get(8)?.turns).toHaveLength(MAX_NATION_TURNS); + }); + it('sets and shifts general turns', async () => { const { db } = buildDb(2408);