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),
|
||||
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',
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -20,4 +20,5 @@ export const TBANK_CACHE_KEYS = {
|
||||
operations: 'tbank:operations',
|
||||
instrument: 'tbank:instrument',
|
||||
events: 'tbank:events',
|
||||
analytics: 'tbank:analytics',
|
||||
} as const;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 })
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user