feat: migrate services to ApiEnvelopePayload, controllers to plain returns
This commit is contained in:
parent
3a89cef76c
commit
b092caf8d6
@ -41,13 +41,7 @@ export class AuthController {
|
||||
async register(@Body() dto: RegisterDto, @Res({ passthrough: true }) res: Response) {
|
||||
const result = await this.authService.register(dto);
|
||||
res.cookie(REFRESH_COOKIE, result.refreshToken, COOKIE_OPTIONS);
|
||||
return {
|
||||
data: {
|
||||
user: result.user,
|
||||
accessToken: result.accessToken,
|
||||
},
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return { user: result.user, accessToken: result.accessToken };
|
||||
}
|
||||
|
||||
@Public()
|
||||
@ -57,13 +51,7 @@ export class AuthController {
|
||||
async login(@Body() dto: LoginDto, @Res({ passthrough: true }) res: Response) {
|
||||
const result = await this.authService.login(dto);
|
||||
res.cookie(REFRESH_COOKIE, result.refreshToken, COOKIE_OPTIONS);
|
||||
return {
|
||||
data: {
|
||||
user: result.user,
|
||||
accessToken: result.accessToken,
|
||||
},
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return { user: result.user, accessToken: result.accessToken };
|
||||
}
|
||||
|
||||
@Public()
|
||||
@ -75,13 +63,7 @@ export class AuthController {
|
||||
const token = req.cookies?.[REFRESH_COOKIE];
|
||||
const result = await this.authService.refresh(token);
|
||||
res.cookie(REFRESH_COOKIE, result.refreshToken, COOKIE_OPTIONS);
|
||||
return {
|
||||
data: {
|
||||
user: result.user,
|
||||
accessToken: result.accessToken,
|
||||
},
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return { user: result.user, accessToken: result.accessToken };
|
||||
}
|
||||
|
||||
@Post('logout')
|
||||
@ -92,10 +74,7 @@ export class AuthController {
|
||||
async logout(@CurrentUser() user: JwtPayload, @Res({ passthrough: true }) res: Response) {
|
||||
await this.authService.logout(user.sub);
|
||||
res.clearCookie(REFRESH_COOKIE, { path: '/api/v1/auth' });
|
||||
return {
|
||||
data: { message: 'Logged out successfully' },
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return { message: 'Logged out successfully' };
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
@ -103,11 +82,7 @@ export class AuthController {
|
||||
@ApiOperation({ summary: 'Get current user profile' })
|
||||
@ApiOkResponse({ type: AuthProfileResponseDto })
|
||||
async getProfile(@CurrentUser() user: JwtPayload) {
|
||||
const profile = await this.authService.getProfile(user.sub);
|
||||
return {
|
||||
data: profile,
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return this.authService.getProfile(user.sub);
|
||||
}
|
||||
|
||||
@Patch('me')
|
||||
@ -115,10 +90,6 @@ export class AuthController {
|
||||
@ApiOperation({ summary: 'Update current user profile' })
|
||||
@ApiOkResponse({ type: AuthProfileResponseDto })
|
||||
async updateProfile(@CurrentUser() user: JwtPayload, @Body() dto: UpdateProfileDto) {
|
||||
const profile = await this.authService.updateProfile(user.sub, dto);
|
||||
return {
|
||||
data: profile,
|
||||
meta: { fromCache: false, cachedAt: null },
|
||||
};
|
||||
return this.authService.updateProfile(user.sub, dto);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import { ApiEnvelopePayload } from '../../common/dto/api-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BondsService {
|
||||
@ -32,8 +33,8 @@ export class BondsService {
|
||||
'marketDataTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: {
|
||||
return new ApiEnvelopePayload(
|
||||
{
|
||||
secid: bond.secid,
|
||||
isin: bond.isin,
|
||||
name: bond.shortName,
|
||||
@ -70,8 +71,9 @@ export class BondsService {
|
||||
: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
|
||||
async getMarketData(secid: string) {
|
||||
@ -90,8 +92,8 @@ export class BondsService {
|
||||
throw new NotFoundException(`Market data for bond ${secid} not found`);
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
return new ApiEnvelopePayload(
|
||||
{
|
||||
price: mkt.last ?? 0,
|
||||
yieldToMaturity: mkt.yield ?? null,
|
||||
duration: mkt.duration ?? null,
|
||||
@ -107,8 +109,9 @@ export class BondsService {
|
||||
? new Date().toISOString().split('T')[0] + 'T' + mkt.updateTime
|
||||
: new Date().toISOString(),
|
||||
},
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
|
||||
async getHistory(secid: string, from: string, till: string) {
|
||||
@ -119,14 +122,15 @@ export class BondsService {
|
||||
'historyTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: data.map((h) => ({
|
||||
return new ApiEnvelopePayload(
|
||||
data.map((h) => ({
|
||||
date: h.tradeDate,
|
||||
closePrice: h.legalClosePrice ?? h.close ?? 0,
|
||||
yieldClose: h.yieldClose ?? null,
|
||||
duration: h.duration ?? null,
|
||||
})),
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import { CandleInterval } from './dto/candles-query.dto';
|
||||
import { ApiEnvelopePayload } from '../../common/dto/api-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CandlesService {
|
||||
@ -29,8 +30,8 @@ export class CandlesService {
|
||||
'candlesTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: data.map((c) => ({
|
||||
return new ApiEnvelopePayload(
|
||||
data.map((c) => ({
|
||||
open: c.open,
|
||||
high: c.high,
|
||||
low: c.low,
|
||||
@ -40,7 +41,8 @@ export class CandlesService {
|
||||
begin: c.begin,
|
||||
end: c.end,
|
||||
})),
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,12 +26,8 @@ import {
|
||||
const nullDataEnvelopeSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'null',
|
||||
},
|
||||
meta: {
|
||||
$ref: getSchemaPath(PortfolioResponseMetaDto),
|
||||
},
|
||||
data: { type: 'null' },
|
||||
meta: { $ref: getSchemaPath(PortfolioResponseMetaDto) },
|
||||
},
|
||||
required: ['data', 'meta'],
|
||||
};
|
||||
@ -47,24 +43,21 @@ export class PortfolioController {
|
||||
@ApiOperation({ summary: 'Get all portfolios for current user' })
|
||||
@ApiOkResponse({ type: PortfolioListEnvelopeDto })
|
||||
async findAll(@CurrentUser() user: { sub: number }) {
|
||||
const portfolios = await this.portfolioService.findAll(user.sub);
|
||||
return { data: portfolios, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.findAll(user.sub);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new portfolio' })
|
||||
@ApiCreatedResponse({ type: PortfolioEnvelopeDto })
|
||||
async create(@CurrentUser() user: { sub: number }, @Body() dto: CreatePortfolioDto) {
|
||||
const portfolio = await this.portfolioService.create(user.sub, dto);
|
||||
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.create(user.sub, dto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get portfolio details with positions and prices' })
|
||||
@ApiOkResponse({ type: PortfolioDetailEnvelopeDto })
|
||||
async findOne(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
||||
const portfolio = await this.portfolioService.findOne(user.sub, id);
|
||||
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.findOne(user.sub, id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ -75,8 +68,7 @@ export class PortfolioController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() dto: UpdatePortfolioDto,
|
||||
) {
|
||||
const portfolio = await this.portfolioService.update(user.sub, id, dto);
|
||||
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.update(user.sub, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ -84,7 +76,7 @@ export class PortfolioController {
|
||||
@ApiOkResponse({ schema: nullDataEnvelopeSchema })
|
||||
async remove(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
||||
await this.portfolioService.remove(user.sub, id);
|
||||
return { data: null, meta: { cachedAt: null, fromCache: false } };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Post(':id/positions')
|
||||
@ -95,8 +87,7 @@ export class PortfolioController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() dto: AddPositionDto,
|
||||
) {
|
||||
const position = await this.portfolioService.addPosition(user.sub, id, dto);
|
||||
return { data: position, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.addPosition(user.sub, id, dto);
|
||||
}
|
||||
|
||||
@Patch(':id/positions/:positionId')
|
||||
@ -108,16 +99,14 @@ export class PortfolioController {
|
||||
@Param('positionId', ParseIntPipe) positionId: number,
|
||||
@Body() dto: UpdatePositionDto,
|
||||
) {
|
||||
const position = await this.portfolioService.updatePosition(user.sub, id, positionId, dto);
|
||||
return { data: position, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.updatePosition(user.sub, id, positionId, dto);
|
||||
}
|
||||
|
||||
@Get(':id/analytics')
|
||||
@ApiOperation({ summary: 'Get portfolio analytics with PnL' })
|
||||
@ApiOkResponse({ type: AnalyticsEnvelopeDto })
|
||||
async getAnalytics(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
||||
const result = await this.portfolioService.getAnalytics(user.sub, id);
|
||||
return { data: result, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.portfolioService.getAnalytics(user.sub, id);
|
||||
}
|
||||
|
||||
@Delete(':id/positions/:positionId')
|
||||
@ -129,6 +118,6 @@ export class PortfolioController {
|
||||
@Param('positionId', ParseIntPipe) positionId: number,
|
||||
) {
|
||||
await this.portfolioService.removePosition(user.sub, id, positionId);
|
||||
return { data: null, meta: { cachedAt: null, fromCache: false } };
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,19 +21,17 @@ export class SecuritiesController {
|
||||
@ApiOperation({ summary: 'Поиск по инструментам' })
|
||||
@ApiOkResponse({ type: SearchEnvelopeDto })
|
||||
async search(@Query(ValidationPipe) query: SearchQueryDto) {
|
||||
const results = await this.securitiesService.search(
|
||||
return this.securitiesService.search(
|
||||
query.q,
|
||||
query.type || SecurityType.ALL,
|
||||
query.limit || 20,
|
||||
);
|
||||
return { data: results, meta: { cachedAt: null, fromCache: false } };
|
||||
}
|
||||
|
||||
@Get('screener')
|
||||
@ApiOperation({ summary: 'Фильтр ценных бумаг по параметрам' })
|
||||
@ApiOkResponse({ type: ScreenerResponseDto })
|
||||
async screener(@Query(ValidationPipe) query: ScreenerQueryDto) {
|
||||
const result = await this.screenerService.screen(query);
|
||||
return { data: result, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.screenerService.screen(query);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,7 @@ export class SharesController {
|
||||
@ApiOperation({ summary: 'Получить спецификацию акции' })
|
||||
@ApiOkResponse({ type: ShareEnvelopeDto })
|
||||
async getShare(@Param('secid') secid: string) {
|
||||
const share = await this.sharesService.getShare(secid);
|
||||
return { data: share, meta: { cachedAt: null, fromCache: false } };
|
||||
return this.sharesService.getShare(secid);
|
||||
}
|
||||
|
||||
@Get(':secid/marketdata')
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { MoexClientService } from '../moex-client/moex-client.service';
|
||||
import { CacheService } from '../cache/cache.service';
|
||||
import { ApiEnvelopePayload } from '../../common/dto/api-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class SharesService {
|
||||
@ -77,8 +78,8 @@ export class SharesService {
|
||||
throw new NotFoundException(`Market data for ${secid} not found`);
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
return new ApiEnvelopePayload(
|
||||
{
|
||||
price: marketData.last ?? 0,
|
||||
change: marketData.lastChange ?? 0,
|
||||
changePercent: marketData.lastChangePrcnt ?? 0,
|
||||
@ -92,8 +93,9 @@ export class SharesService {
|
||||
? new Date().toISOString().split('T')[0] + 'T' + marketData.updateTime
|
||||
: new Date().toISOString(),
|
||||
},
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
|
||||
async getDividends(secid: string) {
|
||||
@ -104,14 +106,15 @@ export class SharesService {
|
||||
'dividendsTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: data.map((d) => ({
|
||||
return new ApiEnvelopePayload(
|
||||
data.map((d) => ({
|
||||
registryCloseDate: d.registryCloseDate,
|
||||
value: d.value,
|
||||
currency: d.currencyId,
|
||||
})),
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
|
||||
async getHistory(secid: string, from: string, till: string) {
|
||||
@ -122,8 +125,8 @@ export class SharesService {
|
||||
'historyTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: data.map((h) => ({
|
||||
return new ApiEnvelopePayload(
|
||||
data.map((h) => ({
|
||||
date: h.tradeDate,
|
||||
open: h.open ?? 0,
|
||||
high: h.high ?? 0,
|
||||
@ -132,7 +135,8 @@ export class SharesService {
|
||||
volume: h.volume,
|
||||
value: h.value,
|
||||
})),
|
||||
meta: { fromCache, cachedAt },
|
||||
};
|
||||
fromCache,
|
||||
cachedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { TBANK_CACHE_KEYS } from '../tbank.config';
|
||||
import type { BrokerAccount } from '../types/broker.types';
|
||||
import type { TBankAccountsResponse } from '../types/tbank-proto.types';
|
||||
import { TBankClientService } from './tbank-client.service';
|
||||
import { ApiEnvelopePayload } from '../../../common/dto/api-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BrokerAccountsService {
|
||||
@ -13,10 +14,7 @@ export class BrokerAccountsService {
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
|
||||
async findAll(): Promise<{
|
||||
data: BrokerAccount[];
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
async findAll(): Promise<ApiEnvelopePayload<BrokerAccount[]>> {
|
||||
const result = await this.cacheService.getOrFetch(
|
||||
TBANK_CACHE_KEYS.accounts,
|
||||
['open-brokerage-iis'],
|
||||
@ -24,10 +22,7 @@ export class BrokerAccountsService {
|
||||
'tbankAccountsTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
async findById(accountId: string): Promise<BrokerAccount | null> {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { CacheService } from '../../cache/cache.service';
|
||||
import { ApiEnvelopePayload } from '../../../common/dto/api-response.dto';
|
||||
import { BrokerAnalyticsDto } from '../dto/broker-analytics-response.dto';
|
||||
import { BrokerAccountsService } from './broker-accounts.service';
|
||||
import { TBANK_CACHE_KEYS } from '../tbank.config';
|
||||
@ -41,10 +42,7 @@ export class BrokerAnalyticsService {
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
|
||||
async getAnalytics(accountId: string): Promise<{
|
||||
data: BrokerAnalyticsDto;
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
async getAnalytics(accountId: string): Promise<ApiEnvelopePayload<BrokerAnalyticsDto>> {
|
||||
const account = await this.accountsService.findById(accountId);
|
||||
if (!account) throw new NotFoundException('Broker account not found');
|
||||
|
||||
@ -55,10 +53,7 @@ export class BrokerAnalyticsService {
|
||||
'tbankAnalyticsTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
private async computeAnalytics(accountId: string): Promise<BrokerAnalyticsDto> {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CacheService } from '../../cache/cache.service';
|
||||
import { MoexClientService } from '../../moex-client/moex-client.service';
|
||||
import { ApiEnvelopePayload } from '../../../common/dto/api-response.dto';
|
||||
import { TBANK_CACHE_KEYS } from '../tbank.config';
|
||||
import { mapQuotationToNumber } from '../mappers/money.mapper';
|
||||
import type {
|
||||
@ -52,10 +53,7 @@ export class BrokerEventsService {
|
||||
async getEvents(
|
||||
accountId: string,
|
||||
query: BrokerEventsQuery,
|
||||
): Promise<{
|
||||
data: BrokerEventsData;
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
): Promise<ApiEnvelopePayload<BrokerEventsData>> {
|
||||
const account = await this.accountsService.findById(accountId);
|
||||
if (!account) throw new NotFoundException('Broker account not found');
|
||||
|
||||
@ -68,10 +66,7 @@ export class BrokerEventsService {
|
||||
'tbankPortfolioTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
private async buildEvents(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CacheService } from '../../cache/cache.service';
|
||||
import { ApiEnvelopePayload } from '../../../common/dto/api-response.dto';
|
||||
import type { BrokerOperationQueryDto } from '../dto/broker-operation-query.dto';
|
||||
import { mapOperationsPage } from '../mappers/operation.mapper';
|
||||
import { TBANK_CACHE_KEYS } from '../tbank.config';
|
||||
@ -19,10 +20,7 @@ export class BrokerOperationsService {
|
||||
async getOperations(
|
||||
accountId: string,
|
||||
query: BrokerOperationQueryDto,
|
||||
): Promise<{
|
||||
data: BrokerOperationsPage;
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
): Promise<ApiEnvelopePayload<BrokerOperationsPage>> {
|
||||
const account = await this.accountsService.findById(accountId);
|
||||
if (!account) throw new NotFoundException('Broker account not found');
|
||||
|
||||
@ -34,10 +32,7 @@ export class BrokerOperationsService {
|
||||
'tbankOperationsTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
private buildRequest(accountId: string, query: BrokerOperationQueryDto): Record<string, unknown> {
|
||||
|
||||
@ -12,6 +12,7 @@ import type {
|
||||
import { BrokerAccountsService } from './broker-accounts.service';
|
||||
import { BrokerInstrumentsService } from './broker-instruments.service';
|
||||
import { TBankClientService } from './tbank-client.service';
|
||||
import { ApiEnvelopePayload } from '../../../common/dto/api-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BrokerPortfolioService {
|
||||
@ -22,10 +23,7 @@ export class BrokerPortfolioService {
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
|
||||
async getPortfolio(accountId: string): Promise<{
|
||||
data: BrokerPortfolio;
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
async getPortfolio(accountId: string): Promise<ApiEnvelopePayload<BrokerPortfolio>> {
|
||||
const account = await this.accountsService.findById(accountId);
|
||||
if (!account) throw new NotFoundException('Broker account not found');
|
||||
|
||||
@ -43,10 +41,7 @@ export class BrokerPortfolioService {
|
||||
'tbankPortfolioTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
async getPositions(
|
||||
@ -54,10 +49,7 @@ export class BrokerPortfolioService {
|
||||
cursor?: string,
|
||||
limit = 10,
|
||||
type?: string,
|
||||
): Promise<{
|
||||
data: BrokerPositionsPage;
|
||||
meta: { fromCache: boolean; cachedAt: string | null };
|
||||
}> {
|
||||
): Promise<ApiEnvelopePayload<BrokerPositionsPage>> {
|
||||
const account = await this.accountsService.findById(accountId);
|
||||
if (!account) throw new NotFoundException('Broker account not found');
|
||||
|
||||
@ -87,10 +79,7 @@ export class BrokerPortfolioService {
|
||||
'tbankPositionsTtl',
|
||||
);
|
||||
|
||||
return {
|
||||
data: result.data,
|
||||
meta: { fromCache: result.fromCache, cachedAt: result.cachedAt },
|
||||
};
|
||||
return new ApiEnvelopePayload(result.data, result.fromCache, result.cachedAt);
|
||||
}
|
||||
|
||||
private async buildInstrumentMap(
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Controller, Get, Param, Post, Query } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiResponse } from '../../common/dto/api-response.dto';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import {
|
||||
BrokerAccountsEnvelopeDto,
|
||||
@ -40,16 +39,14 @@ export class TBankController {
|
||||
@ApiOperation({ summary: 'Get open T-Bank brokerage and IIS accounts' })
|
||||
@ApiOkResponse({ type: BrokerAccountsEnvelopeDto })
|
||||
async getAccounts() {
|
||||
const result = await this.brokerAccountsService.findAll();
|
||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
||||
return this.brokerAccountsService.findAll();
|
||||
}
|
||||
|
||||
@Get('accounts/:accountId/portfolio')
|
||||
@ApiOperation({ summary: 'Get T-Bank broker account portfolio with cash and positions' })
|
||||
@ApiOkResponse({ type: BrokerPortfolioEnvelopeDto })
|
||||
async getPortfolio(@Param('accountId') accountId: string) {
|
||||
const result = await this.brokerPortfolioService.getPortfolio(accountId);
|
||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
||||
return this.brokerPortfolioService.getPortfolio(accountId);
|
||||
}
|
||||
|
||||
@Get('accounts/:accountId/positions')
|
||||
@ -59,13 +56,12 @@ export class TBankController {
|
||||
@Param('accountId') accountId: string,
|
||||
@Query() query: BrokerPositionQueryDto,
|
||||
) {
|
||||
const result = await this.brokerPortfolioService.getPositions(
|
||||
return this.brokerPortfolioService.getPositions(
|
||||
accountId,
|
||||
query.cursor,
|
||||
query.limit,
|
||||
query.type,
|
||||
);
|
||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
||||
}
|
||||
|
||||
@Get('accounts/:accountId/operations')
|
||||
@ -75,24 +71,21 @@ export class TBankController {
|
||||
@Param('accountId') accountId: string,
|
||||
@Query() query: BrokerOperationQueryDto,
|
||||
) {
|
||||
const result = await this.brokerOperationsService.getOperations(accountId, query);
|
||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
||||
return this.brokerOperationsService.getOperations(accountId, query);
|
||||
}
|
||||
|
||||
@Get('accounts/:accountId/events')
|
||||
@ApiOperation({ summary: 'Get broker account calendar events and cashflow' })
|
||||
@ApiOkResponse({ type: BrokerEventsEnvelopeDto })
|
||||
async getEvents(@Param('accountId') accountId: string, @Query() query: BrokerEventsQueryDto) {
|
||||
const result = await this.brokerEventsService.getEvents(accountId, query);
|
||||
return new ApiResponse(result.data, result.meta.fromCache, result.meta.cachedAt);
|
||||
return this.brokerEventsService.getEvents(accountId, query);
|
||||
}
|
||||
|
||||
@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);
|
||||
return this.brokerAnalyticsService.getAnalytics(accountId);
|
||||
}
|
||||
|
||||
@Post('accounts/:accountId/operations/sync')
|
||||
@ -102,7 +95,6 @@ export class TBankController {
|
||||
@Param('accountId') accountId: string,
|
||||
@Query() query: BrokerOperationSyncQueryDto,
|
||||
) {
|
||||
const result = await this.brokerOperationSyncService.syncAccount(accountId, query);
|
||||
return new ApiResponse(result);
|
||||
return this.brokerOperationSyncService.syncAccount(accountId, query);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user