From ebbbff123f674b951bbffc5db48e1d8e74068679 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sat, 1 Aug 2026 10:32:03 +0000 Subject: [PATCH] feat(cc-052): FinancialIntelligenceController --- .../financial-intelligence.controller.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/financial-intelligence/financial-intelligence.controller.ts diff --git a/src/financial-intelligence/financial-intelligence.controller.ts b/src/financial-intelligence/financial-intelligence.controller.ts new file mode 100644 index 0000000..640f2c8 --- /dev/null +++ b/src/financial-intelligence/financial-intelligence.controller.ts @@ -0,0 +1,66 @@ +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { desc, isNull, or, eq, and, gte } from 'drizzle-orm'; +import { CurrentSession } from '../auth/session.decorator'; +import type { AuthenticatedSession } from '../auth/tenant.guard'; +import { db } from '../db/client'; +import { financialSignals } from '../db/schema'; +import { IngestSignalsDto } from './dto'; + +@Controller('financial-intelligence') +export class FinancialIntelligenceController { + /** GET /v1/financial-intelligence/signals — semnale active pentru tenant */ + @Get('signals') + async signals( + @CurrentSession() session: AuthenticatedSession, + @Query('limit') limitStr?: string, + @Query('category') category?: string, + @Query('region') region?: string, + ) { + const limit = Math.min(Math.max(1, parseInt(limitStr ?? '50', 10) || 50), 200); + const now = new Date(); + + const conditions = [ + or(isNull(financialSignals.tenantId), eq(financialSignals.tenantId, session.tenantId)), + or(isNull(financialSignals.expiresAt), gte(financialSignals.expiresAt, now)), + ]; + if (category) conditions.push(eq(financialSignals.category, category)); + if (region) conditions.push(eq(financialSignals.region, region)); + + return db.query.financialSignals.findMany({ + where: and(...conditions), + orderBy: [desc(financialSignals.publishedAt)], + limit, + }); + } + + /** POST /v1/financial-intelligence/ingest — webhook pentru n8n */ + @Post('ingest') + async ingest( + @CurrentSession() session: AuthenticatedSession, + @Body() dto: IngestSignalsDto, + ) { + if (!dto.signals || dto.signals.length === 0) { + return { ingested: 0 }; + } + + const rows = dto.signals.map((s) => ({ + tenantId: dto.tenantId ?? null, + category: s.category ?? 'macro', + region: s.region ?? 'global', + niche: s.niche ?? null, + source: s.source, + indicatorCode: s.indicatorCode ?? null, + title: s.title, + summary: s.summary, + rawValue: s.rawValue != null ? String(s.rawValue) : null, + changePercent: s.changePercent != null ? String(s.changePercent) : null, + unit: s.unit ?? null, + severity: s.severity ?? 'info', + publishedAt: s.publishedAt ? new Date(s.publishedAt) : new Date(), + expiresAt: s.expiresAt ? new Date(s.expiresAt) : null, + })); + + await db.insert(financialSignals).values(rows); + return { ingested: rows.length }; + } +}