feat: 이전 서버 기록 조회 화면을 통합
지난 플레이를 현재 장수 카드와 전투·숙련도·기록 컴포넌트로 표시하고, 중앙 archive의 소유자 기반 조회를 연결한다. 명예의 전당과 왕조에 현재/이전 서버 source 분리를 추가한다.
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { isLegacyArchiveProfile, LEGACY_ARCHIVE_PROFILES, type LegacyArchiveProfile } from '@sammo-ts/common';
|
||||
|
||||
import type { DatabaseClient } from '../context.js';
|
||||
|
||||
export { isLegacyArchiveProfile, LEGACY_ARCHIVE_PROFILES, type LegacyArchiveProfile };
|
||||
|
||||
export type LegacyArchiveDatabase = Pick<DatabaseClient, '$queryRaw'>;
|
||||
|
||||
export interface LegacyGameHistoryRow {
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
serverId: string;
|
||||
legacyId: number;
|
||||
openedAt: Date;
|
||||
completedAt: Date | null;
|
||||
legacyDate: Date;
|
||||
winnerNation: number | null;
|
||||
map: string | null;
|
||||
season: number;
|
||||
scenario: number;
|
||||
scenarioName: string;
|
||||
rawEnv: unknown;
|
||||
}
|
||||
|
||||
export interface LegacyGeneralRow {
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
serverId: string;
|
||||
generalNo: number;
|
||||
legacyId: number;
|
||||
owner: string | null;
|
||||
name: string;
|
||||
lastYearMonth: number;
|
||||
turnTime: Date;
|
||||
schemaVersion: number;
|
||||
sourceFormat: string;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export interface LegacyNationRow {
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
legacyId: number;
|
||||
serverId: string;
|
||||
nation: number;
|
||||
data: unknown;
|
||||
archivedAt: Date;
|
||||
}
|
||||
|
||||
export interface LegacyEmperorRow {
|
||||
id: bigint;
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
legacyId: number;
|
||||
serverId: string | null;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export interface LegacyHallOptionRow {
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
season: number;
|
||||
scenario: number;
|
||||
scenarioName: string;
|
||||
count: bigint;
|
||||
}
|
||||
|
||||
export interface LegacyHallRow {
|
||||
sourceProfile: LegacyArchiveProfile;
|
||||
serverId: string;
|
||||
generalNo: number;
|
||||
type: string;
|
||||
value: number;
|
||||
owner: string | null;
|
||||
aux: unknown;
|
||||
}
|
||||
|
||||
export const findLegacyGeneralsByOwner = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
owner: string
|
||||
): Promise<LegacyGeneralRow[]> =>
|
||||
db.$queryRaw<LegacyGeneralRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"server_id" AS "serverId",
|
||||
"general_no" AS "generalNo",
|
||||
"legacy_id" AS "legacyId",
|
||||
"owner",
|
||||
"name",
|
||||
"last_yearmonth" AS "lastYearMonth",
|
||||
"turntime" AS "turnTime",
|
||||
"schema_version" AS "schemaVersion",
|
||||
"source_format" AS "sourceFormat",
|
||||
"data"
|
||||
FROM "legacy_archive"."general"
|
||||
WHERE "owner" = ${owner}
|
||||
ORDER BY "last_yearmonth" DESC, "source_profile", "server_id" DESC, "general_no"
|
||||
`);
|
||||
|
||||
export const findLegacyGeneral = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
input: { owner: string; sourceProfile: LegacyArchiveProfile; serverId: string; generalNo: number }
|
||||
): Promise<LegacyGeneralRow | null> => {
|
||||
const rows = await db.$queryRaw<LegacyGeneralRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"server_id" AS "serverId",
|
||||
"general_no" AS "generalNo",
|
||||
"legacy_id" AS "legacyId",
|
||||
"owner",
|
||||
"name",
|
||||
"last_yearmonth" AS "lastYearMonth",
|
||||
"turntime" AS "turnTime",
|
||||
"schema_version" AS "schemaVersion",
|
||||
"source_format" AS "sourceFormat",
|
||||
"data"
|
||||
FROM "legacy_archive"."general"
|
||||
WHERE "owner" = ${input.owner}
|
||||
AND "source_profile" = ${input.sourceProfile}
|
||||
AND "server_id" = ${input.serverId}
|
||||
AND "general_no" = ${input.generalNo}
|
||||
LIMIT 1
|
||||
`);
|
||||
return rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const findLegacyGeneralsForServer = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
input: { sourceProfile: LegacyArchiveProfile; serverId: string; generalNos: number[] }
|
||||
): Promise<Array<Pick<LegacyGeneralRow, 'generalNo' | 'name' | 'lastYearMonth'>>> => {
|
||||
if (input.generalNos.length === 0) return [];
|
||||
return db.$queryRaw<Array<Pick<LegacyGeneralRow, 'generalNo' | 'name' | 'lastYearMonth'>>>(GamePrisma.sql`
|
||||
SELECT
|
||||
"general_no" AS "generalNo",
|
||||
"name",
|
||||
"last_yearmonth" AS "lastYearMonth"
|
||||
FROM "legacy_archive"."general"
|
||||
WHERE "source_profile" = ${input.sourceProfile}
|
||||
AND "server_id" = ${input.serverId}
|
||||
AND "general_no" IN (${GamePrisma.join(input.generalNos)})
|
||||
`);
|
||||
};
|
||||
|
||||
export const findLegacyGames = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
keys?: Array<{ sourceProfile: LegacyArchiveProfile; serverId: string }>
|
||||
): Promise<LegacyGameHistoryRow[]> => {
|
||||
if (keys && keys.length === 0) return [];
|
||||
const condition = keys
|
||||
? GamePrisma.sql`WHERE ("source_profile", "server_id") IN (${GamePrisma.join(
|
||||
keys.map(({ sourceProfile, serverId }) => GamePrisma.sql`(${sourceProfile}, ${serverId})`)
|
||||
)})`
|
||||
: GamePrisma.empty;
|
||||
return db.$queryRaw<LegacyGameHistoryRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"server_id" AS "serverId",
|
||||
"legacy_id" AS "legacyId",
|
||||
"opened_at" AS "openedAt",
|
||||
"completed_at" AS "completedAt",
|
||||
"legacy_date" AS "legacyDate",
|
||||
"winner_nation" AS "winnerNation",
|
||||
"map",
|
||||
"season",
|
||||
"scenario",
|
||||
"scenario_name" AS "scenarioName",
|
||||
"raw_env" AS "rawEnv"
|
||||
FROM "legacy_archive"."game_history"
|
||||
${condition}
|
||||
`);
|
||||
};
|
||||
|
||||
export const findLegacyNations = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
keys: Array<{ sourceProfile: LegacyArchiveProfile; serverId: string }>
|
||||
): Promise<LegacyNationRow[]> => {
|
||||
if (keys.length === 0) return [];
|
||||
return db.$queryRaw<LegacyNationRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"legacy_id" AS "legacyId",
|
||||
"server_id" AS "serverId",
|
||||
"nation",
|
||||
"data",
|
||||
"archived_at" AS "archivedAt"
|
||||
FROM "legacy_archive"."nation"
|
||||
WHERE ("source_profile", "server_id") IN (${GamePrisma.join(
|
||||
keys.map(({ sourceProfile, serverId }) => GamePrisma.sql`(${sourceProfile}, ${serverId})`)
|
||||
)})
|
||||
ORDER BY "archived_at" DESC, "legacy_id" DESC
|
||||
`);
|
||||
};
|
||||
|
||||
export const findLegacyEmperors = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
keys?: Array<{ sourceProfile: LegacyArchiveProfile; serverId: string }>
|
||||
): Promise<LegacyEmperorRow[]> => {
|
||||
if (keys && keys.length === 0) return [];
|
||||
const condition = keys
|
||||
? GamePrisma.sql`WHERE ("source_profile", "server_id") IN (${GamePrisma.join(
|
||||
keys.map(({ sourceProfile, serverId }) => GamePrisma.sql`(${sourceProfile}, ${serverId})`)
|
||||
)})`
|
||||
: GamePrisma.empty;
|
||||
return db.$queryRaw<LegacyEmperorRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"id",
|
||||
"source_profile" AS "sourceProfile",
|
||||
"legacy_id" AS "legacyId",
|
||||
"server_id" AS "serverId",
|
||||
"data"
|
||||
FROM "legacy_archive"."emperor"
|
||||
${condition}
|
||||
ORDER BY "id" DESC
|
||||
`);
|
||||
};
|
||||
|
||||
export const findLegacyEmperor = async (db: LegacyArchiveDatabase, id: number): Promise<LegacyEmperorRow | null> => {
|
||||
const rows = await db.$queryRaw<LegacyEmperorRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"id",
|
||||
"source_profile" AS "sourceProfile",
|
||||
"legacy_id" AS "legacyId",
|
||||
"server_id" AS "serverId",
|
||||
"data"
|
||||
FROM "legacy_archive"."emperor"
|
||||
WHERE "id" = ${id}
|
||||
LIMIT 1
|
||||
`);
|
||||
return rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const findLegacyHallOptions = async (db: LegacyArchiveDatabase): Promise<LegacyHallOptionRow[]> =>
|
||||
db.$queryRaw<LegacyHallOptionRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"season",
|
||||
"scenario",
|
||||
MAX("scenario_name") AS "scenarioName",
|
||||
COUNT(*)::bigint AS "count"
|
||||
FROM "legacy_archive"."game_history"
|
||||
GROUP BY "source_profile", "season", "scenario"
|
||||
ORDER BY "season" DESC, "source_profile", "scenario"
|
||||
`);
|
||||
|
||||
export const findLegacyHallRows = async (
|
||||
db: LegacyArchiveDatabase,
|
||||
input: { sourceProfile: LegacyArchiveProfile; season: number; scenario?: number; type: string; take: number }
|
||||
): Promise<LegacyHallRow[]> => {
|
||||
const scenarioCondition =
|
||||
input.scenario === undefined ? GamePrisma.empty : GamePrisma.sql`AND "scenario" = ${input.scenario}`;
|
||||
return db.$queryRaw<LegacyHallRow[]>(GamePrisma.sql`
|
||||
SELECT
|
||||
"source_profile" AS "sourceProfile",
|
||||
"server_id" AS "serverId",
|
||||
"general_no" AS "generalNo",
|
||||
"type",
|
||||
"value",
|
||||
"owner",
|
||||
"aux"
|
||||
FROM "legacy_archive"."hall"
|
||||
WHERE "source_profile" = ${input.sourceProfile}
|
||||
AND "season" = ${input.season}
|
||||
${scenarioCondition}
|
||||
AND "type" = ${input.type}
|
||||
ORDER BY "value" DESC
|
||||
LIMIT ${input.take}
|
||||
`);
|
||||
};
|
||||
Reference in New Issue
Block a user