From 5cd56b239a940e39f1d289ffa1fe859c5483dcfb Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 24 Jun 2026 09:41:57 +0300 Subject: [PATCH] feat: wire up broker analytics endpoint, cache config, and module registration - Add analytics cache key to TBANK_CACHE_KEYS - Add tbankAnalyticsTtl to cache configuration - Register and export BrokerAnalyticsService in TBankModule - Add BrokerAnalyticsEnvelopeDto to broker envelope DTOs - Add GET /accounts/:accountId/analytics endpoint to TBankController - Fix TBankController spec to pass new constructor dependency --- apps/backend/src/config/configuration.ts | 1 + .../src/modules/tbank/dto/broker-envelope.dto.ts | 9 +++++++++ apps/backend/src/modules/tbank/tbank.config.ts | 1 + .../src/modules/tbank/tbank.controller.spec.ts | 8 +++++--- apps/backend/src/modules/tbank/tbank.controller.ts | 11 +++++++++++ apps/backend/src/modules/tbank/tbank.module.ts | 2 ++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/config/configuration.ts b/apps/backend/src/config/configuration.ts index d51e259..9eaf969 100644 --- a/apps/backend/src/config/configuration.ts +++ b/apps/backend/src/config/configuration.ts @@ -35,6 +35,7 @@ export default registerAs('app', () => ({ tbankOperationsTtl: parseInt(process.env.CACHE_TBANK_OPERATIONS_TTL || '300', 10), tbankPositionsTtl: parseInt(process.env.CACHE_TBANK_POSITIONS_TTL || '60', 10), tbankInstrumentTtl: parseInt(process.env.CACHE_TBANK_INSTRUMENT_TTL || '86400', 10), + tbankAnalyticsTtl: parseInt(process.env.CACHE_TBANK_ANALYTICS_TTL || '300', 10), }, auth: { jwtSecret: process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production', diff --git a/apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts b/apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts index 8e8da50..50f047a 100644 --- a/apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts +++ b/apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts @@ -5,6 +5,7 @@ import { BrokerOperationSyncResponseDto } from './broker-operation-sync-query.dt import { BrokerOperationsPageResponseDto } from './broker-operation-response.dto'; import { BrokerPositionsPageResponseDto } from './broker-positions-page-response.dto'; import { BrokerPortfolioResponseDto } from './broker-portfolio-response.dto'; +import { BrokerAnalyticsDto } from './broker-analytics-response.dto'; export class BrokerResponseMetaDto { @ApiProperty({ nullable: true }) @@ -54,6 +55,14 @@ export class BrokerOperationSyncEnvelopeDto { meta!: BrokerResponseMetaDto; } +export class BrokerAnalyticsEnvelopeDto { + @ApiProperty({ type: BrokerAnalyticsDto }) + data!: BrokerAnalyticsDto; + + @ApiProperty({ type: BrokerResponseMetaDto }) + meta!: BrokerResponseMetaDto; +} + export class BrokerEventsEnvelopeDto { @ApiProperty({ type: BrokerEventsDataDto }) data!: BrokerEventsDataDto; diff --git a/apps/backend/src/modules/tbank/tbank.config.ts b/apps/backend/src/modules/tbank/tbank.config.ts index 902fa4f..944712f 100644 --- a/apps/backend/src/modules/tbank/tbank.config.ts +++ b/apps/backend/src/modules/tbank/tbank.config.ts @@ -20,4 +20,5 @@ export const TBANK_CACHE_KEYS = { operations: 'tbank:operations', instrument: 'tbank:instrument', events: 'tbank:events', + analytics: 'tbank:analytics', } as const; diff --git a/apps/backend/src/modules/tbank/tbank.controller.spec.ts b/apps/backend/src/modules/tbank/tbank.controller.spec.ts index c9cc156..8520b10 100644 --- a/apps/backend/src/modules/tbank/tbank.controller.spec.ts +++ b/apps/backend/src/modules/tbank/tbank.controller.spec.ts @@ -2,6 +2,7 @@ import { ROLES_KEY } from '../auth/decorators/roles.decorator'; import { ApiResponse } from '../../common/dto/api-response.dto'; import { TBankController } from './tbank.controller'; import { BrokerAccountsService } from './services/broker-accounts.service'; +import { BrokerAnalyticsService } from './services/broker-analytics.service'; import { BrokerEventsService } from './services/broker-events.service'; import { BrokerOperationSyncService } from './services/broker-operation-sync.service'; import { BrokerOperationsService } from './services/broker-operations.service'; @@ -9,6 +10,7 @@ import { BrokerPortfolioService } from './services/broker-portfolio.service'; describe('TBankController', () => { const accounts = { findAll: vi.fn() } as unknown as BrokerAccountsService; + const analytics = { getAnalytics: vi.fn() } as unknown as BrokerAnalyticsService; const portfolio = { getPortfolio: vi.fn() } as unknown as BrokerPortfolioService; const events = { getEvents: vi.fn() } as unknown as BrokerEventsService; const operations = { getOperations: vi.fn() } as unknown as BrokerOperationsService; @@ -37,7 +39,7 @@ describe('TBankController', () => { meta: { fromCache: true, cachedAt: '2026-06-17T00:00:00.000Z' }, }); - const controller = new TBankController(accounts, portfolio, events, operations, sync); + const controller = new TBankController(accounts, portfolio, events, operations, sync, analytics); const response = await controller.getAccounts(); expect(response).toBeInstanceOf(ApiResponse); @@ -48,7 +50,7 @@ describe('TBankController', () => { it('exposes a sync trigger for durable operation history', async () => { vi.mocked(sync.syncAccount).mockResolvedValueOnce({ upserted: 2 }); - const controller = new TBankController(accounts, portfolio, events, operations, sync); + const controller = new TBankController(accounts, portfolio, events, operations, sync, analytics); const response = await controller.syncOperations('acc-1', { from: '2026-06-01T00:00:00.000Z', to: '2026-06-17T00:00:00.000Z', @@ -84,7 +86,7 @@ describe('TBankController', () => { meta: { fromCache: false, cachedAt: '2026-06-22T00:00:00.000Z' }, }); - const controller = new TBankController(accounts, portfolio, events, operations, sync); + const controller = new TBankController(accounts, portfolio, events, operations, sync, analytics); const query = { from: '2026-06-22', to: '2026-07-29', types: 'dividend,coupon' }; const response = await controller.getEvents('acc-1', query); diff --git a/apps/backend/src/modules/tbank/tbank.controller.ts b/apps/backend/src/modules/tbank/tbank.controller.ts index 6d52de7..95526a0 100644 --- a/apps/backend/src/modules/tbank/tbank.controller.ts +++ b/apps/backend/src/modules/tbank/tbank.controller.ts @@ -4,6 +4,7 @@ import { ApiResponse } from '../../common/dto/api-response.dto'; import { Roles } from '../auth/decorators/roles.decorator'; import { BrokerAccountsEnvelopeDto, + BrokerAnalyticsEnvelopeDto, BrokerEventsEnvelopeDto, BrokerOperationSyncEnvelopeDto, BrokerOperationsEnvelopeDto, @@ -18,6 +19,7 @@ import { BrokerAccountsService } from './services/broker-accounts.service'; import { BrokerEventsService } from './services/broker-events.service'; import { BrokerOperationSyncService } from './services/broker-operation-sync.service'; import { BrokerOperationsService } from './services/broker-operations.service'; +import { BrokerAnalyticsService } from './services/broker-analytics.service'; import { BrokerPortfolioService } from './services/broker-portfolio.service'; @ApiTags('Broker') @@ -31,6 +33,7 @@ export class TBankController { private readonly brokerEventsService: BrokerEventsService, private readonly brokerOperationsService: BrokerOperationsService, private readonly brokerOperationSyncService: BrokerOperationSyncService, + private readonly brokerAnalyticsService: BrokerAnalyticsService, ) {} @Get('accounts') @@ -84,6 +87,14 @@ export class TBankController { return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt); } + @Get('accounts/:accountId/analytics') + @ApiOperation({ summary: 'Get broker account profitability analytics' }) + @ApiOkResponse({ type: BrokerAnalyticsEnvelopeDto }) + async getAnalytics(@Param('accountId') accountId: string) { + const result = await this.brokerAnalyticsService.getAnalytics(accountId); + return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt); + } + @Post('accounts/:accountId/operations/sync') @ApiOperation({ summary: 'Synchronize T-Bank broker account operations into local history' }) @ApiOkResponse({ type: BrokerOperationSyncEnvelopeDto }) diff --git a/apps/backend/src/modules/tbank/tbank.module.ts b/apps/backend/src/modules/tbank/tbank.module.ts index 92dd7f7..918bfe2 100644 --- a/apps/backend/src/modules/tbank/tbank.module.ts +++ b/apps/backend/src/modules/tbank/tbank.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { MoexClientModule } from '../moex-client/moex-client.module'; import { TBankController } from './tbank.controller'; import { BrokerAccountsService } from './services/broker-accounts.service'; +import { BrokerAnalyticsService } from './services/broker-analytics.service'; import { BrokerInstrumentsService } from './services/broker-instruments.service'; import { BrokerEventsService } from './services/broker-events.service'; import { BrokerOperationSyncService } from './services/broker-operation-sync.service'; @@ -20,6 +21,7 @@ import { TBankClientService } from './services/tbank-client.service'; BrokerEventsService, BrokerOperationsService, BrokerOperationSyncService, + BrokerAnalyticsService, ], exports: [ TBankClientService,