refactor: centralize domain values and enforce numeric lint

This commit is contained in:
2026-09-13 14:37:39 +03:00
parent 8ba005346d
commit 8637ad2f34
44 changed files with 543 additions and 174 deletions
@@ -1,3 +1,4 @@
import { MILLISECONDS_PER_SECOND } from '../../../../common/time';
import {
BadRequestException,
Injectable,
@@ -15,6 +16,7 @@ import { AccountReferenceDto } from '../../account-reference.dto';
import { DateOnlyRange, dateOnlyBounds } from '../../investment-calendar';
import {
BrokerageOperationDto,
DEFAULT_OPERATIONS_PAGE_SIZE,
BrokerageOperationsPageDto,
BrokerageOperationsQueryDto,
} from '../presentation/brokerage-operation.dto';
@@ -23,6 +25,8 @@ import {
operationCategory,
} from '../infrastructure/brokerage-operation.mapper';
const CONTINUATION_TTL_MS = 300_000;
type OperationStream = {
account: AccountReferenceDto;
pending: BrokerageOperationDto[];
@@ -54,7 +58,7 @@ export class BrokerageOperationsService {
AllOperationsContinuation
>({
max: 100,
ttl: 5 * 60_000,
ttl: CONTINUATION_TTL_MS,
ttlAutopurge: true,
});
@@ -86,7 +90,10 @@ export class BrokerageOperationsService {
query.instrumentUid && !query.from
? {
...range,
from: new Date(Number(account.openedDate?.seconds ?? 0) * 1000),
from: new Date(
Number(account.openedDate?.seconds ?? 0) *
MILLISECONDS_PER_SECOND,
),
}
: range,
),
@@ -108,7 +115,7 @@ export class BrokerageOperationsService {
from: query.from ?? null,
to: query.to ?? null,
categories: [...new Set(query.categories ?? [])].sort(),
limit: query.limit ?? 100,
limit: query.limit ?? DEFAULT_OPERATIONS_PAGE_SIZE,
});
const continuation = query.cursor
? this.continuations.get(query.cursor)
@@ -178,7 +185,7 @@ export class BrokerageOperationsService {
): Promise<BrokerageOperationsPageDto> {
const items: BrokerageOperationDto[] = [];
// Each stream retains unconsumed rows. A page boundary never discards them.
while (items.length < (query.limit ?? 100)) {
while (items.length < (query.limit ?? DEFAULT_OPERATIONS_PAGE_SIZE)) {
for (const stream of streams) {
while (!stream.pending.length && !stream.finished) {
const page = await this.page(
@@ -324,7 +331,9 @@ export class BrokerageOperationsService {
accountId,
async ({ token, generation, account }) => {
const accountOpenedAt = account.openedDate?.seconds
? new Date(Number(account.openedDate.seconds) * 1000)
? new Date(
Number(account.openedDate.seconds) * MILLISECONDS_PER_SECOND,
)
: undefined;
const selected =
typeof ranges === 'function' ? ranges(accountOpenedAt) : ranges;
@@ -395,7 +404,7 @@ export class BrokerageOperationsService {
from: seconds(range.from),
to: seconds(range.to, /^\d{4}-\d{2}-\d{2}$/.test(query.to ?? '')),
cursor: query.cursor,
limit: query.limit ?? 100,
limit: query.limit ?? DEFAULT_OPERATIONS_PAGE_SIZE,
state: 'OPERATION_STATE_EXECUTED',
...(query.instrumentUid ? { instrumentId: query.instrumentUid } : {}),
});
@@ -480,7 +489,7 @@ function cacheKey(
categories,
instrumentUid: query.instrumentUid ?? null,
cursor: query.cursor ?? null,
limit: query.limit ?? 100,
limit: query.limit ?? DEFAULT_OPERATIONS_PAGE_SIZE,
});
}
@@ -493,7 +502,7 @@ function dateBoundary(value: string | undefined, end: boolean): Date | null {
function seconds(value: Date, endOfSecond = false) {
return {
seconds: Math.floor(value.valueOf() / 1000).toString(),
seconds: Math.floor(value.valueOf() / MILLISECONDS_PER_SECOND).toString(),
...(endOfSecond ? { nanos: 999_999_999 } : {}),
};
}