feat(10-03): implement TenderNormalizerService — fields, dedupKey, hash

- normalize(raw): eForms-DE XML primary for deadlineAt/estimatedValue/
  procedureType (RESEARCH Pattern 3); OCDS primary for ocid/buyerName/
  title/cpvCodes/region/plz
- deadlineAt/estimatedValue nullable by mandate (RESEARCH Pattern 4) —
  missing data normalizes to null, never thrown or zero
- dedupKey priority: ocid -> sourcePortal:sourceNoticeId fallback
- contentHash = sha256(title+deadlineAt+estimatedValue+status), stable
  across repeat calls, changes when the deadline changes (SCHEMA-02 hook)
- Register TenderNormalizerService in TendersModule.providers
- All tender-normalizer.service.spec.ts tests green (6/6); full API
  suite green (59/59); tsc --noEmit clean
This commit is contained in:
2026-07-21 10:58:33 +02:00
parent 3764feb50b
commit 31607df48c
2 changed files with 248 additions and 1 deletions
+2 -1
View File
@@ -4,6 +4,7 @@ import { ModuleRegistryService } from '../module-registry/module-registry.servic
import { PrismaService } from '../prisma/prisma.service';
import { DoeOpenDataAdapter } from './adapters/doe-opendata.adapter';
import { seedTendersModule } from './tenders.seed';
import { TenderNormalizerService } from './tender-normalizer.service';
/**
* NestJS module for the Ausschreibungs-Radar feature.
@@ -22,7 +23,7 @@ import { seedTendersModule } from './tenders.seed';
@Module({
imports: [ModuleRegistryModule],
controllers: [],
providers: [DoeOpenDataAdapter],
providers: [DoeOpenDataAdapter, TenderNormalizerService],
})
export class TendersModule implements OnModuleInit {
private readonly logger = new Logger(TendersModule.name);