Der GExtension-Abgleich liefert die Käufe, der Verbrauch steht im Forum. Die App liest jetzt die drei CC-Boards und sammelt die Anträge. Gelesen wird nur die Board-Übersicht: sie führt bereits den Ersteller, nicht nur den letzten Antwortenden. Damit bleibt ein Lauf bei wenigen Anfragen statt einer je Thread. Unterforen und globale Kästen tragen ebenfalls Thread-Links, deshalb liest der Parser strikt entlang der Listeneinträge - sonst landen Changelogs und Bewerbungen in der Antragsliste. Klassifiziert wird ausschließlich von Hand. Weder Titel noch Board sind verlässlich genug: Threads werden nicht immer verschoben, ein genehmigter Antrag kann weiter im Antrags-Board liegen. Das Board steht deshalb nur als Hinweis an der Zeile. Angenommene Anträge rufen dieselben Aktionen wie die Knöpfe auf der Spielerseite, es gibt keinen zweiten Buchungsweg mit eigenen Regeln. Wandert ein bereits entschiedener Thread später in ein anderes Board, erscheint das als Hinweis zum Abhaken - der CC-Status ändert sich nie von allein. Die Thread-ID bleibt beim Verschieben stabil und traegt deshalb die Wiedererkennung; die Board-Historie wird mitgeschrieben. Zurückgelassene Verweise erkennt der Parser und zählt sie nicht als eigenen Antrag.
380 lines
12 KiB
SQL
380 lines
12 KiB
SQL
-- CreateSchema
|
|
CREATE SCHEMA IF NOT EXISTS "public";
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "Role" AS ENUM ('ADMIN', 'EDITOR', 'VIEWER');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "CreditKind" AS ENUM ('CC', 'REDESIGN_PAID', 'REDESIGN_KULANZ');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "CreditSource" AS ENUM ('CC_PACKAGE', 'REDESIGN_PACKAGE', 'KULANZ', 'ADJUSTMENT', 'CONSUME', 'REVERSAL');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "CharacterStatus" AS ENUM ('ACTIVE', 'INACTIVE');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "InactiveReason" AS ENUM ('PLAYER_INACTIVITY', 'REWORKED', 'OTHER');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ProductTarget" AS ENUM ('CC_PACKAGE', 'REDESIGN_PACKAGE', 'IGNORE');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ForumBoard" AS ENUM ('CUSTOM_CHARACTERS', 'AKTIVE_CCS', 'CC_ARCHIV');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ForumClassification" AS ENUM ('NEW_CC', 'REDESIGN', 'REJECTED', 'IGNORE');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ImportStatus" AS ENUM ('PENDING', 'COMMITTED', 'DISCARDED');
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "User" (
|
|
"id" SERIAL NOT NULL,
|
|
"username" TEXT NOT NULL,
|
|
"passwordHash" TEXT NOT NULL,
|
|
"role" "Role" NOT NULL DEFAULT 'EDITOR',
|
|
"totpSecret" TEXT,
|
|
"totpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
"mustChangePassword" BOOLEAN NOT NULL DEFAULT false,
|
|
"lastLoginAt" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Session" (
|
|
"id" TEXT NOT NULL,
|
|
"tokenHash" TEXT NOT NULL,
|
|
"userId" INTEGER NOT NULL,
|
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"lastSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"ip" TEXT,
|
|
"userAgent" TEXT,
|
|
|
|
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Player" (
|
|
"id" SERIAL NOT NULL,
|
|
"displayName" TEXT NOT NULL,
|
|
"steamId" TEXT,
|
|
"forumName" TEXT,
|
|
"notes" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"gextensionUsage" INTEGER,
|
|
"gextensionSyncedAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "Player_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "UnmatchedPackage" (
|
|
"id" SERIAL NOT NULL,
|
|
"externalId" TEXT NOT NULL,
|
|
"playerId" INTEGER,
|
|
"steamId" TEXT NOT NULL,
|
|
"rawTitle" TEXT NOT NULL,
|
|
"rawTag" TEXT NOT NULL,
|
|
"amount" TEXT,
|
|
"purchasedAt" TIMESTAMP(3),
|
|
"scrapedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"resolved" BOOLEAN NOT NULL DEFAULT false,
|
|
"resolvedAs" TEXT,
|
|
"resolvedById" INTEGER,
|
|
"resolvedAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "UnmatchedPackage_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "CreditLedger" (
|
|
"id" SERIAL NOT NULL,
|
|
"playerId" INTEGER NOT NULL,
|
|
"kind" "CreditKind" NOT NULL,
|
|
"amount" INTEGER NOT NULL,
|
|
"source" "CreditSource" NOT NULL,
|
|
"groupId" TEXT NOT NULL,
|
|
"characterId" INTEGER,
|
|
"importId" INTEGER,
|
|
"note" TEXT,
|
|
"reversalOfId" INTEGER,
|
|
"createdById" INTEGER,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "CreditLedger_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Character" (
|
|
"id" SERIAL NOT NULL,
|
|
"playerId" INTEGER NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"loreText" TEXT,
|
|
"status" "CharacterStatus" NOT NULL DEFAULT 'ACTIVE',
|
|
"inactiveReason" "InactiveReason",
|
|
"inactiveNote" TEXT,
|
|
"lineageId" TEXT NOT NULL,
|
|
"predecessorId" INTEGER,
|
|
"createdById" INTEGER,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Character_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Import" (
|
|
"id" SERIAL NOT NULL,
|
|
"filename" TEXT NOT NULL,
|
|
"status" "ImportStatus" NOT NULL DEFAULT 'PENDING',
|
|
"rowCount" INTEGER NOT NULL DEFAULT 0,
|
|
"bookedCount" INTEGER NOT NULL DEFAULT 0,
|
|
"mapping" JSONB,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"committedAt" TIMESTAMP(3),
|
|
"importedById" INTEGER,
|
|
|
|
CONSTRAINT "Import_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ImportRow" (
|
|
"id" SERIAL NOT NULL,
|
|
"importId" INTEGER NOT NULL,
|
|
"lineNo" INTEGER NOT NULL,
|
|
"externalId" TEXT,
|
|
"playerKey" TEXT,
|
|
"playerName" TEXT,
|
|
"productName" TEXT,
|
|
"purchasedAt" TIMESTAMP(3),
|
|
"target" "ProductTarget",
|
|
"resolvedPlayerId" INTEGER,
|
|
"plan" TEXT NOT NULL,
|
|
"raw" JSONB NOT NULL,
|
|
|
|
CONSTRAINT "ImportRow_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ImportedOrder" (
|
|
"id" SERIAL NOT NULL,
|
|
"externalId" TEXT NOT NULL,
|
|
"importId" INTEGER NOT NULL,
|
|
"playerId" INTEGER NOT NULL,
|
|
"target" "ProductTarget" NOT NULL,
|
|
"groupId" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ImportedOrder_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ProductMapping" (
|
|
"id" SERIAL NOT NULL,
|
|
"matchPattern" TEXT NOT NULL,
|
|
"target" "ProductTarget" NOT NULL,
|
|
"priority" INTEGER NOT NULL DEFAULT 100,
|
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
"note" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "ProductMapping_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "AuditLog" (
|
|
"id" SERIAL NOT NULL,
|
|
"actorUserId" INTEGER,
|
|
"actorName" TEXT,
|
|
"action" TEXT NOT NULL,
|
|
"targetType" TEXT NOT NULL,
|
|
"targetId" TEXT,
|
|
"details" JSONB,
|
|
"ip" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ForumThread" (
|
|
"threadId" INTEGER NOT NULL,
|
|
"title" TEXT NOT NULL,
|
|
"url" TEXT NOT NULL,
|
|
"authorName" TEXT,
|
|
"authorForumId" INTEGER,
|
|
"playerId" INTEGER,
|
|
"currentBoard" "ForumBoard" NOT NULL,
|
|
"firstSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"lastSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"lastBoardChangeAt" TIMESTAMP(3),
|
|
"classification" "ForumClassification",
|
|
"needsClassification" BOOLEAN NOT NULL DEFAULT true,
|
|
"classifiedById" INTEGER,
|
|
"classifiedAt" TIMESTAMP(3),
|
|
"linkedCharacterId" INTEGER,
|
|
"boardChangeNotice" BOOLEAN NOT NULL DEFAULT false,
|
|
|
|
CONSTRAINT "ForumThread_pkey" PRIMARY KEY ("threadId")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ForumThreadBoard" (
|
|
"id" SERIAL NOT NULL,
|
|
"threadId" INTEGER NOT NULL,
|
|
"board" "ForumBoard" NOT NULL,
|
|
"observedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ForumThreadBoard_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Session_tokenHash_key" ON "Session"("tokenHash");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Session_userId_idx" ON "Session"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Session_expiresAt_idx" ON "Session"("expiresAt");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Player_steamId_key" ON "Player"("steamId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Player_displayName_idx" ON "Player"("displayName");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "UnmatchedPackage_externalId_key" ON "UnmatchedPackage"("externalId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "UnmatchedPackage_resolved_idx" ON "UnmatchedPackage"("resolved");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "UnmatchedPackage_playerId_idx" ON "UnmatchedPackage"("playerId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "CreditLedger_reversalOfId_key" ON "CreditLedger"("reversalOfId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "CreditLedger_playerId_idx" ON "CreditLedger"("playerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "CreditLedger_playerId_kind_idx" ON "CreditLedger"("playerId", "kind");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "CreditLedger_groupId_idx" ON "CreditLedger"("groupId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "CreditLedger_createdAt_idx" ON "CreditLedger"("createdAt");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Character_playerId_idx" ON "Character"("playerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Character_playerId_status_idx" ON "Character"("playerId", "status");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Character_lineageId_idx" ON "Character"("lineageId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Import_status_idx" ON "Import"("status");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ImportRow_importId_idx" ON "ImportRow"("importId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "ImportedOrder_externalId_key" ON "ImportedOrder"("externalId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ImportedOrder_playerId_idx" ON "ImportedOrder"("playerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ProductMapping_priority_idx" ON "ProductMapping"("priority");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_targetType_targetId_idx" ON "AuditLog"("targetType", "targetId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "AuditLog_actorUserId_idx" ON "AuditLog"("actorUserId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ForumThread_needsClassification_idx" ON "ForumThread"("needsClassification");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ForumThread_playerId_idx" ON "ForumThread"("playerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ForumThread_currentBoard_idx" ON "ForumThread"("currentBoard");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ForumThreadBoard_threadId_idx" ON "ForumThreadBoard"("threadId");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "UnmatchedPackage" ADD CONSTRAINT "UnmatchedPackage_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_characterId_fkey" FOREIGN KEY ("characterId") REFERENCES "Character"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_reversalOfId_fkey" FOREIGN KEY ("reversalOfId") REFERENCES "CreditLedger"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CreditLedger" ADD CONSTRAINT "CreditLedger_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_predecessorId_fkey" FOREIGN KEY ("predecessorId") REFERENCES "Character"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Character" ADD CONSTRAINT "Character_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Import" ADD CONSTRAINT "Import_importedById_fkey" FOREIGN KEY ("importedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImportRow" ADD CONSTRAINT "ImportRow_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImportedOrder" ADD CONSTRAINT "ImportedOrder_importId_fkey" FOREIGN KEY ("importId") REFERENCES "Import"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ImportedOrder" ADD CONSTRAINT "ImportedOrder_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_actorUserId_fkey" FOREIGN KEY ("actorUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ForumThread" ADD CONSTRAINT "ForumThread_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ForumThread" ADD CONSTRAINT "ForumThread_linkedCharacterId_fkey" FOREIGN KEY ("linkedCharacterId") REFERENCES "Character"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ForumThreadBoard" ADD CONSTRAINT "ForumThreadBoard_threadId_fkey" FOREIGN KEY ("threadId") REFERENCES "ForumThread"("threadId") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|