miinventario-backend-v2/src/migrations/1736502000000-CreateFeedbackTables.ts
rckrdmrd 5a1c966ed2 Migración desde miinventario/backend - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:12:15 -06:00

202 lines
14 KiB
TypeScript

import { MigrationInterface, QueryRunner } from "typeorm";
export class CreateFeedbackTables1736502000000 implements MigrationInterface {
name = 'CreateFeedbackTables1736502000000'
public async up(queryRunner: QueryRunner): Promise<void> {
// ENUMs
await queryRunner.query(`CREATE TYPE "public"."corrections_type_enum" AS ENUM('QUANTITY', 'SKU', 'CONFIRMATION')`);
await queryRunner.query(`CREATE TYPE "public"."ground_truth_status_enum" AS ENUM('PENDING', 'APPROVED', 'REJECTED')`);
await queryRunner.query(`CREATE TYPE "public"."product_submissions_status_enum" AS ENUM('PENDING', 'APPROVED', 'REJECTED')`);
await queryRunner.query(`CREATE TYPE "public"."validation_requests_status_enum" AS ENUM('PENDING', 'COMPLETED', 'SKIPPED', 'EXPIRED')`);
// Corrections table - Historial de correcciones de usuario
await queryRunner.query(`CREATE TABLE "corrections" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"inventoryItemId" uuid NOT NULL,
"userId" uuid NOT NULL,
"storeId" uuid NOT NULL,
"type" "public"."corrections_type_enum" NOT NULL,
"previousValue" jsonb NOT NULL,
"newValue" jsonb NOT NULL,
"reason" character varying(255),
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_corrections" PRIMARY KEY ("id")
)`);
await queryRunner.query(`CREATE INDEX "IDX_corrections_item" ON "corrections" ("inventoryItemId")`);
await queryRunner.query(`CREATE INDEX "IDX_corrections_user" ON "corrections" ("userId")`);
await queryRunner.query(`CREATE INDEX "IDX_corrections_store" ON "corrections" ("storeId")`);
await queryRunner.query(`CREATE INDEX "IDX_corrections_type" ON "corrections" ("type", "createdAt")`);
// Ground Truth table - Datos validados para entrenamiento
await queryRunner.query(`CREATE TABLE "ground_truth" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"inventoryItemId" uuid NOT NULL,
"videoId" uuid NOT NULL,
"storeId" uuid NOT NULL,
"originalDetection" jsonb NOT NULL,
"correctedData" jsonb NOT NULL,
"frameTimestamp" integer,
"boundingBox" jsonb,
"status" "public"."ground_truth_status_enum" NOT NULL DEFAULT 'PENDING',
"validatedBy" uuid,
"validationScore" numeric(5,2),
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_ground_truth" PRIMARY KEY ("id")
)`);
await queryRunner.query(`CREATE INDEX "IDX_ground_truth_item" ON "ground_truth" ("inventoryItemId")`);
await queryRunner.query(`CREATE INDEX "IDX_ground_truth_video" ON "ground_truth" ("videoId")`);
await queryRunner.query(`CREATE INDEX "IDX_ground_truth_status" ON "ground_truth" ("status")`);
await queryRunner.query(`CREATE INDEX "IDX_ground_truth_store" ON "ground_truth" ("storeId")`);
// Product Submissions table - Nuevos productos etiquetados por usuarios
await queryRunner.query(`CREATE TABLE "product_submissions" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"userId" uuid NOT NULL,
"storeId" uuid NOT NULL,
"videoId" uuid,
"name" character varying(255) NOT NULL,
"category" character varying(100),
"barcode" character varying(50),
"imageUrl" character varying(500),
"frameTimestamp" integer,
"boundingBox" jsonb,
"status" "public"."product_submissions_status_enum" NOT NULL DEFAULT 'PENDING',
"reviewedBy" uuid,
"reviewNotes" text,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_product_submissions" PRIMARY KEY ("id")
)`);
await queryRunner.query(`CREATE INDEX "IDX_product_submissions_user" ON "product_submissions" ("userId")`);
await queryRunner.query(`CREATE INDEX "IDX_product_submissions_store" ON "product_submissions" ("storeId")`);
await queryRunner.query(`CREATE INDEX "IDX_product_submissions_status" ON "product_submissions" ("status")`);
await queryRunner.query(`CREATE INDEX "IDX_product_submissions_barcode" ON "product_submissions" ("barcode")`);
// Validation Requests table - Solicitudes de micro-auditoria
await queryRunner.query(`CREATE TABLE "validation_requests" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"videoId" uuid NOT NULL,
"userId" uuid NOT NULL,
"storeId" uuid NOT NULL,
"totalItems" integer NOT NULL DEFAULT '0',
"itemsValidated" integer NOT NULL DEFAULT '0',
"triggerReason" character varying(100) NOT NULL,
"probabilityScore" numeric(5,2) NOT NULL,
"status" "public"."validation_requests_status_enum" NOT NULL DEFAULT 'PENDING',
"expiresAt" TIMESTAMP NOT NULL,
"completedAt" TIMESTAMP,
"creditsRewarded" integer NOT NULL DEFAULT '0',
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_validation_requests" PRIMARY KEY ("id")
)`);
await queryRunner.query(`CREATE INDEX "IDX_validation_requests_video" ON "validation_requests" ("videoId")`);
await queryRunner.query(`CREATE INDEX "IDX_validation_requests_user" ON "validation_requests" ("userId")`);
await queryRunner.query(`CREATE INDEX "IDX_validation_requests_store" ON "validation_requests" ("storeId")`);
await queryRunner.query(`CREATE INDEX "IDX_validation_requests_status" ON "validation_requests" ("status", "expiresAt")`);
// Validation Responses table - Respuestas de validacion por item
await queryRunner.query(`CREATE TABLE "validation_responses" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"requestId" uuid NOT NULL,
"inventoryItemId" uuid NOT NULL,
"userId" uuid NOT NULL,
"isCorrect" boolean,
"correctedQuantity" integer,
"correctedName" character varying(255),
"responseTimeMs" integer,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_validation_responses" PRIMARY KEY ("id")
)`);
await queryRunner.query(`CREATE INDEX "IDX_validation_responses_request" ON "validation_responses" ("requestId")`);
await queryRunner.query(`CREATE INDEX "IDX_validation_responses_item" ON "validation_responses" ("inventoryItemId")`);
await queryRunner.query(`CREATE INDEX "IDX_validation_responses_user" ON "validation_responses" ("userId")`);
// Foreign Keys
await queryRunner.query(`ALTER TABLE "corrections" ADD CONSTRAINT "FK_corrections_item" FOREIGN KEY ("inventoryItemId") REFERENCES "inventory_items"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "corrections" ADD CONSTRAINT "FK_corrections_user" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "corrections" ADD CONSTRAINT "FK_corrections_store" FOREIGN KEY ("storeId") REFERENCES "stores"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "ground_truth" ADD CONSTRAINT "FK_ground_truth_item" FOREIGN KEY ("inventoryItemId") REFERENCES "inventory_items"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "ground_truth" ADD CONSTRAINT "FK_ground_truth_video" FOREIGN KEY ("videoId") REFERENCES "videos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "ground_truth" ADD CONSTRAINT "FK_ground_truth_store" FOREIGN KEY ("storeId") REFERENCES "stores"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "ground_truth" ADD CONSTRAINT "FK_ground_truth_validator" FOREIGN KEY ("validatedBy") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "product_submissions" ADD CONSTRAINT "FK_product_submissions_user" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "product_submissions" ADD CONSTRAINT "FK_product_submissions_store" FOREIGN KEY ("storeId") REFERENCES "stores"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "product_submissions" ADD CONSTRAINT "FK_product_submissions_video" FOREIGN KEY ("videoId") REFERENCES "videos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "product_submissions" ADD CONSTRAINT "FK_product_submissions_reviewer" FOREIGN KEY ("reviewedBy") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_requests" ADD CONSTRAINT "FK_validation_requests_video" FOREIGN KEY ("videoId") REFERENCES "videos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_requests" ADD CONSTRAINT "FK_validation_requests_user" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_requests" ADD CONSTRAINT "FK_validation_requests_store" FOREIGN KEY ("storeId") REFERENCES "stores"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_responses" ADD CONSTRAINT "FK_validation_responses_request" FOREIGN KEY ("requestId") REFERENCES "validation_requests"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_responses" ADD CONSTRAINT "FK_validation_responses_item" FOREIGN KEY ("inventoryItemId") REFERENCES "inventory_items"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "validation_responses" ADD CONSTRAINT "FK_validation_responses_user" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Drop Foreign Keys
await queryRunner.query(`ALTER TABLE "validation_responses" DROP CONSTRAINT "FK_validation_responses_user"`);
await queryRunner.query(`ALTER TABLE "validation_responses" DROP CONSTRAINT "FK_validation_responses_item"`);
await queryRunner.query(`ALTER TABLE "validation_responses" DROP CONSTRAINT "FK_validation_responses_request"`);
await queryRunner.query(`ALTER TABLE "validation_requests" DROP CONSTRAINT "FK_validation_requests_store"`);
await queryRunner.query(`ALTER TABLE "validation_requests" DROP CONSTRAINT "FK_validation_requests_user"`);
await queryRunner.query(`ALTER TABLE "validation_requests" DROP CONSTRAINT "FK_validation_requests_video"`);
await queryRunner.query(`ALTER TABLE "product_submissions" DROP CONSTRAINT "FK_product_submissions_reviewer"`);
await queryRunner.query(`ALTER TABLE "product_submissions" DROP CONSTRAINT "FK_product_submissions_video"`);
await queryRunner.query(`ALTER TABLE "product_submissions" DROP CONSTRAINT "FK_product_submissions_store"`);
await queryRunner.query(`ALTER TABLE "product_submissions" DROP CONSTRAINT "FK_product_submissions_user"`);
await queryRunner.query(`ALTER TABLE "ground_truth" DROP CONSTRAINT "FK_ground_truth_validator"`);
await queryRunner.query(`ALTER TABLE "ground_truth" DROP CONSTRAINT "FK_ground_truth_store"`);
await queryRunner.query(`ALTER TABLE "ground_truth" DROP CONSTRAINT "FK_ground_truth_video"`);
await queryRunner.query(`ALTER TABLE "ground_truth" DROP CONSTRAINT "FK_ground_truth_item"`);
await queryRunner.query(`ALTER TABLE "corrections" DROP CONSTRAINT "FK_corrections_store"`);
await queryRunner.query(`ALTER TABLE "corrections" DROP CONSTRAINT "FK_corrections_user"`);
await queryRunner.query(`ALTER TABLE "corrections" DROP CONSTRAINT "FK_corrections_item"`);
// Drop Tables
await queryRunner.query(`DROP INDEX "public"."IDX_validation_responses_user"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_responses_item"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_responses_request"`);
await queryRunner.query(`DROP TABLE "validation_responses"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_requests_status"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_requests_store"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_requests_user"`);
await queryRunner.query(`DROP INDEX "public"."IDX_validation_requests_video"`);
await queryRunner.query(`DROP TABLE "validation_requests"`);
await queryRunner.query(`DROP INDEX "public"."IDX_product_submissions_barcode"`);
await queryRunner.query(`DROP INDEX "public"."IDX_product_submissions_status"`);
await queryRunner.query(`DROP INDEX "public"."IDX_product_submissions_store"`);
await queryRunner.query(`DROP INDEX "public"."IDX_product_submissions_user"`);
await queryRunner.query(`DROP TABLE "product_submissions"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ground_truth_store"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ground_truth_status"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ground_truth_video"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ground_truth_item"`);
await queryRunner.query(`DROP TABLE "ground_truth"`);
await queryRunner.query(`DROP INDEX "public"."IDX_corrections_type"`);
await queryRunner.query(`DROP INDEX "public"."IDX_corrections_store"`);
await queryRunner.query(`DROP INDEX "public"."IDX_corrections_user"`);
await queryRunner.query(`DROP INDEX "public"."IDX_corrections_item"`);
await queryRunner.query(`DROP TABLE "corrections"`);
// Drop ENUMs
await queryRunner.query(`DROP TYPE "public"."validation_requests_status_enum"`);
await queryRunner.query(`DROP TYPE "public"."product_submissions_status_enum"`);
await queryRunner.query(`DROP TYPE "public"."ground_truth_status_enum"`);
await queryRunner.query(`DROP TYPE "public"."corrections_type_enum"`);
}
}