feat: 完成P6-P8后端能力与P9路由重构(V1-V2,D1,D3,B1-B2,B4,I1-I4,P9A2-P9A3)
这个提交包含在:
@@ -0,0 +1,163 @@
|
||||
-- P6-P8 remaining work baseline tables
|
||||
|
||||
CREATE TABLE "invoice_verification_records" (
|
||||
"id" UUID NOT NULL,
|
||||
"invoice_id" UUID NOT NULL,
|
||||
"provider" VARCHAR(32) NOT NULL,
|
||||
"status" VARCHAR(16) NOT NULL,
|
||||
"request_payload" JSONB,
|
||||
"result_payload" JSONB,
|
||||
"result_code" VARCHAR(64),
|
||||
"result_message" TEXT,
|
||||
"error_code" VARCHAR(64),
|
||||
"error_message" TEXT,
|
||||
"duration_ms" INTEGER,
|
||||
"requested_by" UUID,
|
||||
"requested_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"completed_at" TIMESTAMP(3),
|
||||
"retry_of_id" UUID,
|
||||
"attempts" INTEGER NOT NULL DEFAULT 1,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "invoice_verification_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "budget_pools" (
|
||||
"id" UUID NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"department_id" UUID,
|
||||
"status" VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
"period_start" DATE NOT NULL,
|
||||
"period_end" DATE NOT NULL,
|
||||
"allocated_amount" DECIMAL(14,2) NOT NULL,
|
||||
"used_amount" DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||
"warning_threshold_percent" INTEGER NOT NULL DEFAULT 80,
|
||||
"archived_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
CONSTRAINT "budget_pools_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "budget_ledger_entries" (
|
||||
"id" UUID NOT NULL,
|
||||
"budget_pool_id" UUID NOT NULL,
|
||||
"reimbursement_id" UUID,
|
||||
"kind" VARCHAR(32) NOT NULL,
|
||||
"amount" DECIMAL(14,2) NOT NULL,
|
||||
"note" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
CONSTRAINT "budget_ledger_entries_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "integration_outbox" (
|
||||
"id" UUID NOT NULL,
|
||||
"event_type" VARCHAR(64) NOT NULL,
|
||||
"status" VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
"payload" JSONB NOT NULL,
|
||||
"dedupe_key" VARCHAR(128),
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"max_attempts" INTEGER NOT NULL DEFAULT 5,
|
||||
"last_error" TEXT,
|
||||
"next_retry_at" TIMESTAMP(3),
|
||||
"processed_at" TIMESTAMP(3),
|
||||
"dead_letter_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
CONSTRAINT "integration_outbox_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "travel_orders" (
|
||||
"id" UUID NOT NULL,
|
||||
"source" VARCHAR(32) NOT NULL DEFAULT 'manual',
|
||||
"external_order_no" TEXT NOT NULL,
|
||||
"employee_id" UUID NOT NULL,
|
||||
"department_id" UUID,
|
||||
"linked_reimbursement_id" UUID,
|
||||
"occurred_at" DATE,
|
||||
"amount" DECIMAL(14,2),
|
||||
"currency" VARCHAR(8) NOT NULL DEFAULT 'CNY',
|
||||
"status" VARCHAR(20) NOT NULL DEFAULT 'imported',
|
||||
"raw_payload" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
CONSTRAINT "travel_orders_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "notification_channels" (
|
||||
"id" UUID NOT NULL,
|
||||
"channel_type" VARCHAR(32) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"config_json" JSONB NOT NULL,
|
||||
"template_json" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
CONSTRAINT "notification_channels_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "budget_pools_code_key" ON "budget_pools"("code");
|
||||
CREATE UNIQUE INDEX "integration_outbox_event_dedupe_ux" ON "integration_outbox"("event_type", "dedupe_key");
|
||||
CREATE UNIQUE INDEX "travel_orders_source_order_ux" ON "travel_orders"("source", "external_order_no");
|
||||
CREATE UNIQUE INDEX "notification_channels_type_name_ux" ON "notification_channels"("channel_type", "name");
|
||||
|
||||
CREATE INDEX "idx_invoice_verification_invoice_created_at" ON "invoice_verification_records"("invoice_id", "created_at");
|
||||
CREATE INDEX "idx_invoice_verification_status_requested_at" ON "invoice_verification_records"("status", "requested_at");
|
||||
CREATE INDEX "invoice_verification_records_requested_by_idx" ON "invoice_verification_records"("requested_by");
|
||||
|
||||
CREATE INDEX "idx_budget_pools_department_status" ON "budget_pools"("department_id", "status");
|
||||
CREATE INDEX "idx_budget_pools_period" ON "budget_pools"("status", "period_start", "period_end");
|
||||
CREATE INDEX "idx_budget_ledger_pool_created_at" ON "budget_ledger_entries"("budget_pool_id", "created_at");
|
||||
CREATE INDEX "budget_ledger_entries_reimbursement_id_idx" ON "budget_ledger_entries"("reimbursement_id");
|
||||
|
||||
CREATE INDEX "idx_integration_outbox_queue" ON "integration_outbox"("status", "next_retry_at", "created_at");
|
||||
|
||||
CREATE INDEX "idx_travel_orders_employee_occurred" ON "travel_orders"("employee_id", "occurred_at");
|
||||
CREATE INDEX "travel_orders_linked_reimbursement_id_idx" ON "travel_orders"("linked_reimbursement_id");
|
||||
|
||||
CREATE INDEX "idx_notification_channels_enabled_type" ON "notification_channels"("enabled", "channel_type");
|
||||
|
||||
ALTER TABLE "invoice_verification_records"
|
||||
ADD CONSTRAINT "invoice_verification_records_invoice_id_fkey"
|
||||
FOREIGN KEY ("invoice_id") REFERENCES "invoice_pool"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "invoice_verification_records"
|
||||
ADD CONSTRAINT "invoice_verification_records_requested_by_fkey"
|
||||
FOREIGN KEY ("requested_by") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "invoice_verification_records"
|
||||
ADD CONSTRAINT "invoice_verification_records_retry_of_id_fkey"
|
||||
FOREIGN KEY ("retry_of_id") REFERENCES "invoice_verification_records"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "budget_pools"
|
||||
ADD CONSTRAINT "budget_pools_department_id_fkey"
|
||||
FOREIGN KEY ("department_id") REFERENCES "departments"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "budget_ledger_entries"
|
||||
ADD CONSTRAINT "budget_ledger_entries_budget_pool_id_fkey"
|
||||
FOREIGN KEY ("budget_pool_id") REFERENCES "budget_pools"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "budget_ledger_entries"
|
||||
ADD CONSTRAINT "budget_ledger_entries_reimbursement_id_fkey"
|
||||
FOREIGN KEY ("reimbursement_id") REFERENCES "reimbursement_forms"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "travel_orders"
|
||||
ADD CONSTRAINT "travel_orders_employee_id_fkey"
|
||||
FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "travel_orders"
|
||||
ADD CONSTRAINT "travel_orders_department_id_fkey"
|
||||
FOREIGN KEY ("department_id") REFERENCES "departments"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "travel_orders"
|
||||
ADD CONSTRAINT "travel_orders_linked_reimbursement_id_fkey"
|
||||
FOREIGN KEY ("linked_reimbursement_id") REFERENCES "reimbursement_forms"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
在新工单中引用
屏蔽一个用户