feat(11-06): TenderSavedSearch model + CRUD service (FILTER-06)

GREEN phase — adds TenderSavedSearch (userId+tenantId scoped, filters
Json, @@unique([userId,name])), the migration (applied to local dev DB),
and TenderSavedSearchService following the FavoritesService/
TenderTriageService pattern: manual where:{userId} scoping (no
forTenant()/RLS), ownership check before update/remove, P2002 unique
conflicts translated to ConflictException.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 16:45:04 +02:00
parent 2b0b4f897b
commit df94d921ef
4 changed files with 198 additions and 0 deletions
@@ -0,0 +1,25 @@
-- CreateTable
-- FILTER-06 (D-08/D-11) — per-user Suchprofil (benannte Filterkombination).
-- Scoping-Muster wie FavoriteLink/TenderTriage (T-08-06, Pitfall 4):
-- userId-Scoping im Service, KEIN forTenant()/RLS. tenantId wird
-- zusätzlich mitgeführt (spätere Tenant-Isolation), ist aber nicht das
-- Scoping-Feld. KEIN Tender-FK (Pitfall 6) — filters speichert nur
-- Kriterien, nicht Tender-Ids, daher unkritisch bei der Retention.
CREATE TABLE IF NOT EXISTS "TenderSavedSearch" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"filters" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TenderSavedSearch_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
-- Verhindert zwei Profile gleichen Namens pro Nutzer (T-11-14).
CREATE UNIQUE INDEX IF NOT EXISTS "TenderSavedSearch_userId_name_key" ON "TenderSavedSearch"("userId", "name");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "TenderSavedSearch_userId_idx" ON "TenderSavedSearch"("userId");