feat: 完成P6-P8后端能力与P9路由重构(V1-V2,D1,D3,B1-B2,B4,I1-I4,P9A2-P9A3)

这个提交包含在:
2026-02-10 11:28:28 +08:00
父节点 bec21e1b38
当前提交 6bc09f557b
共修改 33 个文件,包含 3467 行新增和 13 行删除
@@ -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;