import { Injectable } from '@nestjs/common'; import type { MoexIndexClose } from '#src/integrations/moex/moex.client'; import { PrismaService } from '#src/prisma/prisma.service'; @Injectable() export class MarketIndexRepository { constructor(private readonly prisma: PrismaService) {} async read(secid: 'IMOEX' | 'MCFTRR', from: string, to: string) { const [closes, state] = await Promise.all([ this.prisma.marketIndexDailyClose.findMany({ where: { secid, tradeDate: { gte: from, lte: to } }, orderBy: { tradeDate: 'asc' }, }), this.prisma.marketIndexSyncState.findUnique({ where: { secid } }), ]); return { closes, state }; } async attempt(secid: 'IMOEX' | 'MCFTRR', at: Date) { await this.prisma.marketIndexSyncState.upsert({ where: { secid }, create: { secid, lastAttemptAt: at }, update: { lastAttemptAt: at }, }); } // coveredTo is the `till` MOEX answered for: a close for today that is not // published yet is re-checked by the service while the last close < to. async save(secid: 'IMOEX' | 'MCFTRR', closes: MoexIndexClose[], from: string, to: string, at: Date) { await this.prisma.$transaction(async (tx) => { for (const close of closes) { await tx.marketIndexDailyClose.upsert({ where: { secid_tradeDate: { secid, tradeDate: close.tradeDate } }, create: { secid, ...close }, update: { close: close.close }, }); } const state = await tx.marketIndexSyncState.findUniqueOrThrow({ where: { secid } }); const joined = Boolean(state.coveredFrom && state.coveredTo && from <= state.coveredTo && to >= state.coveredFrom); await tx.marketIndexSyncState.update({ where: { secid }, data: { lastSuccessAt: at, coveredFrom: joined && state.coveredFrom && state.coveredFrom < from ? state.coveredFrom : from, coveredTo: joined && state.coveredTo && state.coveredTo > to ? state.coveredTo : to, }, }); }); } }