feat: 完成P6-P8后端能力与P9路由重构(V1-V2,D1,D3,B1-B2,B4,I1-I4,P9A2-P9A3)
这个提交包含在:
@@ -136,6 +136,8 @@ model Department {
|
||||
reimbursement_forms ReimbursementForm[]
|
||||
leave_requests LeaveRequest[]
|
||||
workflow_instances WorkflowInstance[]
|
||||
budget_pools BudgetPool[]
|
||||
travel_orders TravelOrder[]
|
||||
|
||||
@@index([parent_id])
|
||||
@@map("departments")
|
||||
@@ -191,6 +193,8 @@ model Employee {
|
||||
owned_invoices InvoicePool[] @relation("InvoiceOwner")
|
||||
uploaded_invoices InvoicePool[] @relation("InvoiceUploader")
|
||||
overridden_invoices InvoicePool[] @relation("InvoiceOverrideBy")
|
||||
requested_invoice_verifications InvoiceVerificationRecord[] @relation("InvoiceVerificationRequester")
|
||||
travel_orders TravelOrder[]
|
||||
|
||||
workflow_instances_started WorkflowInstance[] @relation("WorkflowInstanceSubmitter")
|
||||
workflow_history_actions WorkflowHistory[] @relation("WorkflowHistoryActor")
|
||||
@@ -383,6 +387,8 @@ model ReimbursementForm {
|
||||
invoice_pool_items InvoicePool[]
|
||||
workflow_instances WorkflowInstance[]
|
||||
approval_transfers ApprovalTransfer[]
|
||||
budget_ledger_entries BudgetLedgerEntry[]
|
||||
travel_orders TravelOrder[]
|
||||
|
||||
@@index([owner_id])
|
||||
@@index([submitter_id])
|
||||
@@ -514,6 +520,7 @@ model InvoicePool {
|
||||
ocr_result OcrResult? @relation(fields: [ocr_result_id], references: [id], onDelete: SetNull)
|
||||
linked_reimbursement ReimbursementForm? @relation(fields: [linked_reimbursement_id], references: [id], onDelete: SetNull)
|
||||
linked_item ReimbursementItem? @relation(fields: [linked_item_id], references: [id], onDelete: SetNull)
|
||||
verification_records InvoiceVerificationRecord[]
|
||||
|
||||
@@index([owner_employee_id])
|
||||
@@index([uploaded_by_employee_id])
|
||||
@@ -526,6 +533,150 @@ model InvoicePool {
|
||||
@@map("invoice_pool")
|
||||
}
|
||||
|
||||
model InvoiceVerificationRecord {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
invoice_id String @db.Uuid
|
||||
provider String @db.VarChar(32)
|
||||
status String @db.VarChar(16)
|
||||
request_payload Json?
|
||||
result_payload Json?
|
||||
result_code String? @db.VarChar(64)
|
||||
result_message String?
|
||||
error_code String? @db.VarChar(64)
|
||||
error_message String?
|
||||
duration_ms Int?
|
||||
requested_by String? @db.Uuid
|
||||
requested_at DateTime @default(now())
|
||||
completed_at DateTime?
|
||||
retry_of_id String? @db.Uuid
|
||||
attempts Int @default(1)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
invoice InvoicePool @relation(fields: [invoice_id], references: [id], onDelete: Cascade)
|
||||
requester Employee? @relation("InvoiceVerificationRequester", fields: [requested_by], references: [id], onDelete: SetNull)
|
||||
retry_of InvoiceVerificationRecord? @relation("InvoiceVerificationRetry", fields: [retry_of_id], references: [id], onDelete: SetNull)
|
||||
retries InvoiceVerificationRecord[] @relation("InvoiceVerificationRetry")
|
||||
|
||||
@@index([invoice_id, created_at], map: "idx_invoice_verification_invoice_created_at")
|
||||
@@index([status, requested_at], map: "idx_invoice_verification_status_requested_at")
|
||||
@@index([requested_by])
|
||||
@@map("invoice_verification_records")
|
||||
}
|
||||
|
||||
model BudgetPool {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
code String @unique
|
||||
name String
|
||||
department_id String? @db.Uuid
|
||||
status String @default("active") @db.VarChar(16)
|
||||
period_start DateTime @db.Date
|
||||
period_end DateTime @db.Date
|
||||
allocated_amount Decimal @db.Decimal(14, 2)
|
||||
used_amount Decimal @default(0) @db.Decimal(14, 2)
|
||||
warning_threshold_percent Int @default(80)
|
||||
archived_at DateTime?
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
|
||||
ledger_entries BudgetLedgerEntry[]
|
||||
|
||||
@@index([department_id, status], map: "idx_budget_pools_department_status")
|
||||
@@index([status, period_start, period_end], map: "idx_budget_pools_period")
|
||||
@@map("budget_pools")
|
||||
}
|
||||
|
||||
model BudgetLedgerEntry {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
budget_pool_id String @db.Uuid
|
||||
reimbursement_id String? @db.Uuid
|
||||
kind String @db.VarChar(32)
|
||||
amount Decimal @db.Decimal(14, 2)
|
||||
note String?
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
|
||||
budget_pool BudgetPool @relation(fields: [budget_pool_id], references: [id], onDelete: Cascade)
|
||||
reimbursement_form ReimbursementForm? @relation(fields: [reimbursement_id], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([budget_pool_id, created_at], map: "idx_budget_ledger_pool_created_at")
|
||||
@@index([reimbursement_id])
|
||||
@@map("budget_ledger_entries")
|
||||
}
|
||||
|
||||
model IntegrationOutbox {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
event_type String @db.VarChar(64)
|
||||
status String @default("pending") @db.VarChar(16)
|
||||
payload Json
|
||||
dedupe_key String? @db.VarChar(128)
|
||||
attempts Int @default(0)
|
||||
max_attempts Int @default(5)
|
||||
last_error String?
|
||||
next_retry_at DateTime?
|
||||
processed_at DateTime?
|
||||
dead_letter_at DateTime?
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
@@unique([event_type, dedupe_key], map: "integration_outbox_event_dedupe_ux")
|
||||
@@index([status, next_retry_at, created_at], map: "idx_integration_outbox_queue")
|
||||
@@map("integration_outbox")
|
||||
}
|
||||
|
||||
model TravelOrder {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
source String @default("manual") @db.VarChar(32)
|
||||
external_order_no String
|
||||
employee_id String @db.Uuid
|
||||
department_id String? @db.Uuid
|
||||
linked_reimbursement_id String? @db.Uuid
|
||||
occurred_at DateTime? @db.Date
|
||||
amount Decimal? @db.Decimal(14, 2)
|
||||
currency String @default("CNY") @db.VarChar(8)
|
||||
status String @default("imported") @db.VarChar(20)
|
||||
raw_payload Json?
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
|
||||
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
|
||||
linked_reimbursement ReimbursementForm? @relation(fields: [linked_reimbursement_id], references: [id], onDelete: SetNull)
|
||||
|
||||
@@unique([source, external_order_no], map: "travel_orders_source_order_ux")
|
||||
@@index([employee_id, occurred_at], map: "idx_travel_orders_employee_occurred")
|
||||
@@index([linked_reimbursement_id])
|
||||
@@map("travel_orders")
|
||||
}
|
||||
|
||||
model NotificationChannel {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
channel_type String @db.VarChar(32)
|
||||
name String
|
||||
enabled Boolean @default(true)
|
||||
config_json Json
|
||||
template_json Json?
|
||||
|
||||
created_at DateTime @default(now())
|
||||
created_by String? @db.Uuid
|
||||
updated_at DateTime @updatedAt
|
||||
updated_by String? @db.Uuid
|
||||
|
||||
@@unique([channel_type, name], map: "notification_channels_type_name_ux")
|
||||
@@index([enabled, channel_type], map: "idx_notification_channels_enabled_type")
|
||||
@@map("notification_channels")
|
||||
}
|
||||
|
||||
model WorkflowDefinition {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
|
||||
在新工单中引用
屏蔽一个用户