491 lines
15 KiB
Plaintext
491 lines
15 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ========== 枚举 ==========
|
|
|
|
enum Plan {
|
|
FREE
|
|
PRO
|
|
ENTERPRISE
|
|
}
|
|
|
|
enum Role {
|
|
ADMIN
|
|
HR
|
|
VIEWER
|
|
}
|
|
|
|
enum EmployeeStatus {
|
|
ACTIVE
|
|
RESIGNED
|
|
}
|
|
|
|
enum ContractType {
|
|
FIXED
|
|
UNFIXED
|
|
UNSIGNED
|
|
}
|
|
|
|
enum SignMethod {
|
|
PAPER
|
|
ELECTRONIC
|
|
}
|
|
|
|
enum RiskType {
|
|
CONTRACT
|
|
SALARY
|
|
TERMINATION
|
|
MONTHLY
|
|
}
|
|
|
|
enum RiskLevel {
|
|
HIGH
|
|
MEDIUM
|
|
LOW
|
|
}
|
|
|
|
enum RiskStatus {
|
|
PENDING
|
|
RESOLVED
|
|
IGNORED
|
|
}
|
|
|
|
enum TerminationReason {
|
|
NEGOTIATED
|
|
FAULT
|
|
NONFAULT
|
|
LAYOFF
|
|
EXPIRED
|
|
}
|
|
|
|
enum RiskAssessment {
|
|
SAFE
|
|
WARNING
|
|
DANGER
|
|
}
|
|
|
|
enum OnboardingStatus {
|
|
PENDING
|
|
APPROVED
|
|
REJECTED
|
|
}
|
|
|
|
enum ContractConfirmStatus {
|
|
UNCONFIRMED
|
|
CONFIRMED
|
|
EXPIRED
|
|
}
|
|
|
|
// ========== 核心表 ==========
|
|
|
|
model Organization {
|
|
id String @id @default(cuid())
|
|
name String
|
|
plan Plan @default(FREE)
|
|
maxEmployees Int @default(20)
|
|
city String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
users User[]
|
|
employees Employee[]
|
|
contracts LaborContract[]
|
|
overtimeRecords OvertimeRecord[]
|
|
terminations TerminationRecord[]
|
|
riskItems RiskItem[]
|
|
auditLogs AuditLog[]
|
|
payslips Payslip[]
|
|
onboardingLinks OnboardingLink[]
|
|
confirmLinks ContractConfirmLink[]
|
|
socialInsuranceConfig SocialInsuranceConfig?
|
|
notificationSetting NotificationSetting?
|
|
notificationLogs NotificationLog[]
|
|
employeeAttachments EmployeeAttachment[]
|
|
disciplinaryRecords DisciplinaryRecord[]
|
|
attendanceRecords AttendanceRecord[]
|
|
trainingRecords TrainingRecord[]
|
|
performanceRecords PerformanceRecord[]
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
phone String @unique
|
|
email String?
|
|
passwordHash String
|
|
name String
|
|
role Role @default(ADMIN)
|
|
createdAt DateTime @default(now())
|
|
lastLoginAt DateTime?
|
|
}
|
|
|
|
// ========== 业务表 ==========
|
|
|
|
model Employee {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
name String
|
|
department String
|
|
hireDate DateTime
|
|
monthlySalary String // AES-256 加密存储
|
|
status EmployeeStatus @default(ACTIVE)
|
|
gender String?
|
|
phone String?
|
|
idCardNumber String? // AES-256 加密存储
|
|
emergencyContact String?
|
|
emergencyPhone String?
|
|
address String?
|
|
bankAccount String? // AES-256 加密存储
|
|
bankName String?
|
|
passwordHash String? // 员工端登录密码
|
|
isPregnant Boolean @default(false)
|
|
isInMedicalPeriod Boolean @default(false)
|
|
isWorkInjured Boolean @default(false)
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
contracts LaborContract[]
|
|
overtimeRecords OvertimeRecord[]
|
|
terminations TerminationRecord[]
|
|
riskItems RiskItem[]
|
|
payslips Payslip[]
|
|
attachments EmployeeAttachment[]
|
|
disciplinaryRecords DisciplinaryRecord[]
|
|
attendanceRecords AttendanceRecord[]
|
|
trainingRecords TrainingRecord[]
|
|
performanceRecords PerformanceRecord[]
|
|
}
|
|
|
|
model LaborContract {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
signDate DateTime?
|
|
startDate DateTime
|
|
endDate DateTime?
|
|
contractType ContractType
|
|
signMethod SignMethod @default(PAPER)
|
|
contractYears Int @default(3)
|
|
probationMonths Int @default(0)
|
|
probationSalary Int @default(0)
|
|
renewalCount Int @default(0)
|
|
attachmentName String?
|
|
attachmentUrl String?
|
|
electronicContractNo String?
|
|
electronicContractUrl String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
confirmLinks ContractConfirmLink[]
|
|
}
|
|
|
|
model OvertimeRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
month String // YYYY-MM
|
|
weekdayHours Float @default(0)
|
|
weekendHours Float @default(0)
|
|
holidayHours Float @default(0)
|
|
weekdayPay Float @default(0)
|
|
weekendPay Float @default(0)
|
|
holidayPay Float @default(0)
|
|
totalPay Float @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([employeeId, month])
|
|
}
|
|
|
|
model TerminationRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
reason TerminationReason
|
|
terminationDate DateTime
|
|
compensation Float @default(0)
|
|
riskLevel RiskAssessment @default(SAFE)
|
|
checklist Json
|
|
remark String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model RiskItem {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String?
|
|
employee Employee? @relation(fields: [employeeId], references: [id], onDelete: SetNull)
|
|
type RiskType
|
|
level RiskLevel
|
|
status RiskStatus @default(PENDING)
|
|
title String
|
|
description String
|
|
actionUrl String?
|
|
resolvedAt DateTime?
|
|
resolvedBy String?
|
|
remark String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([orgId, status])
|
|
@@index([orgId, type])
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
action String
|
|
entity String
|
|
entityId String?
|
|
detail Json?
|
|
ip String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([orgId, createdAt])
|
|
}
|
|
|
|
// ========== 社保 & 通知 & 附件 ==========
|
|
|
|
model SocialInsuranceConfig {
|
|
id String @id @default(cuid())
|
|
orgId String @unique
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
city String @default("北京")
|
|
pensionOrg Float @default(16) // 养老保险 企业比例 %
|
|
pensionEmp Float @default(8) // 养老保险 个人比例 %
|
|
medicalOrg Float @default(9.8) // 医疗保险 企业比例 %
|
|
medicalEmp Float @default(2) // 医疗保险 个人比例 %
|
|
unemploymentOrg Float @default(0.5) // 失业保险 企业比例 %
|
|
unemploymentEmp Float @default(0.5) // 失业保险 个人比例 %
|
|
injuryOrg Float @default(0.2) // 工伤保险 企业比例 %
|
|
maternityOrg Float @default(0.8) // 生育保险 企业比例 %
|
|
housingOrg Float @default(12) // 公积金 企业比例 %
|
|
housingEmp Float @default(12) // 公积金 个人比例 %
|
|
baseMin Float @default(6326) // 缴费基数下限
|
|
baseMax Float @default(33891) // 缴费基数上限
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model NotificationSetting {
|
|
id String @id @default(cuid())
|
|
orgId String @unique
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
contractExpiry Boolean @default(true)
|
|
expiryDays Int @default(30)
|
|
contractUnsigned Boolean @default(true)
|
|
overtimeAlert Boolean @default(true)
|
|
payslipReady Boolean @default(true)
|
|
// 月度事务提醒日(每月几号)
|
|
payrollDay Int @default(10) // 发薪日
|
|
socialInsDay Int @default(15) // 社保缴纳日
|
|
housingFundDay Int @default(15) // 公积金缴纳日
|
|
taxDay Int @default(15) // 个税申报日
|
|
wechatWebhook String?
|
|
emailNotify Boolean @default(false)
|
|
email String?
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model NotificationLog {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
type String // CONTRACT_EXPIRY / CONTRACT_UNSIGNED / OVERTIME / PAYSLIP
|
|
title String
|
|
content String
|
|
channel String // WECHAT / EMAIL / IN_APP
|
|
status String @default("SENT") // SENT / FAILED
|
|
employeeId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([orgId, createdAt])
|
|
}
|
|
|
|
model EmployeeAttachment {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
fileName String
|
|
fileType String // ID_CARD / BANK_CARD / CONTRACT_SCAN / EDUCATION / OTHER
|
|
fileUrl String
|
|
fileSize Int @default(0)
|
|
uploadedBy String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([orgId, employeeId])
|
|
}
|
|
|
|
// ========== 仲裁证据链 ==========
|
|
|
|
model DisciplinaryRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
violationDate DateTime
|
|
violationType String // LATE/ABSENT/INSUBORDINATION/MISCONDUCT/VIOLATE_POLICY/OTHER
|
|
description String
|
|
severity String @default("WARNING") // WARNING/SERIOUS/SEVERE
|
|
action String @default("ORAL_WARNING") // ORAL_WARNING/WRITTEN_WARNING/DEDUCTION/DEMOTION/TERMINATION
|
|
actionDetail String?
|
|
employeeAck Boolean @default(false) // 员工是否签字确认
|
|
ackDate DateTime?
|
|
ackMethod String? // SIGN/ELECTRONIC/REFUSED
|
|
witness String? // 见证人
|
|
attachmentUrl String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([orgId, employeeId])
|
|
}
|
|
|
|
model AttendanceRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
date DateTime
|
|
checkInTime String? // HH:mm
|
|
checkOutTime String? // HH:mm
|
|
status String @default("NORMAL") // NORMAL/LATE/EARLY_LEAVE/ABSENT/LEAVE/BUSINESS_TRIP
|
|
lateMinutes Int @default(0)
|
|
earlyMinutes Int @default(0)
|
|
workHours Float @default(0)
|
|
overtimeHours Float @default(0)
|
|
remark String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([employeeId, date])
|
|
@@index([orgId, employeeId])
|
|
}
|
|
|
|
model TrainingRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
trainingDate DateTime
|
|
topic String // 培训主题/制度名称
|
|
content String? // 培训内容摘要
|
|
trainer String?
|
|
duration Float @default(0) // 培训时长(小时)
|
|
ackStatus String @default("PENDING") // PENDING/SIGNED/REFUSED
|
|
ackDate DateTime?
|
|
attachmentUrl String? // 签收单扫描件
|
|
remark String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([orgId, employeeId])
|
|
}
|
|
|
|
model PerformanceRecord {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
period String // 考核周期 YYYY-MM 或 YYYY-Q1
|
|
score Float @default(0) // 考核得分
|
|
grade String @default("B") // A/B/C/D
|
|
result String @default("QUALIFIED") // EXCELLENT/QUALIFIED/NEED_IMPROVE/UNQUALIFIED
|
|
summary String? // 考核评语
|
|
improvementPlan String? // 改进计划(不胜任时)
|
|
employeeAck Boolean @default(false)
|
|
ackDate DateTime?
|
|
reviewer String?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([employeeId, period])
|
|
@@index([orgId, employeeId])
|
|
}
|
|
|
|
// ========== 员工端表 ==========
|
|
|
|
model Payslip {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
employeeId String
|
|
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
|
month String // YYYY-MM
|
|
baseSalary Float @default(0)
|
|
overtimePay Float @default(0)
|
|
weekdayOvertimePay Float @default(0)
|
|
weekendOvertimePay Float @default(0)
|
|
holidayOvertimePay Float @default(0)
|
|
allowance Float @default(0)
|
|
deduction Float @default(0)
|
|
totalPay Float @default(0)
|
|
confirmedAt DateTime?
|
|
confirmedIp String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([employeeId, month])
|
|
@@index([orgId, month])
|
|
}
|
|
|
|
model OnboardingLink {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
token String @unique
|
|
employeeName String?
|
|
phone String?
|
|
status OnboardingStatus @default(PENDING)
|
|
formData Json?
|
|
expiresAt DateTime
|
|
usedAt DateTime?
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([orgId, status])
|
|
}
|
|
|
|
model ContractConfirmLink {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
|
contractId String
|
|
contract LaborContract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
token String @unique
|
|
status ContractConfirmStatus @default(UNCONFIRMED)
|
|
confirmedAt DateTime?
|
|
confirmedIp String?
|
|
expiresAt DateTime
|
|
createdBy String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([orgId, status])
|
|
}
|