refactor: centralize domain values and enforce numeric lint
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
import { BankOperationKind } from '../domain/bank-operation-kind';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { Prisma } from '../../../../../generated/prisma/client';
|
||||
import { PrismaService } from '../../../../prisma/prisma.service';
|
||||
|
||||
import { conflict, missing } from '../application/bank-card-input';
|
||||
import {
|
||||
BankErrorCode,
|
||||
conflict,
|
||||
missing,
|
||||
} from '../application/bank-card-input';
|
||||
type Tx = Prisma.TransactionClient;
|
||||
const TRANSACTION_MAX_WAIT_MS = 10_000;
|
||||
const TRANSACTION_TIMEOUT_MS = 15_000;
|
||||
const MAX_WRITE_RETRY_COUNT = 2;
|
||||
const seeds = [
|
||||
'Продукты',
|
||||
'Кафе',
|
||||
@@ -23,8 +31,8 @@ export class BankCardsRepository {
|
||||
for (let attempt = 0; ; attempt++) {
|
||||
try {
|
||||
return await this.prisma.$transaction(run, {
|
||||
maxWait: 10000,
|
||||
timeout: 15000,
|
||||
maxWait: TRANSACTION_MAX_WAIT_MS,
|
||||
timeout: TRANSACTION_TIMEOUT_MS,
|
||||
});
|
||||
} catch (error) {
|
||||
const code =
|
||||
@@ -36,9 +44,10 @@ export class BankCardsRepository {
|
||||
code === 'P2028' ||
|
||||
(error instanceof Error &&
|
||||
/SQLITE_BUSY|database is locked/.test(error.message));
|
||||
if ((busy || code === 'P2002') && attempt < 2) continue;
|
||||
if (busy) conflict('BANK_WRITE_CONFLICT');
|
||||
if (code === 'P2002') conflict('BANK_DUPLICATE_CATEGORY');
|
||||
if ((busy || code === 'P2002') && attempt < MAX_WRITE_RETRY_COUNT)
|
||||
continue;
|
||||
if (busy) conflict(BankErrorCode.WriteConflict);
|
||||
if (code === 'P2002') conflict(BankErrorCode.DuplicateCategory);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +55,7 @@ export class BankCardsRepository {
|
||||
async card(tx: Tx, userId: string, id: string, active = false) {
|
||||
const card = await tx.bankCard.findFirst({ where: { id, userId } });
|
||||
if (!card) return missing();
|
||||
if (active && card.archivedAt) conflict('BANK_CARD_ARCHIVED');
|
||||
if (active && card.archivedAt) conflict(BankErrorCode.CardArchived);
|
||||
return card;
|
||||
}
|
||||
async operation(tx: Tx, userId: string, id: string) {
|
||||
@@ -56,7 +65,7 @@ export class BankCardsRepository {
|
||||
}
|
||||
async seed(tx: Tx, userId: string) {
|
||||
for (const [index, label] of seeds.entries()) {
|
||||
const seedKey = index === 8 ? 'other' : String(index);
|
||||
const seedKey = index === seeds.length - 1 ? 'other' : String(index);
|
||||
if (
|
||||
!(await tx.expenseCategory.findUnique({
|
||||
where: { userId_seedKey: { userId, seedKey } },
|
||||
@@ -85,7 +94,7 @@ export class BankCardsRepository {
|
||||
userId,
|
||||
deletedAt: null,
|
||||
date: { gte: period.from, lte: period.to },
|
||||
kind: { in: ['expense', 'refund'] },
|
||||
kind: { in: [BankOperationKind.Expense, BankOperationKind.Refund] },
|
||||
...(cardId ? { cardId } : {}),
|
||||
},
|
||||
include: { originalExpense: { select: { categoryId: true } } },
|
||||
|
||||
Reference in New Issue
Block a user