feat: AIHR 智能人力资源管理系统初始提交
- 员工花名册管理(加密存储、导入导出) - 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条) - 社保公积金(多城市配置、版本管理、基数调整) - 解聘管理(6步流程、证据链、工作交接) - AI 助手(合同审查、风险预测、RAG 知识库) - Dashboard 仪表盘 - 设置与通知
This commit is contained in:
@@ -0,0 +1,826 @@
|
||||
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
|
||||
ONBOARDING
|
||||
}
|
||||
|
||||
enum RiskLevel {
|
||||
HIGH
|
||||
MEDIUM
|
||||
LOW
|
||||
}
|
||||
|
||||
enum PayrollBatchType {
|
||||
REGULAR // 常规发薪
|
||||
TERMINATION // 离职结算
|
||||
BONUS // 年终奖/奖金
|
||||
SEVERANCE // 补偿金按月发放(无社保,个税按政策处理)
|
||||
}
|
||||
|
||||
enum PayrollBatchStatus {
|
||||
DRAFT // 草稿(可编辑)
|
||||
ARCHIVED // 归档(已发薪,锁定)
|
||||
}
|
||||
|
||||
enum PayslipItemType {
|
||||
INPUT // 手工输入项(计算依据)
|
||||
CALCULATED // 计算项(公式自动计算)
|
||||
}
|
||||
|
||||
enum PayslipStatus {
|
||||
PENDING // 待发布
|
||||
PUBLISHED // 已发布到员工端
|
||||
}
|
||||
|
||||
enum RiskStatus {
|
||||
PENDING
|
||||
RESOLVED
|
||||
IGNORED
|
||||
}
|
||||
|
||||
enum TerminationReason {
|
||||
NEGOTIATED
|
||||
FAULT
|
||||
NONFAULT
|
||||
LAYOFF
|
||||
EXPIRED
|
||||
RESIGNATION
|
||||
}
|
||||
|
||||
enum RiskAssessment {
|
||||
SAFE
|
||||
WARNING
|
||||
DANGER
|
||||
}
|
||||
|
||||
enum OnboardingStatus {
|
||||
PENDING
|
||||
APPROVED
|
||||
REJECTED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum ContractConfirmStatus {
|
||||
UNCONFIRMED
|
||||
CONFIRMED
|
||||
EXPIRED
|
||||
}
|
||||
|
||||
// ========== 核心表 ==========
|
||||
|
||||
model Organization {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
plan Plan @default(FREE)
|
||||
maxEmployees Int @default(20)
|
||||
city String?
|
||||
payrollFrequency Int @default(1) // 每月发薪次数(1=一次一批)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
users User[]
|
||||
employees Employee[]
|
||||
contracts LaborContract[]
|
||||
overtimeRecords OvertimeRecord[]
|
||||
terminations TerminationRecord[]
|
||||
riskItems RiskItem[]
|
||||
auditLogs AuditLog[]
|
||||
payslips Payslip[]
|
||||
payrollBatches PayrollBatch[]
|
||||
payslipItems PayslipItem[]
|
||||
salaryChangeRecords SalaryChangeRecord[]
|
||||
onboardingLinks OnboardingLink[]
|
||||
confirmLinks ContractConfirmLink[]
|
||||
aiConversations AIConversation[]
|
||||
aiReviewRecords AIReviewRecord[]
|
||||
socialInsuranceConfig SocialInsuranceConfig[]
|
||||
housingFundConfigs HousingFundConfig[]
|
||||
socialInsRecords EmployeeSocialInsRecord[]
|
||||
housingFundRecords EmployeeHousingFundRecord[]
|
||||
departmentRecords EmployeeDepartmentRecord[]
|
||||
notificationSetting NotificationSetting?
|
||||
overtimeConfig OvertimeConfig?
|
||||
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)
|
||||
disabled Boolean @default(false)
|
||||
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 加密存储
|
||||
idCardHash String? // SHA-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)
|
||||
// 薪税扩展
|
||||
socialInsBase Float? // 社保缴费基数(便捷字段,由Record同步)
|
||||
housingFundBase Float? // 公积金缴费基数(便捷字段,由Record同步)
|
||||
socialInsStartMonth String? // 当前社保开始年月(便捷字段)
|
||||
socialInsEndMonth String? // 当前社保截止年月(便捷字段,null=在保)
|
||||
housingFundStartMonth String? // 当前公积金开始年月(便捷字段)
|
||||
housingFundEndMonth String? // 当前公积金截止年月(便捷字段)
|
||||
specialDeduction Float @default(0) // 专项附加扣除(子女教育、赡养老人等,员工portal端填报)
|
||||
city String? // 员工社保参保城市
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
contracts LaborContract[]
|
||||
overtimeRecords OvertimeRecord[]
|
||||
terminations TerminationRecord[]
|
||||
riskItems RiskItem[]
|
||||
payslips Payslip[]
|
||||
salaryChanges SalaryChangeRecord[]
|
||||
batchEntries BatchEntry[]
|
||||
attachments EmployeeAttachment[]
|
||||
disciplinaryRecords DisciplinaryRecord[]
|
||||
attendanceRecords AttendanceRecord[]
|
||||
trainingRecords TrainingRecord[]
|
||||
performanceRecords PerformanceRecord[]
|
||||
socialInsRecords EmployeeSocialInsRecord[]
|
||||
housingFundRecords EmployeeHousingFundRecord[]
|
||||
departmentRecords EmployeeDepartmentRecord[]
|
||||
aiReviewRecords AIReviewRecord[]
|
||||
|
||||
@@unique([orgId, idCardHash])
|
||||
}
|
||||
|
||||
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)
|
||||
batchId String? // 关联的发薪批次(加入后锁定,不可重复加入)
|
||||
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)
|
||||
type String @default("TERMINATION") // TERMINATION=公司解聘, RESIGNATION=员工主动离职
|
||||
reason TerminationReason
|
||||
terminationDate DateTime
|
||||
resignationReason String? // 主动离职原因(type=RESIGNATION时使用)
|
||||
compensation Float @default(0)
|
||||
socialInsEndMonth String? // 社保截止缴费年月 YYYY-MM
|
||||
housingFundEndMonth String? // 公积金截止缴费年月 YYYY-MM
|
||||
riskLevel RiskAssessment @default(SAFE)
|
||||
checklist Json
|
||||
remark String?
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// 流程状态机
|
||||
status String @default("DRAFT") // DRAFT|PENDING_APPROVAL|APPROVED|EXECUTING|COMPLETED|REJECTED|CANCELLED
|
||||
currentStep Int @default(0) // 当前完成到第几步
|
||||
// 补偿金分项明细 + 调整记录
|
||||
compensationBreakdown Json? // { severance, noticePay, doublePay, other, adjustments: [{field, from, to, reason}] }
|
||||
// 合规检查覆盖记录
|
||||
checklistOverrides Json? // { key: { checked: bool, overrideReason: string } }
|
||||
// 工作交接清单
|
||||
handoverItems Json? // [{ key, label, done, remark }]
|
||||
// 审批信息
|
||||
approvedBy String?
|
||||
approvedAt DateTime?
|
||||
approvalComment String?
|
||||
updatedBy String?
|
||||
|
||||
@@index([orgId, status])
|
||||
}
|
||||
|
||||
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
|
||||
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) // 生育保险 企业比例 %
|
||||
baseMin Float @default(6326) // 社保缴费基数下限
|
||||
baseMax Float @default(33891) // 社保缴费基数上限
|
||||
effectiveFrom String // 生效月份 YYYY-MM
|
||||
effectiveTo String? // 失效月份 YYYY-MM(null=当前有效)
|
||||
isCurrent Boolean @default(true) // 是否当前生效版本
|
||||
adjustmentDone Boolean @default(false) // 是否已执行过社保基数调整
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([orgId, city, effectiveFrom])
|
||||
@@index([orgId, isCurrent])
|
||||
}
|
||||
|
||||
model HousingFundConfig {
|
||||
id String @id @default(cuid())
|
||||
orgId String
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
city String @default("北京")
|
||||
housingOrg Float @default(12) // 公积金 企业比例 %
|
||||
housingEmp Float @default(12) // 公积金 个人比例 %
|
||||
baseMin Float @default(6326) // 公积金缴费基数下限
|
||||
baseMax Float @default(33891) // 公积金缴费基数上限
|
||||
effectiveFrom String // 生效月份 YYYY-MM
|
||||
effectiveTo String? // 失效月份 YYYY-MM(null=当前有效)
|
||||
isCurrent Boolean @default(true) // 是否当前生效版本
|
||||
adjustmentDone Boolean @default(false) // 是否已执行过公积金基数调整
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([orgId, city, effectiveFrom])
|
||||
@@index([orgId, isCurrent])
|
||||
}
|
||||
|
||||
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 OvertimeConfig {
|
||||
id String @id @default(cuid())
|
||||
orgId String @unique
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
weekdayRate Float @default(1.5) // 工作日加班倍率
|
||||
weekendRate Float @default(2.0) // 休息日加班倍率
|
||||
holidayRate Float @default(3.0) // 法定节假日加班倍率
|
||||
monthlyDays Float @default(21.75) // 月计薪天数
|
||||
dailyHours Float @default(8) // 每日工时
|
||||
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)
|
||||
bonus Float @default(0) // 奖金/年终奖
|
||||
totalPay Float @default(0) // 应发合计
|
||||
// 扣除项
|
||||
socialEmp Float @default(0) // 个人社保
|
||||
housingEmp Float @default(0) // 个人公积金
|
||||
tax Float @default(0) // 个人所得税
|
||||
netPay Float @default(0) // 实发工资 = totalPay - socialEmp - housingEmp - tax
|
||||
// 累计预扣法
|
||||
ytdIncome Float @default(0) // 当年累计收入
|
||||
ytdTaxDeducted Float @default(0) // 当年累计已扣税
|
||||
ytdSocialEmp Float @default(0) // 当年累计个人社保
|
||||
ytdHousingEmp Float @default(0) // 当年累计个人公积金
|
||||
// 状态
|
||||
status PayslipStatus @default(PENDING) // PENDING → PUBLISHED
|
||||
confirmedAt DateTime?
|
||||
confirmedIp String?
|
||||
publishedAt DateTime? // 工资条发布到员工端的时间
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([employeeId, month])
|
||||
@@index([orgId, month])
|
||||
@@index([orgId, status])
|
||||
}
|
||||
|
||||
model PayrollBatch {
|
||||
id String @id @default(cuid())
|
||||
orgId String
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
month String // YYYY-MM
|
||||
batchNo Int // 批次序号(1, 2, 3...)
|
||||
name String // 批次名称
|
||||
type PayrollBatchType @default(REGULAR)
|
||||
status PayrollBatchStatus @default(DRAFT)
|
||||
employeeCount Int @default(0)
|
||||
totalPay Float @default(0)
|
||||
totalNetPay Float @default(0)
|
||||
totalSocialOrg Float @default(0)
|
||||
totalSocialEmp Float @default(0)
|
||||
totalHousingOrg Float @default(0)
|
||||
totalHousingEmp Float @default(0)
|
||||
totalTax Float @default(0)
|
||||
remark String?
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
archivedAt DateTime?
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
entries BatchEntry[]
|
||||
|
||||
@@unique([orgId, month, batchNo])
|
||||
@@index([orgId, month])
|
||||
@@index([orgId, status])
|
||||
}
|
||||
|
||||
model BatchEntry {
|
||||
id String @id @default(cuid())
|
||||
batchId String
|
||||
batch PayrollBatch @relation(fields: [batchId], references: [id], onDelete: Cascade)
|
||||
orgId String
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||||
// 薪酬项(可编辑的输入项)
|
||||
baseSalary Float @default(0)
|
||||
overtimePay Float @default(0)
|
||||
allowance Float @default(0)
|
||||
deduction Float @default(0)
|
||||
bonus Float @default(0)
|
||||
// 自动计算项
|
||||
socialEmp Float @default(0)
|
||||
socialOrg Float @default(0)
|
||||
housingEmp Float @default(0)
|
||||
housingOrg Float @default(0)
|
||||
tax Float @default(0)
|
||||
totalPay Float @default(0) // 应发合计
|
||||
netPay Float @default(0) // 实发工资
|
||||
// 风险提示
|
||||
riskWarnings Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([batchId, employeeId])
|
||||
@@index([orgId, employeeId])
|
||||
}
|
||||
|
||||
model PayslipItem {
|
||||
id String @id @default(cuid())
|
||||
orgId String
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
name String // 显示名称
|
||||
code String // 字段代码
|
||||
type PayslipItemType @default(INPUT)
|
||||
formula String? // 计算公式(CALCULATED 类型),如 "baseSalary + overtimePay + allowance - deduction"
|
||||
order Int @default(0)
|
||||
isDefault Boolean @default(true) // 系统预置项不可删除
|
||||
isEditable Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([orgId, code])
|
||||
}
|
||||
|
||||
model SalaryChangeRecord {
|
||||
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)
|
||||
oldSalary Float
|
||||
newSalary Float
|
||||
effectiveDate DateTime // 生效日期
|
||||
effectiveMonth String // 生效年月 YYYY-MM(从 effectiveDate 转换)
|
||||
endMonth String? // 失效年月 YYYY-MM(null=至今有效,被新版本覆盖时设置)
|
||||
changeType String @default("SALARY_CHANGE") // ONBOARDING=入职, REHIRE=重新入职, SALARY_CHANGE=调薪
|
||||
reason String?
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([orgId, employeeId])
|
||||
@@index([employeeId, effectiveMonth, endMonth])
|
||||
}
|
||||
|
||||
model EmployeeSocialInsRecord {
|
||||
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)
|
||||
city String @default("北京") // 参保城市
|
||||
startMonth String // 开始缴费年月 YYYY-MM
|
||||
endMonth String? // 截止缴费年月 YYYY-MM(null=至今有效)
|
||||
base Float // 缴费基数
|
||||
changeType String // ONBOARDING=入职, REHIRE=重新入职, ADJUST=调基, TERMINATION=离职/解聘
|
||||
changeRefId String? // 关联的 TerminationRecord ID(离职/解聘时)
|
||||
remark String?
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([orgId, employeeId])
|
||||
@@index([employeeId, startMonth, endMonth])
|
||||
@@index([orgId, city])
|
||||
}
|
||||
|
||||
model EmployeeHousingFundRecord {
|
||||
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)
|
||||
city String @default("北京") // 参保城市
|
||||
startMonth String // 开始缴费年月 YYYY-MM
|
||||
endMonth String? // 截止缴费年月 YYYY-MM(null=至今有效)
|
||||
base Float // 缴费基数
|
||||
changeType String // ONBOARDING=入职, REHIRE=重新入职, ADJUST=调基, TERMINATION=离职/解聘
|
||||
changeRefId String? // 关联的 TerminationRecord ID(离职/解聘时)
|
||||
remark String?
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([orgId, employeeId])
|
||||
@@index([employeeId, startMonth, endMonth])
|
||||
}
|
||||
|
||||
model EmployeeDepartmentRecord {
|
||||
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)
|
||||
oldDepartment String // 调整前部门
|
||||
newDepartment String // 调整后部门
|
||||
effectiveMonth String // 生效年月 YYYY-MM
|
||||
endMonth String? // 失效年月 YYYY-MM(null=至今有效)
|
||||
reason String? // 调部门原因
|
||||
changeType String // ONBOARDING=入职, REHIRE=重新入职, TRANSFER=调部门
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([orgId, employeeId])
|
||||
@@index([employeeId, effectiveMonth, endMonth])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
// ========== AI 会话 & 审查记录 ==========
|
||||
|
||||
model AIConversation {
|
||||
id String @id @default(cuid())
|
||||
orgId String
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
title String @default("新对话")
|
||||
messages Json // [{ role, content }]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([orgId, userId])
|
||||
}
|
||||
|
||||
model AIReviewRecord {
|
||||
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 String // REVIEW=合同审查, CASE=案例匹配
|
||||
input String // 用户输入的合同文本或争议情形
|
||||
result String // AI 返回的审查/分析结果
|
||||
createdBy String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([orgId, employeeId])
|
||||
}
|
||||
|
||||
// ========== RAG 知识库 ==========
|
||||
|
||||
model RagKnowledge {
|
||||
id String @id
|
||||
title String
|
||||
content String
|
||||
source String
|
||||
category String
|
||||
embedding Unsupported("vector(1536)")?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([category])
|
||||
@@map("rag_knowledge")
|
||||
}
|
||||
Reference in New Issue
Block a user