GuDingZiChan/backend/prisma/schema.prisma
2026-03-23 11:54:41 +08:00

572 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model AssetType {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(50)
short_name String? @db.VarChar(20)
status String @default("active") @db.VarChar(20)
can_be_parent Boolean @default(false) // 可作为父资产
can_be_child Boolean @default(false) // 可作为子资产
created_at DateTime @default(now())
updated_at DateTime @updatedAt
equipments Equipment[]
inventoryTasks InventoryTask[]
inventoryTaskItems InventoryTaskItem[]
@@index([status])
@@index([short_name])
@@map("asset_type")
}
model Department {
id Int @id @default(autoincrement())
name String @db.VarChar(100)
status String @default("active") @db.VarChar(20)
parent_id Int? // 父部门ID,null表示根部门
source_group_id Int? @unique
source_parent_group_id Int?
source_system String? @db.VarChar(30)
last_synced_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
// 自关联 - 部门层级
parent Department? @relation("DepartmentHierarchy", fields: [parent_id], references: [id])
children Department[] @relation("DepartmentHierarchy")
equipments Equipment[]
mainPersons Person[] @relation("PersonMainDepartment")
personDepartments PersonDepartment[]
inventoryTasks InventoryTask[]
inventoryTaskParticipants InventoryTaskParticipant[]
inventoryTaskItems InventoryTaskItem[] @relation("InventoryTaskItemSnapshotDepartment")
@@index([status])
@@index([parent_id])
@@index([source_system])
@@index([last_synced_at])
@@map("department")
}
model Equipment {
id Int @id @default(autoincrement())
equipment_code String @unique @db.VarChar(50)
equipment_name String @db.VarChar(100)
equipment_model String? @db.VarChar(100)
equipment_type String @db.VarChar(50)
code_generation_mode String @default("auto") @db.VarChar(20)
status String @default("可用") @db.VarChar(20)
total_quantity Int @default(0)
remaining_quantity Int @default(0)
unit_price Decimal @default(0.00) @db.Decimal(10, 2)
total_value Decimal @default(0.00) @db.Decimal(12, 2)
borrowed_value Decimal @default(0.00) @db.Decimal(12, 2)
available_value Decimal @default(0.00) @db.Decimal(12, 2)
description String? @db.Text
purchase_date DateTime? @db.Date
supplier String? @db.VarChar(100)
storage_location_id Int?
asset_type_id Int?
department_id Int?
// 父子资产关联字段
parent_id Int? // 父资产ID(如果已挂载)
mount_status String @default("none") @db.VarChar(20) // none/mounted/unmounted
total_value_with_children Decimal @default(0.00) @db.Decimal(12, 2) // 含配件总价值
created_at DateTime @default(now())
updated_at DateTime @updatedAt
borrow_records BorrowRecord[]
storageLocation StorageLocation? @relation(fields: [storage_location_id], references: [id])
assetType AssetType? @relation(fields: [asset_type_id], references: [id])
department Department? @relation(fields: [department_id], references: [id])
// 父子资产自关联
parent Equipment? @relation("ParentChild", fields: [parent_id], references: [id])
children Equipment[] @relation("ParentChild")
// 挂载历史关联
mountHistories MountHistory[]
// 报废记录关联
disposalRecords DisposalRecord[]
// 维修记录关联
maintenanceRecords MaintenanceRecord[]
inventoryTaskItems InventoryTaskItem[]
@@index([equipment_code])
@@index([equipment_type])
@@index([status])
@@index([code_generation_mode])
@@index([unit_price])
@@index([storage_location_id])
@@index([asset_type_id])
@@index([department_id])
@@index([parent_id])
@@index([mount_status])
@@map("equipment")
}
model BorrowRecord {
id Int @id @default(autoincrement())
equipment_id Int
equipment_code String @db.VarChar(50)
person_id Int
person_name String @db.VarChar(50)
person_nickname String @db.VarChar(50)
person_identifier String? @db.VarChar(50)
borrow_time DateTime @default(now())
expected_return_time DateTime?
is_long_term Boolean @default(false)
return_time DateTime?
status String @default("已借出") @db.VarChar(20)
return_condition String @default("正常") @db.VarChar(20)
notes String? @db.Text
children_snapshot String? @db.Text // JSON格式的配件快照
created_at DateTime @default(now())
updated_at DateTime @updatedAt
equipment Equipment @relation(fields: [equipment_id], references: [id])
person Person @relation(fields: [person_id], references: [id])
@@index([equipment_id])
@@index([person_id])
@@index([status])
@@index([borrow_time])
@@map("borrow_record")
}
model User {
id Int @id @default(autoincrement())
username String @unique @db.VarChar(50)
password String @db.VarChar(255)
name String @db.VarChar(50)
email String? @db.VarChar(100)
phone String? @db.VarChar(20)
role String @default("user") @db.VarChar(20)
status String @default("active") @db.VarChar(20)
last_login_at DateTime? @db.DateTime
created_at DateTime @default(now())
updated_at DateTime @updatedAt
operation_logs OperationLog[]
site_configs SiteConfig[] @relation("SiteConfigUpdatedBy")
mount_histories MountHistory[]
// 报废记录关联
disposal_applied DisposalRecord[] @relation("DisposalApplier")
disposal_approved DisposalRecord[] @relation("DisposalApprover")
// 维修记录关联
maintenance_records MaintenanceRecord[]
notifications UserNotification[]
processed_sync_changes SyncChange[] @relation("SyncChangeProcessedBy")
inventory_tasks_created InventoryTask[] @relation("InventoryTaskCreatedBy")
inventory_actions InventoryTaskAction[] @relation("InventoryTaskActionOperator")
@@index([role])
@@index([status])
@@map("user")
}
model SiteConfig {
id Int @id @default(autoincrement())
site_name String @db.VarChar(100)
logo_url String? @db.VarChar(255)
favicon_url String? @db.VarChar(255)
theme_color String? @db.VarChar(20)
updated_by Int?
created_at DateTime @default(now())
updated_at DateTime @default(now())
updatedBy User? @relation("SiteConfigUpdatedBy", fields: [updated_by], references: [id])
@@map("site_config")
}
model Person {
id Int @id @default(autoincrement())
nickname String @db.VarChar(50)
real_name String @db.VarChar(50)
id_number String? @unique @db.VarChar(50)
department String? @db.VarChar(100)
main_department_id Int?
position String? @db.VarChar(100)
phone String? @db.VarChar(20)
email String? @db.VarChar(100)
status String @default("active") @db.VarChar(20)
remarks String? @db.Text
source_userid String? @unique @db.VarChar(100)
source_system String? @db.VarChar(30)
source_main_department_id Int?
source_dept_ids String? @db.Text
source_department_name String? @db.VarChar(100)
source_main_department_name String? @db.VarChar(100)
department_sync_bucket String? @db.VarChar(20)
manager_source_userid String? @db.VarChar(100)
source_employee_status String? @db.VarChar(30)
last_synced_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
borrow_records BorrowRecord[]
mainDepartment Department? @relation("PersonMainDepartment", fields: [main_department_id], references: [id])
personDepartments PersonDepartment[]
inventoryTaskParticipants InventoryTaskParticipant[]
inventoryTaskItems InventoryTaskItem[] @relation("InventoryTaskItemSnapshotPerson")
@@index([nickname])
@@index([real_name])
@@index([status])
@@index([main_department_id])
@@index([source_system])
@@index([department_sync_bucket])
@@index([last_synced_at])
@@map("person")
}
model PersonDepartment {
person_id Int
department_id Int
created_at DateTime @default(now())
updated_at DateTime @updatedAt
person Person @relation(fields: [person_id], references: [id], onDelete: Cascade)
department Department @relation(fields: [department_id], references: [id], onDelete: Cascade)
@@id([person_id, department_id])
@@index([department_id])
@@map("person_department")
}
model StorageLocation {
id Int @id @default(autoincrement())
name String @db.VarChar(100)
code String? @db.VarChar(50)
status String @default("active") @db.VarChar(20)
description String? @db.Text
created_at DateTime @default(now())
updated_at DateTime @updatedAt
equipments Equipment[]
@@index([status])
@@map("storage_location")
}
model OperationLog {
id Int @id @default(autoincrement())
user_id Int?
username String? @db.VarChar(50)
operation_type String @db.VarChar(50)
equipment_id Int?
equipment_code String? @db.VarChar(50)
operation_details String? @db.Text
ip_address String? @db.VarChar(50)
created_at DateTime @default(now())
user User? @relation(fields: [user_id], references: [id])
@@index([user_id])
@@index([operation_type])
@@index([created_at])
@@map("operation_log")
}
model SyncJob {
id Int @id @default(autoincrement())
sync_type String @db.VarChar(30)
trigger_mode String @db.VarChar(30)
status String @default("success") @db.VarChar(20)
operator String? @db.VarChar(50)
summary String? @db.Text
result_payload String? @db.Text
created_at DateTime @default(now())
changes SyncChange[]
notifications Notification[]
@@index([sync_type, created_at])
@@index([status, created_at])
@@map("sync_job")
}
model SyncChange {
id Int @id @default(autoincrement())
sync_job_id Int
entity_type String @db.VarChar(20)
entity_key String? @db.VarChar(100)
entity_name String @db.VarChar(100)
change_type String @db.VarChar(50)
title String @db.VarChar(150)
detail String? @db.Text
before_snapshot String? @db.Text
after_snapshot String? @db.Text
processing_status String @default("pending") @db.VarChar(20)
processing_notes String? @db.Text
processed_by Int?
processed_at DateTime?
created_at DateTime @default(now())
syncJob SyncJob @relation(fields: [sync_job_id], references: [id], onDelete: Cascade)
processedBy User? @relation("SyncChangeProcessedBy", fields: [processed_by], references: [id], onDelete: SetNull)
@@index([sync_job_id])
@@index([entity_type, change_type])
@@index([processing_status, created_at])
@@index([processed_by])
@@map("sync_change")
}
model Notification {
id Int @id @default(autoincrement())
title String @db.VarChar(150)
content String? @db.Text
type String @default("info") @db.VarChar(30)
level String @default("info") @db.VarChar(20)
action_url String? @db.VarChar(255)
related_sync_job_id Int?
metadata String? @db.Text
created_at DateTime @default(now())
syncJob SyncJob? @relation(fields: [related_sync_job_id], references: [id], onDelete: SetNull)
recipients UserNotification[]
@@index([related_sync_job_id])
@@index([created_at])
@@map("notification")
}
model UserNotification {
id Int @id @default(autoincrement())
user_id Int
notification_id Int
is_read Boolean @default(false)
read_at DateTime?
created_at DateTime @default(now())
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
notification Notification @relation(fields: [notification_id], references: [id], onDelete: Cascade)
@@unique([user_id, notification_id])
@@index([user_id, is_read, created_at])
@@map("user_notification")
}
model InventoryTask {
id Int @id @default(autoincrement())
title String @db.VarChar(150)
description String? @db.Text
status String @default("active") @db.VarChar(20)
public_token String @unique @db.VarChar(120)
scope_department_id Int?
scope_department_name String? @db.VarChar(100)
scope_asset_type_id Int?
scope_asset_type_name String? @db.VarChar(100)
scope_snapshot_json String? @db.Text
participants_count Int @default(0)
items_count Int @default(0)
completed_count Int @default(0)
exception_count Int @default(0)
unassigned_count Int @default(0)
created_by Int
closed_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
scopeDepartment Department? @relation(fields: [scope_department_id], references: [id])
scopeAssetType AssetType? @relation(fields: [scope_asset_type_id], references: [id])
creator User @relation("InventoryTaskCreatedBy", fields: [created_by], references: [id])
participants InventoryTaskParticipant[]
items InventoryTaskItem[]
actions InventoryTaskAction[]
@@index([status, created_at])
@@index([scope_department_id, status])
@@index([scope_asset_type_id, status])
@@index([created_by])
@@map("inventory_task")
}
model InventoryTaskParticipant {
id Int @id @default(autoincrement())
task_id Int
person_id Int?
person_name String @db.VarChar(50)
person_nickname String @db.VarChar(50)
person_source_userid String? @db.VarChar(100)
department_id Int?
department_name String? @db.VarChar(100)
status String @default("pending") @db.VarChar(20)
confirmed_at DateTime?
last_action_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
task InventoryTask @relation(fields: [task_id], references: [id], onDelete: Cascade)
person Person? @relation(fields: [person_id], references: [id], onDelete: SetNull)
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull)
assigned_items InventoryTaskItem[] @relation("InventoryTaskItemAssignee")
checked_items InventoryTaskItem[] @relation("InventoryTaskItemChecker")
actions InventoryTaskAction[]
@@unique([task_id, person_id])
@@index([task_id, status])
@@index([task_id, person_id])
@@index([task_id, person_nickname])
@@index([department_id])
@@map("inventory_task_participant")
}
model InventoryTaskItem {
id Int @id @default(autoincrement())
task_id Int
participant_id Int?
equipment_id Int
equipment_code String @db.VarChar(50)
equipment_name String @db.VarChar(100)
equipment_model String? @db.VarChar(100)
equipment_type String @db.VarChar(50)
asset_type_id Int?
asset_type_name String? @db.VarChar(100)
snapshot_department_id Int?
snapshot_department_name String? @db.VarChar(100)
snapshot_person_id Int?
snapshot_person_name String? @db.VarChar(50)
snapshot_person_nickname String? @db.VarChar(50)
snapshot_status String? @db.VarChar(20)
source_borrow_record_id Int?
is_unassigned Boolean @default(false)
check_status String @default("pending") @db.VarChar(20)
checked_by_participant_id Int?
checked_at DateTime?
exception_type String? @db.VarChar(30)
exception_note String? @db.Text
created_at DateTime @default(now())
updated_at DateTime @updatedAt
task InventoryTask @relation(fields: [task_id], references: [id], onDelete: Cascade)
participant InventoryTaskParticipant? @relation("InventoryTaskItemAssignee", fields: [participant_id], references: [id], onDelete: SetNull)
checked_by_participant InventoryTaskParticipant? @relation("InventoryTaskItemChecker", fields: [checked_by_participant_id], references: [id], onDelete: SetNull)
equipment Equipment @relation(fields: [equipment_id], references: [id], onDelete: Cascade)
assetType AssetType? @relation(fields: [asset_type_id], references: [id], onDelete: SetNull)
snapshotDepartment Department? @relation("InventoryTaskItemSnapshotDepartment", fields: [snapshot_department_id], references: [id], onDelete: SetNull)
snapshotPerson Person? @relation("InventoryTaskItemSnapshotPerson", fields: [snapshot_person_id], references: [id], onDelete: SetNull)
actions InventoryTaskAction[]
@@unique([task_id, equipment_id])
@@index([task_id, participant_id, check_status])
@@index([task_id, equipment_id])
@@index([task_id, is_unassigned])
@@index([task_id, check_status])
@@index([checked_by_participant_id])
@@index([snapshot_department_id])
@@index([snapshot_person_id])
@@map("inventory_task_item")
}
model InventoryTaskAction {
id Int @id @default(autoincrement())
task_id Int
participant_id Int?
item_id Int?
action_type String @db.VarChar(40)
action_payload String? @db.Text
ip_address String? @db.VarChar(50)
user_agent String? @db.VarChar(255)
operator_user_id Int?
created_at DateTime @default(now())
task InventoryTask @relation(fields: [task_id], references: [id], onDelete: Cascade)
participant InventoryTaskParticipant? @relation(fields: [participant_id], references: [id], onDelete: SetNull)
item InventoryTaskItem? @relation(fields: [item_id], references: [id], onDelete: SetNull)
operator User? @relation("InventoryTaskActionOperator", fields: [operator_user_id], references: [id], onDelete: SetNull)
@@index([task_id, created_at])
@@index([participant_id])
@@index([item_id])
@@index([operator_user_id])
@@map("inventory_task_action")
}
model MountHistory {
id Int @id @default(autoincrement())
child_id Int // 子资产ID
parent_id Int? // 父资产ID
operation_type String @db.VarChar(20) // mount/unmount/replace
operated_at DateTime @default(now())
operated_by Int // 操作人ID
notes String? @db.Text
old_parent_id Int? // 更换时的旧父资产ID
new_parent_id Int? // 更换时的新父资产ID
child Equipment @relation(fields: [child_id], references: [id])
operator User @relation(fields: [operated_by], references: [id])
@@index([child_id])
@@index([parent_id])
@@index([operation_type])
@@index([operated_at])
@@map("mount_history")
}
// 报废记录
model DisposalRecord {
id Int @id @default(autoincrement())
equipment_id Int
equipment_code String @db.VarChar(50)
disposal_reason String @db.Text // 报废原因
disposal_method String @db.VarChar(50) // 处置方式:报废/转让/捐赠
estimated_value Decimal @db.Decimal(12,2) // 残值估价
actual_value Decimal? @db.Decimal(12,2) // 实际处置价值
status String @default("pending") @db.VarChar(20) // pending/approved/rejected/completed
applied_by Int // 申请人
applied_at DateTime @default(now())
approved_by Int? // 审批人
approved_at DateTime?
completed_at DateTime? // 完成时间
notes String? @db.Text
created_at DateTime @default(now())
updated_at DateTime @updatedAt
equipment Equipment @relation(fields: [equipment_id], references: [id])
applier User @relation("DisposalApplier", fields: [applied_by], references: [id])
approver User? @relation("DisposalApprover", fields: [approved_by], references: [id])
@@index([equipment_id])
@@index([status])
@@map("disposal_record")
}
// 维修记录
model MaintenanceRecord {
id Int @id @default(autoincrement())
equipment_id Int
equipment_code String @db.VarChar(50)
issue_description String @db.Text // 故障描述
repair_vendor String? @db.VarChar(100) // 维修商
estimated_cost Decimal @db.Decimal(10,2) // 预估费用
actual_cost Decimal? @db.Decimal(10,2) // 实际费用
start_date DateTime @default(now()) // 送修日期
expected_end_date DateTime? // 预计完成日期
actual_end_date DateTime? // 实际完成日期
status String @default("in_progress") @db.VarChar(20) // in_progress/completed/cancelled
repair_result String? @db.Text // 维修结果
created_by Int
created_at DateTime @default(now())
updated_at DateTime @updatedAt
equipment Equipment @relation(fields: [equipment_id], references: [id])
creator User @relation(fields: [created_by], references: [id])
@@index([equipment_id])
@@index([status])
@@map("maintenance_record")
}