WARCH1 实现企微审批最小历史留存

这个提交包含在:
2026-05-20 00:22:27 +08:00
父节点 56a8e2bb0c
当前提交 5cd960cf90
共修改 28 个文件,包含 3081 行新增和 261 行删除
@@ -0,0 +1,257 @@
CREATE TABLE "wecom_approval_archives" (
"id" UUID NOT NULL,
"corp_id_hash" VARCHAR(64) NOT NULL,
"sp_no" VARCHAR(160) NOT NULL,
"template_id" VARCHAR(160),
"template_type" VARCHAR(40) NOT NULL DEFAULT 'unknown',
"sp_name" VARCHAR(160),
"normalized_status" VARCHAR(32) NOT NULL DEFAULT 'unknown',
"wecom_status" VARCHAR(40),
"applicant_employee_id" UUID,
"applicant_external_user_id" VARCHAR(128),
"submitted_at" TIMESTAMP(3),
"applied_at" TIMESTAMP(3),
"completed_at" TIMESTAMP(3),
"status_observed_at" TIMESTAMP(3),
"last_callback_at" TIMESTAMP(3),
"last_compensation_at" TIMESTAMP(3),
"last_source" VARCHAR(40),
"last_detail_digest" VARCHAR(64),
"readability_status" VARCHAR(40),
"single_tap_approve_risk" BOOLEAN NOT NULL DEFAULT false,
"template_adjustment_required" BOOLEAN NOT NULL DEFAULT false,
"external_config_status" VARCHAR(40),
"source_summary" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_archives_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_participants" (
"id" UUID NOT NULL,
"archive_id" UUID NOT NULL,
"role" VARCHAR(32) NOT NULL,
"node_index" INTEGER NOT NULL DEFAULT 0,
"external_user_id" VARCHAR(128) NOT NULL,
"employee_id" UUID,
"is_current" BOOLEAN NOT NULL DEFAULT false,
"mapping_status" VARCHAR(32) NOT NULL DEFAULT 'unknown',
"display_summary" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_participants_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_actions" (
"id" UUID NOT NULL,
"archive_id" UUID NOT NULL,
"action_type" VARCHAR(40) NOT NULL,
"operator_external_user_id" VARCHAR(128),
"operator_employee_id" UUID,
"action_time" TIMESTAMP(3),
"node_index" INTEGER NOT NULL DEFAULT 0,
"comment_summary" TEXT,
"source" VARCHAR(40) NOT NULL,
"idempotency_key" VARCHAR(96) NOT NULL,
"evidence_digest" VARCHAR(64),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_actions_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_field_summaries" (
"id" UUID NOT NULL,
"archive_id" UUID NOT NULL,
"field_key" VARCHAR(120) NOT NULL,
"field_index" INTEGER NOT NULL DEFAULT 0,
"field_name" VARCHAR(160),
"field_type" VARCHAR(80),
"normalized_value" TEXT,
"value_preview" TEXT,
"sensitive_level" VARCHAR(32) NOT NULL DEFAULT 'normal',
"display_allowed" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_field_summaries_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_attachment_summaries" (
"id" UUID NOT NULL,
"archive_id" UUID NOT NULL,
"attachment_index" INTEGER NOT NULL DEFAULT 0,
"attachment_type" VARCHAR(80),
"external_ref_summary" TEXT,
"downloaded" BOOLEAN NOT NULL DEFAULT false,
"sensitive_level" VARCHAR(32) NOT NULL DEFAULT 'restricted',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_attachment_summaries_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_callback_events" (
"id" UUID NOT NULL,
"archive_id" UUID,
"corp_id_hash" VARCHAR(64) NOT NULL,
"sp_no" VARCHAR(160) NOT NULL,
"template_id" VARCHAR(160),
"event_type" VARCHAR(80) NOT NULL,
"callback_kind" VARCHAR(80) NOT NULL,
"approval_type" VARCHAR(40) NOT NULL DEFAULT 'unknown',
"normalized_status" VARCHAR(32) NOT NULL DEFAULT 'unknown',
"wecom_status" VARCHAR(40),
"received_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"event_time" TIMESTAMP(3),
"apply_time" TIMESTAMP(3),
"processing_status" VARCHAR(40) NOT NULL DEFAULT 'accepted',
"processing_error_code" VARCHAR(80),
"processing_error_message" TEXT,
"retry_count" INTEGER NOT NULL DEFAULT 0,
"idempotency_key" VARCHAR(96) NOT NULL,
"event_digest" VARCHAR(64) NOT NULL,
"source_summary" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_callback_events_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_compensation_runs" (
"id" UUID NOT NULL,
"run_key" VARCHAR(64),
"mode" VARCHAR(32) NOT NULL DEFAULT 'live_read',
"status" VARCHAR(32) NOT NULL DEFAULT 'running',
"start_time" TIMESTAMP(3) NOT NULL,
"end_time" TIMESTAMP(3) NOT NULL,
"template_filter" JSONB,
"cursor_summary" TEXT,
"page_size" INTEGER NOT NULL DEFAULT 100,
"page_count" INTEGER NOT NULL DEFAULT 0,
"item_count" INTEGER NOT NULL DEFAULT 0,
"detail_count" INTEGER NOT NULL DEFAULT 0,
"finding_count" INTEGER NOT NULL DEFAULT 0,
"error_code" VARCHAR(80),
"error_message" TEXT,
"started_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"finished_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_compensation_runs_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_compensation_items" (
"id" UUID NOT NULL,
"run_id" UUID NOT NULL,
"archive_id" UUID,
"sp_no" VARCHAR(160) NOT NULL,
"template_id" VARCHAR(160),
"template_type" VARCHAR(40) NOT NULL DEFAULT 'unknown',
"normalized_status" VARCHAR(32) NOT NULL DEFAULT 'unknown',
"detail_status" VARCHAR(40) NOT NULL DEFAULT 'pending',
"detail_digest" VARCHAR(64),
"error_code" VARCHAR(80),
"error_message" TEXT,
"source_summary" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_compensation_items_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_approval_reconcile_findings" (
"id" UUID NOT NULL,
"archive_id" UUID,
"run_id" UUID,
"finding_type" VARCHAR(80) NOT NULL,
"status" VARCHAR(32) NOT NULL DEFAULT 'open',
"severity" VARCHAR(32) NOT NULL DEFAULT 'info',
"evidence_digest" VARCHAR(64) NOT NULL,
"source" VARCHAR(40) NOT NULL,
"description" TEXT,
"detected_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"resolved_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_approval_reconcile_findings_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "wecom_approval_archives_corp_sp_no_ux" ON "wecom_approval_archives"("corp_id_hash", "sp_no");
CREATE INDEX "idx_wecom_approval_arch_applicant_status_at" ON "wecom_approval_archives"("applicant_employee_id", "status_observed_at");
CREATE INDEX "idx_wecom_approval_arch_type_status" ON "wecom_approval_archives"("template_type", "normalized_status");
CREATE INDEX "idx_wecom_approval_arch_last_source" ON "wecom_approval_archives"("last_source");
CREATE UNIQUE INDEX "wecom_approval_participants_node_user_ux" ON "wecom_approval_participants"("archive_id", "role", "node_index", "external_user_id");
CREATE INDEX "idx_wecom_approval_part_employee_role" ON "wecom_approval_participants"("employee_id", "role");
CREATE INDEX "idx_wecom_approval_part_external_user" ON "wecom_approval_participants"("external_user_id");
CREATE UNIQUE INDEX "wecom_approval_actions_archive_key_ux" ON "wecom_approval_actions"("archive_id", "idempotency_key");
CREATE INDEX "idx_wecom_approval_actions_operator_time" ON "wecom_approval_actions"("operator_employee_id", "action_time");
CREATE INDEX "idx_wecom_approval_actions_type_time" ON "wecom_approval_actions"("action_type", "action_time");
CREATE UNIQUE INDEX "wecom_approval_fields_archive_key_ux" ON "wecom_approval_field_summaries"("archive_id", "field_key", "field_index");
CREATE INDEX "idx_wecom_approval_fields_key" ON "wecom_approval_field_summaries"("field_key");
CREATE INDEX "idx_wecom_approval_attach_archive_idx" ON "wecom_approval_attachment_summaries"("archive_id", "attachment_index");
CREATE UNIQUE INDEX "wecom_approval_callback_events_idempotency_key_key" ON "wecom_approval_callback_events"("idempotency_key");
CREATE INDEX "idx_wecom_approval_callbacks_sp_no" ON "wecom_approval_callback_events"("sp_no");
CREATE INDEX "idx_wecom_approval_callbacks_archive_at" ON "wecom_approval_callback_events"("archive_id", "received_at");
CREATE INDEX "idx_wecom_approval_callbacks_status_at" ON "wecom_approval_callback_events"("processing_status", "received_at");
CREATE INDEX "idx_wecom_approval_comp_runs_key" ON "wecom_approval_compensation_runs"("run_key");
CREATE INDEX "idx_wecom_approval_comp_runs_status" ON "wecom_approval_compensation_runs"("status", "started_at");
CREATE INDEX "idx_wecom_approval_comp_runs_window" ON "wecom_approval_compensation_runs"("start_time", "end_time");
CREATE UNIQUE INDEX "wecom_approval_comp_items_run_sp_no_ux" ON "wecom_approval_compensation_items"("run_id", "sp_no");
CREATE INDEX "idx_wecom_approval_comp_items_archive" ON "wecom_approval_compensation_items"("archive_id");
CREATE INDEX "idx_wecom_approval_comp_items_status" ON "wecom_approval_compensation_items"("detail_status");
CREATE UNIQUE INDEX "wecom_approval_findings_archive_digest_ux" ON "wecom_approval_reconcile_findings"("archive_id", "finding_type", "evidence_digest");
CREATE UNIQUE INDEX "wecom_approval_findings_run_digest_ux" ON "wecom_approval_reconcile_findings"("run_id", "finding_type", "evidence_digest");
CREATE INDEX "idx_wecom_approval_findings_status_at" ON "wecom_approval_reconcile_findings"("status", "detected_at");
CREATE INDEX "idx_wecom_approval_findings_run" ON "wecom_approval_reconcile_findings"("run_id");
ALTER TABLE "wecom_approval_archives"
ADD CONSTRAINT "wecom_approval_archives_applicant_employee_id_fkey"
FOREIGN KEY ("applicant_employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_participants"
ADD CONSTRAINT "wecom_approval_participants_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_participants"
ADD CONSTRAINT "wecom_approval_participants_employee_id_fkey"
FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_actions"
ADD CONSTRAINT "wecom_approval_actions_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_actions"
ADD CONSTRAINT "wecom_approval_actions_operator_employee_id_fkey"
FOREIGN KEY ("operator_employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_field_summaries"
ADD CONSTRAINT "wecom_approval_field_summaries_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_attachment_summaries"
ADD CONSTRAINT "wecom_approval_attachment_summaries_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_callback_events"
ADD CONSTRAINT "wecom_approval_callback_events_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_compensation_items"
ADD CONSTRAINT "wecom_approval_compensation_items_run_id_fkey"
FOREIGN KEY ("run_id") REFERENCES "wecom_approval_compensation_runs"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_compensation_items"
ADD CONSTRAINT "wecom_approval_compensation_items_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_reconcile_findings"
ADD CONSTRAINT "wecom_approval_reconcile_findings_archive_id_fkey"
FOREIGN KEY ("archive_id") REFERENCES "wecom_approval_archives"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "wecom_approval_reconcile_findings"
ADD CONSTRAINT "wecom_approval_reconcile_findings_run_id_fkey"
FOREIGN KEY ("run_id") REFERENCES "wecom_approval_compensation_runs"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+414 -163
查看文件
@@ -542,43 +542,43 @@ model Department {
parent Department? @relation("DepartmentToParent", fields: [parent_id], references: [id], onDelete: Restrict)
children Department[] @relation("DepartmentToParent")
employees Employee[]
reimbursement_forms ReimbursementForm[]
leave_requests LeaveRequest[]
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
employees Employee[]
reimbursement_forms ReimbursementForm[]
leave_requests LeaveRequest[]
purchase_requests PurchaseRequest[]
travel_requests TravelRequest[]
attendance_corrections AttendanceCorrection[]
workflow_instances WorkflowInstance[]
budget_pools BudgetPool[]
travel_orders TravelOrder[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
workflow_instances WorkflowInstance[]
budget_pools BudgetPool[]
travel_orders TravelOrder[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
@@index([parent_id])
@@map("departments")
}
model Employee {
id String @id @default(uuid()) @db.Uuid
employee_no String @unique @default(dbgenerated("generate_employee_number()"))
name String
phone String @unique
email String @unique
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
title String
direct_manager_id String? @db.Uuid
hire_date DateTime @db.Date
status EmployeeStatus @default(active)
employment_status EmploymentLifecycleStatus @default(active)
probation_end_date DateTime? @db.Date
last_working_date DateTime? @db.Date
work_location String?
emergency_contact_name String?
emergency_contact_phone String?
notes String?
avatar_path String?
notification_push_enabled Boolean @default(true)
id String @id @default(uuid()) @db.Uuid
employee_no String @unique @default(dbgenerated("generate_employee_number()"))
name String
phone String @unique
email String @unique
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
title String
direct_manager_id String? @db.Uuid
hire_date DateTime @db.Date
status EmployeeStatus @default(active)
employment_status EmploymentLifecycleStatus @default(active)
probation_end_date DateTime? @db.Date
last_working_date DateTime? @db.Date
work_location String?
emergency_contact_name String?
emergency_contact_phone String?
notes String?
avatar_path String?
notification_push_enabled Boolean @default(true)
bank_account_encrypted String
bank_name String
@@ -611,31 +611,34 @@ model Employee {
audit_logs AuditLog[] @relation("AuditActor")
notifications Notification[]
submitted_reimbursements ReimbursementForm[] @relation("ReimbursementSubmitter")
owned_reimbursements ReimbursementForm[] @relation("ReimbursementOwner")
paid_reimbursements ReimbursementForm[] @relation("ReimbursementPaidBy")
reimbursement_attachments_uploaded ReimbursementAttachment[] @relation("ReimbursementAttachmentUploader")
payment_batch_receipts_uploaded PaymentBatchReceiptAttachment[] @relation("PaymentBatchReceiptAttachmentUploader")
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
wecom_checkin_records WecomCheckinRecord[]
wecom_checkin_sync_runs WecomCheckinSyncRun[] @relation("WecomCheckinSyncRunTrigger")
wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunEmployee")
triggered_wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunTrigger")
wecom_hr_master_sync_items WecomHrMasterSyncItem[] @relation("WecomHrMasterSyncItemEmployee")
triggered_wecom_hr_master_sync_runs WecomHrMasterSyncRun[] @relation("WecomHrMasterSyncRunTrigger")
attendance_corrections AttendanceCorrection[]
owned_cost_centers CostCenter[] @relation("CostCenterOwner")
purchase_requests_applicant PurchaseRequest[] @relation("PurchaseRequestApplicant")
travel_requests_applicant TravelRequest[] @relation("TravelRequestApplicant")
travel_requests_submitted TravelRequest[] @relation("TravelRequestSubmitter")
purchase_requests_submitted PurchaseRequest[] @relation("PurchaseRequestSubmitter")
purchase_attachments_uploaded PurchaseAttachment[] @relation("PurchaseAttachmentUploader")
purchase_order_archives PurchaseOrderArchive[] @relation("PurchaseOrderArchiveArchivedBy")
assigned_asset_cards AssetCard[] @relation("AssetCardAssignee")
asset_assignments AssetAssignment[]
asset_transfers_from AssetTransfer[] @relation("AssetTransferFromEmployee")
asset_transfers_to AssetTransfer[] @relation("AssetTransferToEmployee")
submitted_reimbursements ReimbursementForm[] @relation("ReimbursementSubmitter")
owned_reimbursements ReimbursementForm[] @relation("ReimbursementOwner")
paid_reimbursements ReimbursementForm[] @relation("ReimbursementPaidBy")
reimbursement_attachments_uploaded ReimbursementAttachment[] @relation("ReimbursementAttachmentUploader")
payment_batch_receipts_uploaded PaymentBatchReceiptAttachment[] @relation("PaymentBatchReceiptAttachmentUploader")
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
wecom_checkin_records WecomCheckinRecord[]
wecom_checkin_sync_runs WecomCheckinSyncRun[] @relation("WecomCheckinSyncRunTrigger")
wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunEmployee")
triggered_wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunTrigger")
wecom_hr_master_sync_items WecomHrMasterSyncItem[] @relation("WecomHrMasterSyncItemEmployee")
triggered_wecom_hr_master_sync_runs WecomHrMasterSyncRun[] @relation("WecomHrMasterSyncRunTrigger")
wecom_approval_archives_applicant WecomApprovalArchive[] @relation("WecomApprovalArchiveApplicant")
wecom_approval_participants WecomApprovalParticipant[] @relation("WecomApprovalParticipantEmployee")
wecom_approval_actions_operator WecomApprovalAction[] @relation("WecomApprovalActionOperator")
attendance_corrections AttendanceCorrection[]
owned_cost_centers CostCenter[] @relation("CostCenterOwner")
purchase_requests_applicant PurchaseRequest[] @relation("PurchaseRequestApplicant")
travel_requests_applicant TravelRequest[] @relation("TravelRequestApplicant")
travel_requests_submitted TravelRequest[] @relation("TravelRequestSubmitter")
purchase_requests_submitted PurchaseRequest[] @relation("PurchaseRequestSubmitter")
purchase_attachments_uploaded PurchaseAttachment[] @relation("PurchaseAttachmentUploader")
purchase_order_archives PurchaseOrderArchive[] @relation("PurchaseOrderArchiveArchivedBy")
assigned_asset_cards AssetCard[] @relation("AssetCardAssignee")
asset_assignments AssetAssignment[]
asset_transfers_from AssetTransfer[] @relation("AssetTransferFromEmployee")
asset_transfers_to AssetTransfer[] @relation("AssetTransferToEmployee")
owned_invoices InvoicePool[] @relation("InvoiceOwner")
uploaded_invoices InvoicePool[] @relation("InvoiceUploader")
@@ -802,17 +805,17 @@ model Notification {
}
model NotificationDelivery {
id String @id @default(uuid()) @db.Uuid
notification_id String @db.Uuid
channel_id String @db.Uuid
status NotificationDeliveryStatus
attempt_no Int
result_code String? @db.VarChar(64)
error_message String?
response_body Json?
attempted_at DateTime @default(now())
id String @id @default(uuid()) @db.Uuid
notification_id String @db.Uuid
channel_id String @db.Uuid
status NotificationDeliveryStatus
attempt_no Int
result_code String? @db.VarChar(64)
error_message String?
response_body Json?
attempted_at DateTime @default(now())
notification Notification @relation(fields: [notification_id], references: [id], onDelete: Cascade)
notification Notification @relation(fields: [notification_id], references: [id], onDelete: Cascade)
channel NotificationChannel @relation(fields: [channel_id], references: [id], onDelete: Cascade)
@@index([notification_id, attempted_at], map: "idx_notification_deliveries_notification_attempted")
@@ -845,15 +848,15 @@ model ExpenseType {
}
model ExpenseStandard {
id String @id @default(uuid()) @db.Uuid
expense_type_id String @db.Uuid
name String @db.VarChar(100)
max_amount Decimal @db.Decimal(14, 2)
per_unit String @default("item") @db.VarChar(20)
control_mode String @default("warn") @db.VarChar(16)
scope_type String @default("global") @db.VarChar(32)
scope_key String? @db.Uuid
enabled Boolean @default(true)
id String @id @default(uuid()) @db.Uuid
expense_type_id String @db.Uuid
name String @db.VarChar(100)
max_amount Decimal @db.Decimal(14, 2)
per_unit String @default("item") @db.VarChar(20)
control_mode String @default("warn") @db.VarChar(16)
scope_type String @default("global") @db.VarChar(32)
scope_key String? @db.Uuid
enabled Boolean @default(true)
note String?
created_at DateTime @default(now())
@@ -1299,33 +1302,33 @@ model InventoryCheckItem {
}
model ReimbursementForm {
id String @id @default(uuid()) @db.Uuid
form_number String? @unique
submitter_id String @db.Uuid
owner_id String @db.Uuid
department_id String @db.Uuid
reimbursement_date DateTime @db.Date
total_amount Decimal @default(0) @db.Decimal(14, 2)
status ReimbursementStatus @default(draft)
description String?
payment_voucher_no String? @db.VarChar(50)
payment_date DateTime? @db.Date
payment_channel String? @db.VarChar(20)
paid_at DateTime?
paid_by String? @db.Uuid
project_id String? @db.Uuid
linked_travel_request_id String? @db.Uuid
id String @id @default(uuid()) @db.Uuid
form_number String? @unique
submitter_id String @db.Uuid
owner_id String @db.Uuid
department_id String @db.Uuid
reimbursement_date DateTime @db.Date
total_amount Decimal @default(0) @db.Decimal(14, 2)
status ReimbursementStatus @default(draft)
description String?
payment_voucher_no String? @db.VarChar(50)
payment_date DateTime? @db.Date
payment_channel String? @db.VarChar(20)
paid_at DateTime?
paid_by String? @db.Uuid
project_id String? @db.Uuid
linked_travel_request_id String? @db.Uuid
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
submitter Employee @relation("ReimbursementSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
owner Employee @relation("ReimbursementOwner", fields: [owner_id], references: [id], onDelete: Restrict)
paid_by_employee Employee? @relation("ReimbursementPaidBy", fields: [paid_by], references: [id], onDelete: SetNull)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
submitter Employee @relation("ReimbursementSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
owner Employee @relation("ReimbursementOwner", fields: [owner_id], references: [id], onDelete: Restrict)
paid_by_employee Employee? @relation("ReimbursementPaidBy", fields: [paid_by], references: [id], onDelete: SetNull)
department Department @relation(fields: [department_id], references: [id], onDelete: Restrict)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
travel_request TravelRequest? @relation(fields: [linked_travel_request_id], references: [id], onDelete: SetNull)
items ReimbursementItem[]
@@ -1762,7 +1765,7 @@ model PaymentBatch {
updated_at DateTime @updatedAt
updated_by String? @db.Uuid
items PaymentBatchItem[]
items PaymentBatchItem[]
receipt_attachments PaymentBatchReceiptAttachment[]
@@index([entity_type, status, created_at], map: "idx_payment_batches_entity_status_created")
@@ -1907,18 +1910,18 @@ model EmployeePresence {
}
model AttendanceRecord {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
work_date DateTime @db.Date
clock_in_at DateTime?
clock_out_at DateTime?
work_minutes Int?
is_late Boolean @default(false)
late_minutes Int @default(0)
is_early_leave Boolean @default(false)
early_minutes Int @default(0)
note String?
source String @default("web") @db.VarChar(16)
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
work_date DateTime @db.Date
clock_in_at DateTime?
clock_out_at DateTime?
work_minutes Int?
is_late Boolean @default(false)
late_minutes Int @default(0)
is_early_leave Boolean @default(false)
early_minutes Int @default(0)
note String?
source String @default("web") @db.VarChar(16)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
@@ -1961,7 +1964,7 @@ model WecomCheckinSyncRun {
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
trigger Employee? @relation("WecomCheckinSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
trigger Employee? @relation("WecomCheckinSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
records WecomCheckinRecord[]
@@index([status, created_at], map: "idx_wecom_checkin_sync_runs_status_created")
@@ -2010,7 +2013,7 @@ model WecomDirectorySyncRun {
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation("WecomDirectorySyncRunEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
employee Employee @relation("WecomDirectorySyncRunEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
trigger Employee? @relation("WecomDirectorySyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
@@index([employee_id, created_at], map: "idx_wecom_directory_sync_runs_employee_created")
@@ -2115,23 +2118,23 @@ model WecomIdentityBinding {
}
model WecomHrMasterSyncRun {
id String @id @default(uuid()) @db.Uuid
mode String @db.VarChar(16)
status String @default("running") @db.VarChar(24)
token_source Json?
requested_userids Json?
limit Int?
summary Json?
error_code String? @db.VarChar(80)
error_message String?
triggered_by String? @db.Uuid
started_at DateTime @default(now())
finished_at DateTime?
id String @id @default(uuid()) @db.Uuid
mode String @db.VarChar(16)
status String @default("running") @db.VarChar(24)
token_source Json?
requested_userids Json?
limit Int?
summary Json?
error_code String? @db.VarChar(80)
error_message String?
triggered_by String? @db.Uuid
started_at DateTime @default(now())
finished_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
trigger Employee? @relation("WecomHrMasterSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
trigger Employee? @relation("WecomHrMasterSyncRunTrigger", fields: [triggered_by], references: [id], onDelete: SetNull)
items WecomHrMasterSyncItem[]
@@index([status, created_at], map: "idx_wecom_hr_master_sync_runs_status_created")
@@ -2165,6 +2168,254 @@ model WecomHrMasterSyncItem {
@@map("wecom_hr_master_sync_items")
}
model WecomApprovalArchive {
id String @id @default(uuid()) @db.Uuid
corp_id_hash String @db.VarChar(64)
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
template_type String @default("unknown") @db.VarChar(40)
sp_name String? @db.VarChar(160)
normalized_status String @default("unknown") @db.VarChar(32)
wecom_status String? @db.VarChar(40)
applicant_employee_id String? @db.Uuid
applicant_external_user_id String? @db.VarChar(128)
submitted_at DateTime?
applied_at DateTime?
completed_at DateTime?
status_observed_at DateTime?
last_callback_at DateTime?
last_compensation_at DateTime?
last_source String? @db.VarChar(40)
last_detail_digest String? @db.VarChar(64)
readability_status String? @db.VarChar(40)
single_tap_approve_risk Boolean @default(false)
template_adjustment_required Boolean @default(false)
external_config_status String? @db.VarChar(40)
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
applicant Employee? @relation("WecomApprovalArchiveApplicant", fields: [applicant_employee_id], references: [id], onDelete: SetNull)
participants WecomApprovalParticipant[]
actions WecomApprovalAction[]
field_summaries WecomApprovalFieldSummary[]
attachment_summaries WecomApprovalAttachmentSummary[]
callback_events WecomApprovalCallbackEvent[]
compensation_items WecomApprovalCompensationItem[]
reconcile_findings WecomApprovalReconcileFinding[]
@@unique([corp_id_hash, sp_no], map: "wecom_approval_archives_corp_sp_no_ux")
@@index([applicant_employee_id, status_observed_at], map: "idx_wecom_approval_arch_applicant_status_at")
@@index([template_type, normalized_status], map: "idx_wecom_approval_arch_type_status")
@@index([last_source], map: "idx_wecom_approval_arch_last_source")
@@map("wecom_approval_archives")
}
model WecomApprovalParticipant {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
role String @db.VarChar(32)
node_index Int @default(0)
external_user_id String @db.VarChar(128)
employee_id String? @db.Uuid
is_current Boolean @default(false)
mapping_status String @default("unknown") @db.VarChar(32)
display_summary String?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
employee Employee? @relation("WecomApprovalParticipantEmployee", fields: [employee_id], references: [id], onDelete: SetNull)
@@unique([archive_id, role, node_index, external_user_id], map: "wecom_approval_participants_node_user_ux")
@@index([employee_id, role], map: "idx_wecom_approval_part_employee_role")
@@index([external_user_id], map: "idx_wecom_approval_part_external_user")
@@map("wecom_approval_participants")
}
model WecomApprovalAction {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
action_type String @db.VarChar(40)
operator_external_user_id String? @db.VarChar(128)
operator_employee_id String? @db.Uuid
action_time DateTime?
node_index Int @default(0)
comment_summary String?
source String @db.VarChar(40)
idempotency_key String @db.VarChar(96)
evidence_digest String? @db.VarChar(64)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
operator Employee? @relation("WecomApprovalActionOperator", fields: [operator_employee_id], references: [id], onDelete: SetNull)
@@unique([archive_id, idempotency_key], map: "wecom_approval_actions_archive_key_ux")
@@index([operator_employee_id, action_time], map: "idx_wecom_approval_actions_operator_time")
@@index([action_type, action_time], map: "idx_wecom_approval_actions_type_time")
@@map("wecom_approval_actions")
}
model WecomApprovalFieldSummary {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
field_key String @db.VarChar(120)
field_index Int @default(0)
field_name String? @db.VarChar(160)
field_type String? @db.VarChar(80)
normalized_value String?
value_preview String?
sensitive_level String @default("normal") @db.VarChar(32)
display_allowed Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
@@unique([archive_id, field_key, field_index], map: "wecom_approval_fields_archive_key_ux")
@@index([field_key], map: "idx_wecom_approval_fields_key")
@@map("wecom_approval_field_summaries")
}
model WecomApprovalAttachmentSummary {
id String @id @default(uuid()) @db.Uuid
archive_id String @db.Uuid
attachment_index Int @default(0)
attachment_type String? @db.VarChar(80)
external_ref_summary String?
downloaded Boolean @default(false)
sensitive_level String @default("restricted") @db.VarChar(32)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive @relation(fields: [archive_id], references: [id], onDelete: Cascade)
@@index([archive_id, attachment_index], map: "idx_wecom_approval_attach_archive_idx")
@@map("wecom_approval_attachment_summaries")
}
model WecomApprovalCallbackEvent {
id String @id @default(uuid()) @db.Uuid
archive_id String? @db.Uuid
corp_id_hash String @db.VarChar(64)
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
event_type String @db.VarChar(80)
callback_kind String @db.VarChar(80)
approval_type String @default("unknown") @db.VarChar(40)
normalized_status String @default("unknown") @db.VarChar(32)
wecom_status String? @db.VarChar(40)
received_at DateTime @default(now())
event_time DateTime?
apply_time DateTime?
processing_status String @default("accepted") @db.VarChar(40)
processing_error_code String? @db.VarChar(80)
processing_error_message String?
retry_count Int @default(0)
idempotency_key String @unique @db.VarChar(96)
event_digest String @db.VarChar(64)
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
@@index([sp_no], map: "idx_wecom_approval_callbacks_sp_no")
@@index([archive_id, received_at], map: "idx_wecom_approval_callbacks_archive_at")
@@index([processing_status, received_at], map: "idx_wecom_approval_callbacks_status_at")
@@map("wecom_approval_callback_events")
}
model WecomApprovalCompensationRun {
id String @id @default(uuid()) @db.Uuid
run_key String? @db.VarChar(64)
mode String @default("live_read") @db.VarChar(32)
status String @default("running") @db.VarChar(32)
start_time DateTime
end_time DateTime
template_filter Json?
cursor_summary String?
page_size Int @default(100)
page_count Int @default(0)
item_count Int @default(0)
detail_count Int @default(0)
finding_count Int @default(0)
error_code String? @db.VarChar(80)
error_message String?
started_at DateTime @default(now())
finished_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
items WecomApprovalCompensationItem[]
findings WecomApprovalReconcileFinding[]
@@index([run_key], map: "idx_wecom_approval_comp_runs_key")
@@index([status, started_at], map: "idx_wecom_approval_comp_runs_status")
@@index([start_time, end_time], map: "idx_wecom_approval_comp_runs_window")
@@map("wecom_approval_compensation_runs")
}
model WecomApprovalCompensationItem {
id String @id @default(uuid()) @db.Uuid
run_id String @db.Uuid
archive_id String? @db.Uuid
sp_no String @db.VarChar(160)
template_id String? @db.VarChar(160)
template_type String @default("unknown") @db.VarChar(40)
normalized_status String @default("unknown") @db.VarChar(32)
detail_status String @default("pending") @db.VarChar(40)
detail_digest String? @db.VarChar(64)
error_code String? @db.VarChar(80)
error_message String?
source_summary Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
run WecomApprovalCompensationRun @relation(fields: [run_id], references: [id], onDelete: Cascade)
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
@@unique([run_id, sp_no], map: "wecom_approval_comp_items_run_sp_no_ux")
@@index([archive_id], map: "idx_wecom_approval_comp_items_archive")
@@index([detail_status], map: "idx_wecom_approval_comp_items_status")
@@map("wecom_approval_compensation_items")
}
model WecomApprovalReconcileFinding {
id String @id @default(uuid()) @db.Uuid
archive_id String? @db.Uuid
run_id String? @db.Uuid
finding_type String @db.VarChar(80)
status String @default("open") @db.VarChar(32)
severity String @default("info") @db.VarChar(32)
evidence_digest String @db.VarChar(64)
source String @db.VarChar(40)
description String?
detected_at DateTime @default(now())
resolved_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
archive WecomApprovalArchive? @relation(fields: [archive_id], references: [id], onDelete: SetNull)
run WecomApprovalCompensationRun? @relation(fields: [run_id], references: [id], onDelete: SetNull)
@@unique([archive_id, finding_type, evidence_digest], map: "wecom_approval_findings_archive_digest_ux")
@@unique([run_id, finding_type, evidence_digest], map: "wecom_approval_findings_run_digest_ux")
@@index([status, detected_at], map: "idx_wecom_approval_findings_status_at")
@@index([run_id], map: "idx_wecom_approval_findings_run")
@@map("wecom_approval_reconcile_findings")
}
model Project {
id String @id @default(uuid()) @db.Uuid
project_no String @unique
@@ -2519,10 +2770,10 @@ model Payslip {
}
model PayslipItem {
id String @id @default(uuid()) @db.Uuid
payslip_id String @db.Uuid
salary_item_id String @db.Uuid
amount Decimal @default(0) @db.Decimal(12, 2)
id String @id @default(uuid()) @db.Uuid
payslip_id String @db.Uuid
salary_item_id String @db.Uuid
amount Decimal @default(0) @db.Decimal(12, 2)
payslip Payslip @relation(fields: [payslip_id], references: [id], onDelete: Cascade)
salary_item SalaryItem @relation(fields: [salary_item_id], references: [id], onDelete: Restrict)
@@ -2555,11 +2806,11 @@ model PerformanceReviewCycle {
}
model PerformanceReview {
id String @id @default(uuid()) @db.Uuid
cycle_id String @db.Uuid
employee_id String @db.Uuid
reviewer_employee_id String @db.Uuid
overall_score Decimal @db.Decimal(3, 1)
id String @id @default(uuid()) @db.Uuid
cycle_id String @db.Uuid
employee_id String @db.Uuid
reviewer_employee_id String @db.Uuid
overall_score Decimal @db.Decimal(3, 1)
comment String?
created_at DateTime @default(now())
@@ -2567,9 +2818,9 @@ model PerformanceReview {
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
cycle PerformanceReviewCycle @relation(fields: [cycle_id], references: [id], onDelete: Cascade)
employee Employee @relation("PerformanceReviewEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
reviewer_employee Employee @relation("PerformanceReviewReviewer", fields: [reviewer_employee_id], references: [id], onDelete: Restrict)
cycle PerformanceReviewCycle @relation(fields: [cycle_id], references: [id], onDelete: Cascade)
employee Employee @relation("PerformanceReviewEmployee", fields: [employee_id], references: [id], onDelete: Cascade)
reviewer_employee Employee @relation("PerformanceReviewReviewer", fields: [reviewer_employee_id], references: [id], onDelete: Restrict)
dimensions PerformanceReviewDimension[]
@@unique([cycle_id, employee_id], map: "performance_reviews_cycle_employee_ux")
@@ -2762,13 +3013,13 @@ model TravelRequest {
budget_pool BudgetPool? @relation(fields: [budget_pool_id], references: [id], onDelete: SetNull)
project Project? @relation(fields: [project_id], references: [id], onDelete: SetNull)
segments TravelRequestSegment[]
travel_orders TravelOrder[]
segments TravelRequestSegment[]
travel_orders TravelOrder[]
linked_reimbursements ReimbursementForm[]
reconciliation_items TravelReconciliationItem[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
workflow_instances WorkflowInstance[]
reconciliation_items TravelReconciliationItem[]
card_authorizations CardAuthorization[]
card_transactions CardTransaction[]
workflow_instances WorkflowInstance[]
@@index([applicant_employee_id, start_date], map: "idx_travel_requests_applicant_start")
@@index([submitter_id], map: "idx_travel_requests_submitter")
@@ -3034,35 +3285,35 @@ model WorkflowDefinition {
}
model WorkflowInstance {
id String @id @default(uuid()) @db.Uuid
workflow_id String @db.Uuid
entity_type String
entity_id String @db.Uuid
reimbursement_id String? @db.Uuid
leave_request_id String? @db.Uuid
purchase_request_id String? @db.Uuid
travel_request_id String? @db.Uuid
attendance_correction_id String? @db.Uuid
submitter_id String @db.Uuid
current_status WorkflowInstanceStatus @default(pending)
current_step_id String
context_data Json
started_at DateTime @default(now())
completed_at DateTime?
id String @id @default(uuid()) @db.Uuid
workflow_id String @db.Uuid
entity_type String
entity_id String @db.Uuid
reimbursement_id String? @db.Uuid
leave_request_id String? @db.Uuid
purchase_request_id String? @db.Uuid
travel_request_id String? @db.Uuid
attendance_correction_id String? @db.Uuid
submitter_id String @db.Uuid
current_status WorkflowInstanceStatus @default(pending)
current_step_id String
context_data Json
started_at DateTime @default(now())
completed_at DateTime?
created_at DateTime @default(now())
created_by String? @db.Uuid
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
workflow_definition WorkflowDefinition @relation(fields: [workflow_id], references: [id], onDelete: Restrict)
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: Cascade)
purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
workflow_definition WorkflowDefinition @relation(fields: [workflow_id], references: [id], onDelete: Restrict)
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: Cascade)
leave_request LeaveRequest? @relation(fields: [leave_request_id], references: [id], onDelete: Cascade)
purchase_request PurchaseRequest? @relation(fields: [purchase_request_id], references: [id], onDelete: Cascade)
travel_request TravelRequest? @relation(fields: [travel_request_id], references: [id], onDelete: Cascade)
attendance_correction AttendanceCorrection? @relation(fields: [attendance_correction_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)
submitter Employee @relation("WorkflowInstanceSubmitter", fields: [submitter_id], references: [id], onDelete: Restrict)
department Department? @relation(fields: [context_department_id], references: [id], onDelete: SetNull)
workflow_tasks WorkflowTask[]
workflow_history WorkflowHistory[]