fix: 지난 플레이 전투 자료를 보존한다

통일·장수 삭제·게임 취소 archive에 전투 집계와 숙련도, 전투 결과를 저장한다. 개인 기록과 전투 상세는 보존 대상에서 제외하고 기존 Core snapshot도 정규화한다.
This commit is contained in:
2026-08-22 09:15:45 +00:00
parent d74a8e48bb
commit d3d89cf003
12 changed files with 484 additions and 78 deletions
@@ -143,7 +143,9 @@ type ActiveGeneral = {
const buildActiveGeneralArchive = (
general: ActiveGeneral,
ranks: Record<string, number>,
history: string[],
battleResults: string[],
cancellation: { id: string; at: Date; reason: string }
): InputJsonValue =>
asJson({
@@ -190,9 +192,14 @@ const buildActiveGeneralArchive = (
},
},
lastTurn: general.lastTurn,
meta: general.meta,
meta: {
...asRecord(general.meta),
...Object.fromEntries(Object.entries(ranks).map(([key, value]) => [`rank_${key}`, value])),
},
penalty: general.penalty,
history,
records: { battleResult: battleResults },
availability: { battleResultLogs: true },
abandonedGame: {
cancellationId: cancellation.id,
cancelledAt: cancellation.at.toISOString(),
@@ -266,14 +273,14 @@ const cancelGameInTransaction = async (
]);
const activeIds = activeGenerals.map((general) => general.id);
const [resolvedRankRows, resolvedHistoryLogs] = await Promise.all([
const [resolvedRankRows, resolvedRecordLogs] = await Promise.all([
activeIds.length ? prisma.rankData.findMany({ where: { generalId: { in: activeIds } } }) : [],
activeIds.length
? prisma.logEntry.findMany({
where: {
generalId: { in: activeIds },
scope: LogScope.GENERAL,
category: LogCategory.HISTORY,
category: { in: [LogCategory.HISTORY, LogCategory.BATTLE_BRIEF] },
},
orderBy: { id: 'desc' },
})
@@ -293,11 +300,19 @@ const cancelGameInTransaction = async (
ranksByGeneral.set(row.generalId, ranks);
}
const logsByGeneral = new Map<number, string[]>();
for (const row of resolvedHistoryLogs) {
const battleResultsByGeneral = new Map<number, string[]>();
for (const row of resolvedRecordLogs) {
if (row.generalId === null) continue;
const logs = logsByGeneral.get(row.generalId) ?? [];
const target =
row.category === LogCategory.HISTORY
? logsByGeneral
: row.category === LogCategory.BATTLE_BRIEF
? battleResultsByGeneral
: null;
if (!target) continue;
const logs = target.get(row.generalId) ?? [];
logs.push(row.text);
logsByGeneral.set(row.generalId, logs);
target.set(row.generalId, logs);
}
const participantUsers = new Set<string>();
@@ -464,7 +479,9 @@ const cancelGameInTransaction = async (
turnTime: general.turnTime,
data: buildActiveGeneralArchive(
general as ActiveGeneral,
ranksByGeneral.get(general.id) ?? {},
logsByGeneral.get(general.id) ?? [],
battleResultsByGeneral.get(general.id) ?? [],
abandonment
),
},
@@ -477,7 +494,9 @@ const cancelGameInTransaction = async (
turnTime: general.turnTime,
data: buildActiveGeneralArchive(
general as ActiveGeneral,
ranksByGeneral.get(general.id) ?? {},
logsByGeneral.get(general.id) ?? [],
battleResultsByGeneral.get(general.id) ?? [],
abandonment
),
},
@@ -286,24 +286,37 @@ const archiveDeletedGeneral = async (
): Promise<void> => {
const serverId =
typeof worldMeta.serverId === 'string' && worldMeta.serverId.trim() ? worldMeta.serverId.trim() : 'default';
const history = await prisma.logEntry.findMany({
where: {
generalId: event.generalId,
scope: LogScope.GENERAL,
category: LogCategory.HISTORY,
},
orderBy: { id: 'desc' },
select: { text: true },
});
const archivedMeta = { ...asRecord(event.before.meta) };
const [recordRows, rankRows] = await Promise.all([
prisma.logEntry.findMany({
where: {
generalId: event.generalId,
scope: LogScope.GENERAL,
category: { in: [LogCategory.HISTORY, LogCategory.BATTLE_BRIEF] },
},
orderBy: { id: 'desc' },
select: { category: true, text: true },
}),
prisma.rankData.findMany({
where: { generalId: event.generalId },
select: { type: true, value: true },
}),
]);
const archivedMeta = {
...asRecord(event.before.meta),
...Object.fromEntries(rankRows.map((row) => [`rank_${row.type}`, row.value])),
};
delete archivedMeta.inheritRandomUnique;
delete archivedMeta.inheritSpecificSpecialWar;
const history = recordRows.filter((row) => row.category === LogCategory.HISTORY).map((row) => row.text);
const battleResults = recordRows.filter((row) => row.category === LogCategory.BATTLE_BRIEF).map((row) => row.text);
const data = {
...event.before,
meta: archivedMeta,
turnTime: event.before.turnTime.toISOString(),
recentWarTime: event.before.recentWarTime?.toISOString() ?? null,
history: history.map((entry) => entry.text),
history,
records: { battleResult: battleResults },
availability: { battleResultLogs: true },
};
await prisma.oldGeneral.upsert({
where: { by_no: { serverId, generalNo: event.generalId } },
@@ -531,29 +531,44 @@ export const persistUnificationFinalization = async (
});
const archiveGenerals = [...neutralGenerals, ...winnerGenerals];
const generalHistoryRows = archiveGenerals.length
const generalRecordRows = archiveGenerals.length
? await transaction.logEntry.findMany({
where: {
generalId: { in: archiveGenerals.map((general) => general.id) },
scope: LogScope.GENERAL,
category: LogCategory.HISTORY,
category: { in: [LogCategory.HISTORY, LogCategory.BATTLE_BRIEF] },
},
orderBy: { id: 'asc' },
select: { generalId: true, text: true },
select: { generalId: true, category: true, text: true },
})
: [];
const historyByGeneral = new Map<number, string[]>();
for (const row of generalHistoryRows) {
const battleResultsByGeneral = new Map<number, string[]>();
for (const row of generalRecordRows) {
if (row.generalId === null) continue;
const history = historyByGeneral.get(row.generalId) ?? [];
history.push(row.text);
historyByGeneral.set(row.generalId, history);
const target =
row.category === LogCategory.HISTORY
? historyByGeneral
: row.category === LogCategory.BATTLE_BRIEF
? battleResultsByGeneral
: null;
if (!target) continue;
const records = target.get(row.generalId) ?? [];
records.push(row.text);
target.set(row.generalId, records);
}
for (const general of archiveGenerals) {
const ranks = ranksByGeneral.get(general.id) ?? {};
const data = {
...general,
meta: {
...asRecord(general.meta),
...Object.fromEntries(Object.entries(ranks).map(([key, value]) => [`rank_${key}`, value])),
},
turnTime: general.turnTime.toISOString(),
history: historyByGeneral.get(general.id) ?? [],
records: { battleResult: [...(battleResultsByGeneral.get(general.id) ?? [])].reverse() },
availability: { battleResultLogs: true },
generationKey: input.generationKey,
};
await transaction.oldGeneral.upsert({