// 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[] disposalItems DisposalItem[] // 维修记录关联 maintenanceRecords MaintenanceRecord[] inventoryTaskItems InventoryTaskItem[] transferRootRecords AssetTransferRecord[] @relation("AssetTransferRoot") transferItems AssetTransferItem[] @@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) person_id Int? @unique 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") disposal_completed DisposalRecord[] @relation("DisposalCompleter") disposal_actions DisposalAction[] @relation("DisposalActionOperator") // 维修记录关联 maintenance_records MaintenanceRecord[] notifications UserNotification[] processed_sync_changes SyncChange[] @relation("SyncChangeProcessedBy") inventory_tasks_created InventoryTask[] @relation("InventoryTaskCreatedBy") inventory_actions InventoryTaskAction[] @relation("InventoryTaskActionOperator") print_terminal_pair_codes PrintTerminalPairCode[] @relation("PrintTerminalPairCodeCreator") requested_print_jobs PrintJob[] @relation("PrintJobRequester") asset_transfers_operated AssetTransferRecord[] @relation("AssetTransferOperator") dingtalk_deliveries DingTalkDelivery[] @relation("DingTalkDeliveryRecipient") person Person? @relation(fields: [person_id], references: [id], onDelete: SetNull) @@index([role]) @@index([status]) @@map("user") } model PrintTerminal { id Int @id @default(autoincrement()) name String @db.VarChar(100) platform String @db.VarChar(30) machine_name String @db.VarChar(100) status String @default("offline") @db.VarChar(20) protocol_version Int @default(1) is_default Boolean @default(false) last_seen_at DateTime? selected_printer_name String? @db.VarChar(150) printer_snapshot_json String? @db.Text label_profile_json String? @db.Text terminal_token_hash String @unique @db.VarChar(128) paired_at DateTime @default(now()) current_error_message String? @db.VarChar(255) created_at DateTime @default(now()) updated_at DateTime @updatedAt used_pair_codes PrintTerminalPairCode[] @relation("PrintTerminalPairCodeUsedBy") target_jobs PrintJob[] @relation("PrintJobTargetTerminal") claimed_jobs PrintJob[] @relation("PrintJobClaimedByTerminal") @@index([status]) @@index([is_default]) @@index([last_seen_at]) @@map("print_terminal") } model PrintTerminalPairCode { id Int @id @default(autoincrement()) code String @unique @db.VarChar(32) expires_at DateTime created_by Int used_at DateTime? used_by_terminal_id Int? created_at DateTime @default(now()) updated_at DateTime @updatedAt creator User @relation("PrintTerminalPairCodeCreator", fields: [created_by], references: [id]) usedByTerminal PrintTerminal? @relation("PrintTerminalPairCodeUsedBy", fields: [used_by_terminal_id], references: [id]) @@index([expires_at]) @@index([created_by]) @@index([used_by_terminal_id]) @@map("print_terminal_pair_code") } model PrintJob { id Int @id @default(autoincrement()) job_type String @default("asset_label") @db.VarChar(30) status String @default("queued") @db.VarChar(20) target_terminal_id Int claimed_by_terminal_id Int? requested_by Int payload_json String @db.Text error_message String? @db.Text claimed_at DateTime? claim_token String? @db.VarChar(64) lease_expires_at DateTime? result_confirmed_by Int? result_confirmed_at DateTime? started_at DateTime? completed_at DateTime? attempt_count Int @default(0) created_at DateTime @default(now()) updated_at DateTime @updatedAt targetTerminal PrintTerminal @relation("PrintJobTargetTerminal", fields: [target_terminal_id], references: [id]) claimedByTerminal PrintTerminal? @relation("PrintJobClaimedByTerminal", fields: [claimed_by_terminal_id], references: [id]) requester User @relation("PrintJobRequester", fields: [requested_by], references: [id]) @@index([status, created_at]) @@index([status, lease_expires_at]) @@index([target_terminal_id, status, created_at]) @@index([requested_by]) @@index([claimed_by_terminal_id]) @@map("print_job") } 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) job_number String? @unique @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") user User? @@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) identity_mode String @default("dingtalk") @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 deadline_at DateTime? 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]) @@index([status, deadline_at]) @@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 // 任务创建时冻结的直接父资产;为空表示该项在本任务中按根资产展示。 snapshot_parent_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, snapshot_parent_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? // 完成时间 completed_by Int? pre_status String @default("可用") @db.VarChar(20) reject_reason String? @db.Text completion_notes String? @db.Text reminded_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]) completer User? @relation("DisposalCompleter", fields: [completed_by], references: [id]) items DisposalItem[] actions DisposalAction[] @@index([equipment_id]) @@index([status]) @@index([applied_by, created_at]) @@index([approved_by, approved_at]) @@index([completed_by, completed_at]) @@map("disposal_record") } model DisposalItem { id Int @id @default(autoincrement()) disposal_id Int equipment_id Int equipment_code String @db.VarChar(50) equipment_name String @db.VarChar(100) parent_equipment_id Int? parent_equipment_code String? @db.VarChar(50) pre_status String @db.VarChar(20) snapshot_json String? @db.Text created_at DateTime @default(now()) disposal DisposalRecord @relation(fields: [disposal_id], references: [id], onDelete: Cascade) equipment Equipment @relation(fields: [equipment_id], references: [id]) @@unique([disposal_id, equipment_id]) @@index([equipment_id]) @@index([parent_equipment_id]) @@map("disposal_item") } model DisposalAction { id Int @id @default(autoincrement()) disposal_id Int action_type String @db.VarChar(30) operator_id Int? from_status String? @db.VarChar(20) to_status String? @db.VarChar(20) detail String? @db.Text created_at DateTime @default(now()) disposal DisposalRecord @relation(fields: [disposal_id], references: [id], onDelete: Cascade) operator User? @relation("DisposalActionOperator", fields: [operator_id], references: [id], onDelete: SetNull) @@index([disposal_id, created_at]) @@index([operator_id]) @@map("disposal_action") } model AssetTransferRecord { id Int @id @default(autoincrement()) root_equipment_id Int action_type String @db.VarChar(20) from_department_id Int? from_department_name String? @db.VarChar(100) to_department_id Int? to_department_name String? @db.VarChar(100) reason String? @db.Text operated_by Int operated_at DateTime @default(now()) created_at DateTime @default(now()) rootEquipment Equipment @relation("AssetTransferRoot", fields: [root_equipment_id], references: [id]) operator User @relation("AssetTransferOperator", fields: [operated_by], references: [id]) items AssetTransferItem[] @@index([root_equipment_id, operated_at]) @@index([operated_by]) @@index([action_type, operated_at]) @@map("asset_transfer_record") } model AssetTransferItem { id Int @id @default(autoincrement()) transfer_id Int equipment_id Int equipment_code String @db.VarChar(50) equipment_name String @db.VarChar(100) parent_equipment_id Int? from_department_id Int? to_department_id Int? storage_location_id Int? snapshot_status String @db.VarChar(20) created_at DateTime @default(now()) transfer AssetTransferRecord @relation(fields: [transfer_id], references: [id], onDelete: Cascade) equipment Equipment @relation(fields: [equipment_id], references: [id]) @@unique([transfer_id, equipment_id]) @@index([equipment_id, created_at]) @@index([parent_equipment_id]) @@map("asset_transfer_item") } model DingTalkDelivery { id Int @id @default(autoincrement()) idempotency_key String @unique @db.VarChar(191) event_type String @db.VarChar(50) business_type String @db.VarChar(30) business_id Int recipient_user_id Int recipient_dingtalk_userid String? @db.VarChar(100) status String @default("pending") @db.VarChar(20) attempt_count Int @default(0) max_attempts Int @default(5) next_attempt_at DateTime @default(now()) lease_owner String? @db.VarChar(100) lease_expires_at DateTime? provider_type String @default("notification_center") @db.VarChar(20) dingtalk_task_id String? @db.VarChar(100) retry_of_delivery_id Int? manual_created_by Int? manual_created_at DateTime? last_manual_action_by Int? last_manual_action_type String? @db.VarChar(30) last_manual_action_at DateTime? payload_json String @db.Text last_error_code String? @db.VarChar(100) last_error_message String? @db.Text failure_notified_at DateTime? sent_at DateTime? read_at DateTime? created_at DateTime @default(now()) updated_at DateTime @updatedAt recipient User @relation("DingTalkDeliveryRecipient", fields: [recipient_user_id], references: [id]) @@index([status, next_attempt_at]) @@index([lease_expires_at]) @@index([business_type, business_id]) @@index([recipient_user_id, created_at]) @@index([dingtalk_task_id]) @@index([retry_of_delivery_id]) @@map("dingtalk_delivery") } model WebSession { sid String @id @db.VarChar(191) data String @db.LongText expires_at DateTime created_at DateTime @default(now()) updated_at DateTime @updatedAt @@index([expires_at]) @@map("web_session") } // 维修记录 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 // 维修结果 original_tree_snapshot String? @db.LongText // 送修前整棵资产树状态和关系快照 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") }