feat: enrich portfolio list with total value and position breakdown from MOEX #10

Merged
ksv741 merged 7 commits from perf/portfolio-enricher-optimization into main 2026-06-14 14:19:56 +03:00
Showing only changes of commit f0e5e36b7d - Show all commits

View File

@ -60,10 +60,55 @@ export class PortfolioService {
}
async findAll(userId: number) {
return this.prisma.portfolio.findMany({
const portfolios = await this.prisma.portfolio.findMany({
where: { userId },
include: { positions: true },
orderBy: { updatedAt: 'desc' },
});
const allPositions = portfolios.flatMap((p) => p.positions);
if (allPositions.length === 0) {
return portfolios.map((p) => ({
id: p.id,
name: p.name,
description: p.description,
currency: p.currency,
createdAt: p.createdAt.toISOString(),
updatedAt: p.updatedAt.toISOString(),
totalValue: 0,
positionCount: 0,
shareCount: 0,
bondCount: 0,
}));
}
const enrichedPositions = await this.enrichPositions(allPositions);
const posByPortfolioId = new Map<number, (typeof enrichedPositions)[number][]>();
for (let i = 0; i < enrichedPositions.length; i++) {
const pfId = allPositions[i].portfolioId;
if (!posByPortfolioId.has(pfId)) {
posByPortfolioId.set(pfId, []);
}
posByPortfolioId.get(pfId)!.push(enrichedPositions[i]);
}
return portfolios.map((p) => {
const positions = posByPortfolioId.get(p.id) ?? [];
const totalValue = positions.reduce((sum, pos) => sum + (pos.currentValue ?? 0), 0);
return {
id: p.id,
name: p.name,
description: p.description,
currency: p.currency,
createdAt: p.createdAt.toISOString(),
updatedAt: p.updatedAt.toISOString(),
totalValue: Math.round(totalValue * 100) / 100,
positionCount: positions.length,
shareCount: positions.filter((pos) => pos.type === 'share').length,
bondCount: positions.filter((pos) => pos.type === 'bond').length,
};
});
}
async findOne(userId: number, id: number) {