feat: 베팅 기능 추가 및 베팅 항목 저장 로직 구현

This commit is contained in:
2026-01-23 17:01:01 +00:00
parent 293a5035c6
commit e02e6c8fc4
2 changed files with 67 additions and 0 deletions
@@ -116,4 +116,64 @@ export const tournamentRouter = router({
await store.setBettingEntries(input);
return { ok: true, count: input.length };
}),
placeBet: authedProcedure
.input(
z.object({
targetId: z.number().int().positive(),
amount: z.number().int().positive(),
})
)
.mutation(async ({ ctx, input }) => {
const userId = ctx.auth?.user.id;
if (!userId) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
const store = new TournamentStore(ctx.redis, buildTournamentKeys(ctx.profile.name));
const state = await store.getState();
if (!state || state.stage !== 6) {
throw new TRPCError({ code: 'BAD_REQUEST', message: '베팅 기간이 아닙니다.' });
}
const closeAt = state.bettingCloseAt ? new Date(state.bettingCloseAt).getTime() : 0;
if (closeAt && closeAt <= Date.now()) {
throw new TRPCError({ code: 'BAD_REQUEST', message: '베팅이 마감되었습니다.' });
}
const matches = await store.getMatches();
const candidateIds = new Set<number>();
for (const match of matches) {
if (match.stage === 7) {
candidateIds.add(match.attackerId);
candidateIds.add(match.defenderId);
}
}
if (!candidateIds.has(input.targetId)) {
throw new TRPCError({ code: 'BAD_REQUEST', message: '올바르지 않은 베팅 대상입니다.' });
}
const general = await ctx.db.general.findFirst({
where: { userId },
select: { id: true, gold: true },
});
if (!general) {
throw new TRPCError({ code: 'PRECONDITION_FAILED', message: '장수가 존재하지 않습니다.' });
}
const minRemainGold = 500;
if (general.gold - input.amount < minRemainGold) {
throw new TRPCError({ code: 'BAD_REQUEST', message: '소지금이 부족합니다.' });
}
await ctx.db.general.update({
where: { id: general.id },
data: { gold: general.gold - input.amount },
});
await store.appendBettingEntry({
generalId: general.id,
targetId: input.targetId,
amount: input.amount,
});
return { ok: true };
}),
});
+7
View File
@@ -51,4 +51,11 @@ export class TournamentStore {
async setBettingEntries(entries: TournamentBetEntry[]): Promise<void> {
await this.redis.set(this.keys.bettingKey, JSON.stringify(entries));
}
async appendBettingEntry(entry: TournamentBetEntry): Promise<TournamentBetEntry[]> {
const entries = await this.getBettingEntries();
entries.push(entry);
await this.setBettingEntries(entries);
return entries;
}
}