feat(server): 工作流扩展 leave 实体骨架(F24)
- workflow_instances 解耦 reimbursement FK:新增 reimbursement_id/leave_request_id + 回填迁移 - 新增 leave_requests 表与 /api/leave-requests 最小链路(create/submit) - workflow approve/reject/return 支持 leave 状态更新;transfer 非报销任务禁用 - seed-config 增加 leave workflow definition;更新 v3 CSV 状态推进
这个提交包含在:
@@ -0,0 +1,56 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "LeaveRequestStatus" AS ENUM ('draft', 'reviewing', 'approved', 'rejected', 'cancelled');
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "workflow_instances" DROP CONSTRAINT "workflow_instances_entity_id_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "workflow_instances" ADD COLUMN "leave_request_id" UUID,
|
||||
ADD COLUMN "reimbursement_id" UUID;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "leave_requests" (
|
||||
"id" UUID NOT NULL,
|
||||
"owner_id" UUID NOT NULL,
|
||||
"department_id" UUID NOT NULL,
|
||||
"start_date" DATE NOT NULL,
|
||||
"end_date" DATE NOT NULL,
|
||||
"reason" TEXT,
|
||||
"status" "LeaveRequestStatus" NOT NULL DEFAULT 'draft',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_by" UUID,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" UUID,
|
||||
|
||||
CONSTRAINT "leave_requests_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "leave_requests_owner_id_idx" ON "leave_requests"("owner_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "leave_requests_department_id_idx" ON "leave_requests"("department_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "leave_requests_status_idx" ON "leave_requests"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_leave_owner_status_updated_at" ON "leave_requests"("owner_id", "status", "updated_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "workflow_instances_reimbursement_id_idx" ON "workflow_instances"("reimbursement_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "workflow_instances_leave_request_id_idx" ON "workflow_instances"("leave_request_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "leave_requests" ADD CONSTRAINT "leave_requests_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "leave_requests" ADD CONSTRAINT "leave_requests_department_id_fkey" FOREIGN KEY ("department_id") REFERENCES "departments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "workflow_instances" ADD CONSTRAINT "workflow_instances_reimbursement_id_fkey" FOREIGN KEY ("reimbursement_id") REFERENCES "reimbursement_forms"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "workflow_instances" ADD CONSTRAINT "workflow_instances_leave_request_id_fkey" FOREIGN KEY ("leave_request_id") REFERENCES "leave_requests"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
-- Backfill workflow_instances.*_id columns for existing rows.
|
||||
-- Reimbursement instances historically used entity_id as FK target.
|
||||
UPDATE "workflow_instances"
|
||||
SET reimbursement_id = entity_id
|
||||
WHERE entity_type = 'reimbursement'
|
||||
AND reimbursement_id IS NULL;
|
||||
|
||||
UPDATE "workflow_instances"
|
||||
SET leave_request_id = entity_id
|
||||
WHERE entity_type = 'leave'
|
||||
AND leave_request_id IS NULL;
|
||||
|
||||
+42
-1
@@ -36,6 +36,14 @@ enum ReimbursementStatus {
|
||||
cancelled
|
||||
}
|
||||
|
||||
enum LeaveRequestStatus {
|
||||
draft
|
||||
reviewing
|
||||
approved
|
||||
rejected
|
||||
cancelled
|
||||
}
|
||||
|
||||
enum InvoicePoolStatus {
|
||||
pending_ocr
|
||||
ocr_failed
|
||||
@@ -126,6 +134,7 @@ model Department {
|
||||
|
||||
employees Employee[]
|
||||
reimbursement_forms ReimbursementForm[]
|
||||
leave_requests LeaveRequest[]
|
||||
workflow_instances WorkflowInstance[]
|
||||
|
||||
@@index([parent_id])
|
||||
@@ -177,6 +186,7 @@ model Employee {
|
||||
submitted_reimbursements ReimbursementForm[] @relation("ReimbursementSubmitter")
|
||||
owned_reimbursements ReimbursementForm[] @relation("ReimbursementOwner")
|
||||
paid_reimbursements ReimbursementForm[] @relation("ReimbursementPaidBy")
|
||||
leave_requests LeaveRequest[]
|
||||
|
||||
owned_invoices InvoicePool[] @relation("InvoiceOwner")
|
||||
uploaded_invoices InvoicePool[] @relation("InvoiceUploader")
|
||||
@@ -408,6 +418,32 @@ model ReimbursementItem {
|
||||
@@map("reimbursement_items")
|
||||
}
|
||||
|
||||
model LeaveRequest {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
owner_id String @db.Uuid
|
||||
department_id String @db.Uuid
|
||||
start_date DateTime @db.Date
|
||||
end_date DateTime @db.Date
|
||||
reason String?
|
||||
status LeaveRequestStatus @default(draft)
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
owner Employee @relation(fields: [owner_id], references: [id], onDelete: Restrict)
|
||||
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
|
||||
|
||||
workflow_instances WorkflowInstance[]
|
||||
|
||||
@@index([owner_id])
|
||||
@@index([department_id])
|
||||
@@index([status])
|
||||
@@index([owner_id, status, updated_at], map: "idx_leave_owner_status_updated_at")
|
||||
@@map("leave_requests")
|
||||
}
|
||||
|
||||
model OcrResult {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
file_sha256 String
|
||||
@@ -513,6 +549,8 @@ model WorkflowInstance {
|
||||
workflow_id String @db.Uuid
|
||||
entity_type String
|
||||
entity_id String @db.Uuid
|
||||
reimbursement_id String? @db.Uuid
|
||||
leave_request_id String? @db.Uuid
|
||||
submitter_id String @db.Uuid
|
||||
current_status WorkflowInstanceStatus @default(pending)
|
||||
current_step_id String
|
||||
@@ -526,7 +564,8 @@ model WorkflowInstance {
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
workflow_definition WorkflowDefinition @relation(fields: [workflow_id], references: [id], onDelete: Restrict)
|
||||
reimbursement_form ReimbursementForm @relation(fields: [entity_id], references: [id], onDelete: Cascade)
|
||||
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
|
||||
leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: Cascade)
|
||||
submitter Employee @relation("WorkflowInstanceSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
|
||||
department Department? @relation(fields: [context_department_id], references: [id], onDelete: SetNull)
|
||||
|
||||
@@ -538,6 +577,8 @@ model WorkflowInstance {
|
||||
|
||||
@@index([workflow_id])
|
||||
@@index([entity_type, entity_id])
|
||||
@@index([reimbursement_id])
|
||||
@@index([leave_request_id])
|
||||
@@index([current_status])
|
||||
@@index([submitter_id])
|
||||
@@index([context_department_id])
|
||||
|
||||
在新工单中引用
屏蔽一个用户