- IntelligenceModule: proxy tenant-scoped catre intelligence-api
(/companies/search, /companies/{id}); ceo-web nu vorbeste niciodata
direct cu intelligence-api (blueprint 10.3/16).
- SavedSegments: salveaza o cautare Apollo si o ruleaza din nou oricand.
- ResearchBriefs: dovezi curatate manual per companie, cu sursa Apollo
implicita plus surse suplimentare -- explicit fara rezumat generat de
AI (AI Gateway inca neconstruit, blueprint 14).
- BriefingService: /v1/briefing/today, agregare deterministica de
taskuri restante/viitoare + activitate saptamanala; campul
aiExplanation ramane null si vizibil in raspuns, nu simulat.
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { and, desc, eq } from 'drizzle-orm';
|
|
import { db } from '../db/client';
|
|
import { researchBriefs } from '../db/schema';
|
|
import { AuditService } from '../audit/audit.service';
|
|
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
|
import type { CreateResearchBriefDto } from './dto';
|
|
|
|
@Injectable()
|
|
export class ResearchBriefsService {
|
|
constructor(private readonly audit: AuditService) {}
|
|
|
|
async list(session: AuthenticatedSession) {
|
|
return db.query.researchBriefs.findMany({
|
|
where: eq(researchBriefs.tenantId, session.tenantId),
|
|
orderBy: desc(researchBriefs.createdAt),
|
|
});
|
|
}
|
|
|
|
async getById(session: AuthenticatedSession, id: string) {
|
|
const brief = await db.query.researchBriefs.findFirst({
|
|
where: and(eq(researchBriefs.id, id), eq(researchBriefs.tenantId, session.tenantId)),
|
|
});
|
|
if (!brief) {
|
|
throw new NotFoundException('Research brief not found');
|
|
}
|
|
return brief;
|
|
}
|
|
|
|
async create(session: AuthenticatedSession, dto: CreateResearchBriefDto) {
|
|
// Provenance obligatoriu (blueprint 9.1 Evidence & Provenance Fabric): chiar in
|
|
// versiunea curatata manual, fiecare brief indica cel putin sursa Apollo/intelligence-api.
|
|
const sources = [
|
|
{ label: 'Apollo / intelligence-api', url: 'https://intelligence-api.boardmind.dev' },
|
|
...(dto.sources ?? []),
|
|
];
|
|
|
|
const [row] = await db
|
|
.insert(researchBriefs)
|
|
.values({
|
|
tenantId: session.tenantId,
|
|
createdByUserId: session.userId,
|
|
organizationId: dto.organizationId,
|
|
organizationName: dto.organizationName,
|
|
title: dto.title,
|
|
summary: dto.summary,
|
|
sources,
|
|
})
|
|
.returning();
|
|
|
|
await this.audit.record(null, {
|
|
tenantId: session.tenantId,
|
|
actorId: session.userId,
|
|
action: 'research_brief.created',
|
|
resource: `research_brief:${row.id}`,
|
|
});
|
|
return row;
|
|
}
|
|
|
|
async remove(session: AuthenticatedSession, id: string) {
|
|
const brief = await this.getById(session, id);
|
|
await db.delete(researchBriefs).where(eq(researchBriefs.id, brief.id));
|
|
await this.audit.record(null, {
|
|
tenantId: session.tenantId,
|
|
actorId: session.userId,
|
|
action: 'research_brief.deleted',
|
|
resource: `research_brief:${id}`,
|
|
});
|
|
return { deleted: true };
|
|
}
|
|
}
|