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
This commit is contained in:
parent
5184d34262
commit
5cd56b239a
@ -35,6 +35,7 @@ export default registerAs('app', () => ({
|
|||||||
tbankOperationsTtl: parseInt(process.env.CACHE_TBANK_OPERATIONS_TTL || '300', 10),
|
tbankOperationsTtl: parseInt(process.env.CACHE_TBANK_OPERATIONS_TTL || '300', 10),
|
||||||
tbankPositionsTtl: parseInt(process.env.CACHE_TBANK_POSITIONS_TTL || '60', 10),
|
tbankPositionsTtl: parseInt(process.env.CACHE_TBANK_POSITIONS_TTL || '60', 10),
|
||||||
tbankInstrumentTtl: parseInt(process.env.CACHE_TBANK_INSTRUMENT_TTL || '86400', 10),
|
tbankInstrumentTtl: parseInt(process.env.CACHE_TBANK_INSTRUMENT_TTL || '86400', 10),
|
||||||
|
tbankAnalyticsTtl: parseInt(process.env.CACHE_TBANK_ANALYTICS_TTL || '300', 10),
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
jwtSecret: process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production',
|
jwtSecret: process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production',
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { BrokerOperationSyncResponseDto } from './broker-operation-sync-query.dt
|
|||||||
import { BrokerOperationsPageResponseDto } from './broker-operation-response.dto';
|
import { BrokerOperationsPageResponseDto } from './broker-operation-response.dto';
|
||||||
import { BrokerPositionsPageResponseDto } from './broker-positions-page-response.dto';
|
import { BrokerPositionsPageResponseDto } from './broker-positions-page-response.dto';
|
||||||
import { BrokerPortfolioResponseDto } from './broker-portfolio-response.dto';
|
import { BrokerPortfolioResponseDto } from './broker-portfolio-response.dto';
|
||||||
|
import { BrokerAnalyticsDto } from './broker-analytics-response.dto';
|
||||||
|
|
||||||
export class BrokerResponseMetaDto {
|
export class BrokerResponseMetaDto {
|
||||||
@ApiProperty({ nullable: true })
|
@ApiProperty({ nullable: true })
|
||||||
@ -54,6 +55,14 @@ export class BrokerOperationSyncEnvelopeDto {
|
|||||||
meta!: BrokerResponseMetaDto;
|
meta!: BrokerResponseMetaDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BrokerAnalyticsEnvelopeDto {
|
||||||
|
@ApiProperty({ type: BrokerAnalyticsDto })
|
||||||
|
data!: BrokerAnalyticsDto;
|
||||||
|
|
||||||
|
@ApiProperty({ type: BrokerResponseMetaDto })
|
||||||
|
meta!: BrokerResponseMetaDto;
|
||||||
|
}
|
||||||
|
|
||||||
export class BrokerEventsEnvelopeDto {
|
export class BrokerEventsEnvelopeDto {
|
||||||
@ApiProperty({ type: BrokerEventsDataDto })
|
@ApiProperty({ type: BrokerEventsDataDto })
|
||||||
data!: BrokerEventsDataDto;
|
data!: BrokerEventsDataDto;
|
||||||
|
|||||||
@ -20,4 +20,5 @@ export const TBANK_CACHE_KEYS = {
|
|||||||
operations: 'tbank:operations',
|
operations: 'tbank:operations',
|
||||||
instrument: 'tbank:instrument',
|
instrument: 'tbank:instrument',
|
||||||
events: 'tbank:events',
|
events: 'tbank:events',
|
||||||
|
analytics: 'tbank:analytics',
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ROLES_KEY } from '../auth/decorators/roles.decorator';
|
|||||||
import { ApiResponse } from '../../common/dto/api-response.dto';
|
import { ApiResponse } from '../../common/dto/api-response.dto';
|
||||||
import { TBankController } from './tbank.controller';
|
import { TBankController } from './tbank.controller';
|
||||||
import { BrokerAccountsService } from './services/broker-accounts.service';
|
import { BrokerAccountsService } from './services/broker-accounts.service';
|
||||||
|
import { BrokerAnalyticsService } from './services/broker-analytics.service';
|
||||||
import { BrokerEventsService } from './services/broker-events.service';
|
import { BrokerEventsService } from './services/broker-events.service';
|
||||||
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
||||||
import { BrokerOperationsService } from './services/broker-operations.service';
|
import { BrokerOperationsService } from './services/broker-operations.service';
|
||||||
@ -9,6 +10,7 @@ import { BrokerPortfolioService } from './services/broker-portfolio.service';
|
|||||||
|
|
||||||
describe('TBankController', () => {
|
describe('TBankController', () => {
|
||||||
const accounts = { findAll: vi.fn() } as unknown as BrokerAccountsService;
|
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 portfolio = { getPortfolio: vi.fn() } as unknown as BrokerPortfolioService;
|
||||||
const events = { getEvents: vi.fn() } as unknown as BrokerEventsService;
|
const events = { getEvents: vi.fn() } as unknown as BrokerEventsService;
|
||||||
const operations = { getOperations: vi.fn() } as unknown as BrokerOperationsService;
|
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' },
|
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();
|
const response = await controller.getAccounts();
|
||||||
|
|
||||||
expect(response).toBeInstanceOf(ApiResponse);
|
expect(response).toBeInstanceOf(ApiResponse);
|
||||||
@ -48,7 +50,7 @@ describe('TBankController', () => {
|
|||||||
it('exposes a sync trigger for durable operation history', async () => {
|
it('exposes a sync trigger for durable operation history', async () => {
|
||||||
vi.mocked(sync.syncAccount).mockResolvedValueOnce({ upserted: 2 });
|
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', {
|
const response = await controller.syncOperations('acc-1', {
|
||||||
from: '2026-06-01T00:00:00.000Z',
|
from: '2026-06-01T00:00:00.000Z',
|
||||||
to: '2026-06-17T00: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' },
|
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 query = { from: '2026-06-22', to: '2026-07-29', types: 'dividend,coupon' };
|
||||||
const response = await controller.getEvents('acc-1', query);
|
const response = await controller.getEvents('acc-1', query);
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { ApiResponse } from '../../common/dto/api-response.dto';
|
|||||||
import { Roles } from '../auth/decorators/roles.decorator';
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
import {
|
import {
|
||||||
BrokerAccountsEnvelopeDto,
|
BrokerAccountsEnvelopeDto,
|
||||||
|
BrokerAnalyticsEnvelopeDto,
|
||||||
BrokerEventsEnvelopeDto,
|
BrokerEventsEnvelopeDto,
|
||||||
BrokerOperationSyncEnvelopeDto,
|
BrokerOperationSyncEnvelopeDto,
|
||||||
BrokerOperationsEnvelopeDto,
|
BrokerOperationsEnvelopeDto,
|
||||||
@ -18,6 +19,7 @@ import { BrokerAccountsService } from './services/broker-accounts.service';
|
|||||||
import { BrokerEventsService } from './services/broker-events.service';
|
import { BrokerEventsService } from './services/broker-events.service';
|
||||||
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
||||||
import { BrokerOperationsService } from './services/broker-operations.service';
|
import { BrokerOperationsService } from './services/broker-operations.service';
|
||||||
|
import { BrokerAnalyticsService } from './services/broker-analytics.service';
|
||||||
import { BrokerPortfolioService } from './services/broker-portfolio.service';
|
import { BrokerPortfolioService } from './services/broker-portfolio.service';
|
||||||
|
|
||||||
@ApiTags('Broker')
|
@ApiTags('Broker')
|
||||||
@ -31,6 +33,7 @@ export class TBankController {
|
|||||||
private readonly brokerEventsService: BrokerEventsService,
|
private readonly brokerEventsService: BrokerEventsService,
|
||||||
private readonly brokerOperationsService: BrokerOperationsService,
|
private readonly brokerOperationsService: BrokerOperationsService,
|
||||||
private readonly brokerOperationSyncService: BrokerOperationSyncService,
|
private readonly brokerOperationSyncService: BrokerOperationSyncService,
|
||||||
|
private readonly brokerAnalyticsService: BrokerAnalyticsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get('accounts')
|
@Get('accounts')
|
||||||
@ -84,6 +87,14 @@ export class TBankController {
|
|||||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
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')
|
@Post('accounts/:accountId/operations/sync')
|
||||||
@ApiOperation({ summary: 'Synchronize T-Bank broker account operations into local history' })
|
@ApiOperation({ summary: 'Synchronize T-Bank broker account operations into local history' })
|
||||||
@ApiOkResponse({ type: BrokerOperationSyncEnvelopeDto })
|
@ApiOkResponse({ type: BrokerOperationSyncEnvelopeDto })
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
|||||||
import { MoexClientModule } from '../moex-client/moex-client.module';
|
import { MoexClientModule } from '../moex-client/moex-client.module';
|
||||||
import { TBankController } from './tbank.controller';
|
import { TBankController } from './tbank.controller';
|
||||||
import { BrokerAccountsService } from './services/broker-accounts.service';
|
import { BrokerAccountsService } from './services/broker-accounts.service';
|
||||||
|
import { BrokerAnalyticsService } from './services/broker-analytics.service';
|
||||||
import { BrokerInstrumentsService } from './services/broker-instruments.service';
|
import { BrokerInstrumentsService } from './services/broker-instruments.service';
|
||||||
import { BrokerEventsService } from './services/broker-events.service';
|
import { BrokerEventsService } from './services/broker-events.service';
|
||||||
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
import { BrokerOperationSyncService } from './services/broker-operation-sync.service';
|
||||||
@ -20,6 +21,7 @@ import { TBankClientService } from './services/tbank-client.service';
|
|||||||
BrokerEventsService,
|
BrokerEventsService,
|
||||||
BrokerOperationsService,
|
BrokerOperationsService,
|
||||||
BrokerOperationSyncService,
|
BrokerOperationSyncService,
|
||||||
|
BrokerAnalyticsService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
TBankClientService,
|
TBankClientService,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user