import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { diffSnapshots, summarizeChanges } from './contract-document-diff.util'; import { ContractDocumentRevision } from './entities/contract-document-revision.entity'; import type { ContractDocumentSnapshot } from './entities/contract.entity'; export interface RecordRevisionInput { contractId: string; before: ContractDocumentSnapshot | null; after: ContractDocumentSnapshot | null; actorId?: string | null; actorRole?: string | null; stepId?: string | null; } @Injectable() export class ContractDocumentHistoryService { private readonly logger = new Logger(ContractDocumentHistoryService.name); constructor( @InjectRepository(ContractDocumentRevision) private readonly revisionRepo: Repository, ) {} /** * Append a revision describing what an edit changed. Best-effort: recording * history must never break the edit that triggered it, so failures are logged * and swallowed. A no-op edit records nothing. */ async record(input: RecordRevisionInput): Promise { try { const changes = diffSnapshots(input.before, input.after); if (changes.length === 0) return; await this.revisionRepo.save( this.revisionRepo.create({ contractId: input.contractId, actorId: input.actorId ?? null, actorRole: input.actorRole ?? null, stepId: input.stepId ?? null, summary: summarizeChanges(changes), changes, }), ); } catch (err) { this.logger.error( `Failed to record document revision for contract ${input.contractId}: ${String(err)}`, ); } } /** Revision history for a contract, newest first. */ list(contractId: string): Promise { return this.revisionRepo.find({ where: { contractId }, order: { createdAt: 'DESC' }, }); } }