import { MigrationInterface, QueryRunner } from "typeorm"; export class CreateFeedbackTables1736502000000 implements MigrationInterface { name = 'CreateFeedbackTables1736502000000' public async up(queryRunner: QueryRunner): Promise { // 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 { // 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"`); } }