e1b5ae9aab
P0紧急修复(6项): - 草稿保存完整恢复所有字段(含socialAvgWage) - 补偿金批次从compensationBreakdown读取 - 违法解除风险确认UI - 合同结束日期前后校验(前后端双保险) P1高优先级(14项): - 离职日期联动社保/公积金截止月(15号规则) - 合规检查+工作交接改为软阻断(生成待办) - 补偿月数(N/N+1/2N/自定义)+计算基数(近12月/合同/自定义) - 解聘并入花名册操作栏(类型选择跳转向导) - 合同续签开始日期自动推导(原合同结束日+1天) - 年龄合规筛查(童工阻断/未成年工/退休警告) - 编辑入职日期后状态联动(待入职↔在职) - 转正移植到花名册操作栏+薪资回写 - 男职工无法选择三期 P2体验优化(9项): - "劳动合同"调整为"用工关系" - 费用结算新增剩余年假折算(300%日工资) - 身份证号全域改为"证件号码"(前后端18个文件) - 手机号查重 - 开具证明+合同续签移植到花名册操作栏 - 批量转正+批量开具证明 - 去掉用工办理模块 P3规划(2项): - 组织架构+审批流(Department/Position/ApprovalFlow/ApprovalInstance) - 客服工作台(Ticket/ChatSession+SUPPORT角色) 新增模型: Department/Position/ApprovalFlow/ApprovalInstance/Ticket/TicketMessage/ChatSession/ChatMessage 新增字段: Employee.departmentId/supervisorId 新增角色: SUPPORT Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1706 lines
63 KiB
Plaintext
1706 lines
63 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
// ========== 枚举 ==========
|
||
|
||
enum Plan {
|
||
FREE
|
||
PRO
|
||
ENTERPRISE
|
||
}
|
||
|
||
enum Role {
|
||
SUPER_ADMIN
|
||
ADMIN
|
||
HR
|
||
VIEWER
|
||
SUPPORT
|
||
}
|
||
|
||
enum EmployeeStatus {
|
||
ACTIVE
|
||
RESIGNED
|
||
TERMINATED
|
||
}
|
||
|
||
enum FemaleWorkerType {
|
||
CADRE // 干部/管理岗
|
||
WORKER // 工人/操作岗
|
||
}
|
||
|
||
enum ContractType {
|
||
FIXED
|
||
UNFIXED
|
||
UNSIGNED
|
||
LABOR
|
||
INTERNSHIP
|
||
DISPATCH
|
||
OUTSOURCING
|
||
PARTTIME
|
||
}
|
||
|
||
enum SignMethod {
|
||
PAPER
|
||
ELECTRONIC
|
||
}
|
||
|
||
enum RiskType {
|
||
CONTRACT
|
||
SALARY
|
||
TERMINATION
|
||
MONTHLY
|
||
ONBOARDING
|
||
RETIREMENT
|
||
}
|
||
|
||
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?
|
||
contactName String?
|
||
contactPhone String?
|
||
payrollDays Json @default("[5]") // 每月发薪日期,如 [5, 20] 表示每月5号和20号
|
||
payrollReminderDays Int @default(3) // 发薪提前提醒天数
|
||
retirementReminderEnabled Boolean @default(false) // 退休提醒开关
|
||
esignPolicyEnabled Boolean @default(false) // 规章制度电子签
|
||
esignPayslipEnabled Boolean @default(false) // 工资条电子签
|
||
esignOnboardingEnabled Boolean @default(false) // 入职文件电子签
|
||
esignTrainingEnabled Boolean @default(false) // 培训记录电子签
|
||
esignPerformanceEnabled Boolean @default(false) // 绩效考核电子签
|
||
esignDisciplinaryEnabled Boolean @default(false) // 违纪记录电子签
|
||
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[]
|
||
performanceTemplates PerformanceTemplate[]
|
||
retirementPolicies RetirementPolicy[]
|
||
socialMonthlyProcesses SocialMonthlyProcess[]
|
||
evidenceChains EvidenceChain[]
|
||
attendanceConfirmations AttendanceConfirmation[]
|
||
policyDocuments PolicyDocument[]
|
||
policyReadRecords PolicyReadRecord[]
|
||
healthCheckReports HealthCheckReport[]
|
||
annualValueReports AnnualValueReport[]
|
||
specialDeductionRecords SpecialDeductionRecord[]
|
||
shifts Shift[]
|
||
shiftAssignments ShiftAssignment[]
|
||
leaveRecords LeaveRecord[]
|
||
leaveRequests LeaveRequest[]
|
||
calendarEvents CalendarEvent[]
|
||
consultations Consultation[]
|
||
workProcesses WorkProcess[]
|
||
enterpriseTemplates EnterpriseTemplate[]
|
||
attendancePublishes AttendancePublish[]
|
||
specialStatuses EmployeeSpecialStatus[]
|
||
companyFiles CompanyFile[]
|
||
commercialInsPlans CommercialInsurancePlan[]
|
||
commercialInsEnrollments CommercialInsuranceEnrollment[]
|
||
benefitPlans EmployeeBenefitPlan[]
|
||
benefitEnrollments EmployeeBenefitEnrollment[]
|
||
eSignRecords ESignRecord[]
|
||
medicalPeriodPolicies MedicalPeriodPolicy[]
|
||
// 组织架构 + 审批流
|
||
departments Department[]
|
||
positions Position[]
|
||
approvalFlows ApprovalFlow[]
|
||
approvalInstances ApprovalInstance[]
|
||
// 客服工作台
|
||
tickets Ticket[]
|
||
chatSessions ChatSession[]
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(cuid())
|
||
orgId String? // 平台管理员不绑定企业(null)
|
||
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
|
||
employeeNo String? // 工号
|
||
department String
|
||
position String? // 岗位
|
||
hireDate DateTime
|
||
// 组织架构扩展
|
||
departmentId String? // 关联 Department 表(兼容旧 department 字符串)
|
||
supervisorId String? // 直属上级员工ID
|
||
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? // 员工社保参保城市
|
||
birthDate DateTime? // 出生日期(从身份证号提取)
|
||
education String? // 学历(博士/硕士/本科/大专/高中/其他)
|
||
femaleWorkerType FemaleWorkerType? // 女性岗位类型(CADRE=干部/WORKER=工人,仅女性需要区分)
|
||
retirementDaysLeft Int? // 距退休天数(便捷字段,定期计算)
|
||
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[]
|
||
evidenceChains EvidenceChain[]
|
||
attendanceConfirmations AttendanceConfirmation[]
|
||
policyReadRecords PolicyReadRecord[]
|
||
specialDeductionRecords SpecialDeductionRecord[]
|
||
shiftAssignments ShiftAssignment[]
|
||
leaveRecords LeaveRecord[]
|
||
leaveRequests LeaveRequest[]
|
||
calendarEvents CalendarEvent[]
|
||
workProcesses WorkProcess[]
|
||
specialStatuses EmployeeSpecialStatus[]
|
||
commercialInsEnrollments CommercialInsuranceEnrollment[]
|
||
benefitEnrollments EmployeeBenefitEnrollment[]
|
||
eSignRecords ESignRecord[]
|
||
// 组织架构关联
|
||
dept Department? @relation("DeptEmployees", fields: [departmentId], references: [id])
|
||
supervisor Employee? @relation("SupervisorSubordinates", fields: [supervisorId], references: [id])
|
||
subordinates Employee[] @relation("SupervisorSubordinates")
|
||
approvalInstances ApprovalInstance[]
|
||
|
||
@@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?
|
||
// 风险量化:预估损失金额(元)
|
||
estimatedLoss Float?
|
||
// 风险量化:损失区间 [最低, 最高]
|
||
lossRange Json?
|
||
// 建议处理截止日期
|
||
deadline DateTime?
|
||
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) // 社保缴费基数上限(养老/失业/工伤)
|
||
medicalBaseMin Float @default(0) // 医疗/生育保险基数下限(0 时 fallback 到 baseMin)
|
||
medicalBaseMax Float @default(0) // 医疗/生育保险基数上限(0 时 fallback 到 baseMax)
|
||
extraInsurances Json? // 附加险种配置 JSON: [{ name, orgRate, empRate, baseType: 'pension'|'medical'|'fixed', fixedAmount }]
|
||
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("北京")
|
||
accountType String @default("BASIC") // BASIC=基本公积金, SUPPLEMENTARY=补充公积金
|
||
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, accountType, 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 MedicalPeriodPolicy {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
region String // 地区名称,如"全国"、"上海"、"广东"
|
||
legalBasis String // 法律依据
|
||
rules Json // 分档规则: [{ maxYears: 5, months: 3, cycleMonths: 6 }, ...]
|
||
isDefault Boolean @default(false)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([orgId, region])
|
||
@@index([orgId])
|
||
}
|
||
|
||
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 / TERMINATION_DOC / RETIREMENT_DOC / INJURY_CERT / MEDICAL_CERT / PREGNANCY_CERT / DISCIPLINARY / OTHER
|
||
fileUrl String
|
||
fileSize Int @default(0)
|
||
// 关联违纪记录(可选)
|
||
disciplinaryRecordId String?
|
||
uploadedBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([orgId, employeeId])
|
||
@@index([disciplinaryRecordId])
|
||
}
|
||
|
||
// ========== 公司备用文件 ==========
|
||
model CompanyFile {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
fileName String
|
||
fileType String // BUSINESS_LICENSE=营业执照 / WORK_HOURS=工时备案 / HR_POLICY=制度文件 / LABOR_CONTRACT_TEMPLATE=合同模板 / OTHER=其他
|
||
fileUrl String
|
||
fileSize Int @default(0)
|
||
remark String? // 备注
|
||
expiryDate DateTime? // 有效期(如营业执照到期日)
|
||
uploadedBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, fileType])
|
||
}
|
||
|
||
// ========== 仲裁证据链 ==========
|
||
|
||
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
|
||
periodType String @default("MONTHLY") // MONTHLY/QUARTERLY/YEARLY
|
||
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? // 改进计划(不胜任时)
|
||
templateId String? // 关联绩效模板(选填)
|
||
dimensionScores Json? // 各维度得分明细 { dimensionName: score }
|
||
employeeAck Boolean @default(false)
|
||
ackDate DateTime?
|
||
reviewer String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([employeeId, period])
|
||
@@index([orgId, employeeId])
|
||
}
|
||
|
||
model PerformanceTemplate {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String // 模板名称
|
||
description String? // 模板说明
|
||
dimensions Json // 考核维度 [{ name, weight, maxScore, description }]
|
||
gradeRules Json? // 等级规则 [{ grade, result, minScore, maxScore }]
|
||
isDefault Boolean @default(false)
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId])
|
||
}
|
||
|
||
// ========== 员工端表 ==========
|
||
|
||
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? // 工资条发布到员工端的时间
|
||
publishStatus String? // UNPUBLISHED/PUBLISHED/SCHEDULED
|
||
scheduledAt DateTime? // 定时发送时间
|
||
viewedAt 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)
|
||
// 细化薪资项
|
||
positionSalary Float @default(0) // 岗位工资
|
||
performanceSalary Float @default(0) // 绩效工资
|
||
senioritySalary Float @default(0) // 工龄工资
|
||
transportAllowance Float @default(0) // 交通补贴
|
||
mealAllowance Float @default(0) // 餐补
|
||
housingAllowance Float @default(0) // 住房补贴
|
||
communicationAllowance Float @default(0) // 通讯补贴
|
||
otherDeduction 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 SocialMonthlyProcess {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
month String // 办理月份 YYYY-MM
|
||
type String // SOCIAL=社保, HOUSING=公积金
|
||
status String @default("COMPLETED") // COMPLETED=已办理
|
||
snapshot Json // 办理时的数据快照(增减员+在保人员+缴费明细)
|
||
processedBy String
|
||
processedAt DateTime @default(now())
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([orgId, month, type])
|
||
@@index([orgId, month])
|
||
}
|
||
|
||
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")
|
||
}
|
||
|
||
// ========== 退休政策版本管理 ==========
|
||
|
||
model RetirementPolicy {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
version Int @default(1)
|
||
content String // AI 返回的完整政策内容
|
||
contentHash String // 内容 hash,用于判断内容是否变化
|
||
status String @default("PENDING") // PENDING=待确认, CONFIRMED=已生效, SUPERSEDED=已被新版本替代
|
||
maleRetireAge Float @default(60) // 男性退休年龄
|
||
femaleRetireAge Float @default(55) // 女性退休年龄(干部55,工人50,由AI解析最新政策)
|
||
femaleWorkerAge Float @default(50) // 女性工人退休年龄
|
||
createdAt DateTime @default(now())
|
||
confirmedAt DateTime? // 用户确认时间
|
||
|
||
@@index([orgId, createdAt])
|
||
}
|
||
|
||
// ========== 证据链管理 ==========
|
||
|
||
model EvidenceChain {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
category String // CONTRACT_SIGN / ONBOARD / PAYSLIP_CONFIRM / DISCIPLINARY / ATTENDANCE / TERMINATION
|
||
refId String? // 关联记录 ID(合同ID/工资条ID等)
|
||
employeeId String?
|
||
employee Employee? @relation(fields: [employeeId], references: [id], onDelete: SetNull)
|
||
events Json // [{ action, timestamp, ip, userAgent, smsCode?, location? }]
|
||
hash String // 全链路 SHA-256 哈希(防篡改)
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([orgId, category, refId])
|
||
@@index([orgId, employeeId])
|
||
}
|
||
|
||
// ========== 考勤确认 ==========
|
||
|
||
model AttendanceConfirmation {
|
||
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
|
||
workDays Int @default(0)
|
||
weekdayHours Float @default(0)
|
||
weekendHours Float @default(0)
|
||
holidayHours Float @default(0)
|
||
overtimePay Float @default(0)
|
||
confirmedAt DateTime?
|
||
confirmedBy String?
|
||
confirmIp String?
|
||
status String @default("PENDING") // PENDING / CONFIRMED / DISPUTED
|
||
disputeNote String? // 员工有异议时的说明
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([orgId, employeeId, month])
|
||
@@index([orgId, month])
|
||
@@index([orgId, status])
|
||
}
|
||
|
||
// ========== 规章制度民主程序 ==========
|
||
|
||
model PolicyDocument {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
title String
|
||
content String
|
||
type String @default("RULES") // RULES=规章制度 / NOTICE=通知
|
||
status String @default("DRAFT") // DRAFT / DISCUSSING / CONSULTING / PUBLISHED
|
||
democracyProgress Json? // { currentStep: 1-4, steps: [{ name, status, date, note }] }
|
||
publishedAt DateTime?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
readRecords PolicyReadRecord[]
|
||
|
||
@@index([orgId, status])
|
||
}
|
||
|
||
// ========== 规章制度阅读签收 ==========
|
||
|
||
model PolicyReadRecord {
|
||
id String @id @default(cuid())
|
||
policyId String
|
||
policy PolicyDocument @relation(fields: [policyId], references: [id], onDelete: Cascade)
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
employeeId String
|
||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||
readAt DateTime @default(now())
|
||
ip String?
|
||
userAgent String?
|
||
|
||
@@unique([policyId, employeeId])
|
||
@@index([orgId, employeeId])
|
||
}
|
||
|
||
// ========== 用工体检诊断报告 ==========
|
||
|
||
model HealthCheckReport {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
year Int // 诊断年度
|
||
totalScore Int // 综合评分 0-100
|
||
level String // safe / warning / danger
|
||
dimensions Json // [{ key, name, score, findings: [], recommendations: [] }]
|
||
summary String // 诊断总结
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([orgId, year])
|
||
@@index([orgId, createdAt])
|
||
}
|
||
|
||
// ========== 年度价值报告 ==========
|
||
|
||
model AnnualValueReport {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
year Int // 报告年度
|
||
totalValue Int // 总价值(元)
|
||
roi Float // ROI 百分比
|
||
metrics Json // { risksResolved, lossAvoided, aiQueries, contractsReviewed, payrollProcessed, employeesManaged }
|
||
timeline Json // [{ month, event, value }] 月度事件时间轴
|
||
costSaved Int // 节约成本(元)
|
||
timeSaved Int // 节约工时(小时)
|
||
summary String // 年度总结
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([orgId, year])
|
||
@@index([orgId, createdAt])
|
||
}
|
||
|
||
// 专项附加扣除按月记录
|
||
model SpecialDeductionRecord {
|
||
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 月份
|
||
amount Float @default(0) // 专项附加扣除总额
|
||
children Float @default(0) // 子女教育
|
||
elderly Float @default(0) // 赡养老人
|
||
housing Float @default(0) // 住房贷款利息/住房租金
|
||
education Float @default(0) // 继续教育
|
||
infant Float @default(0) // 3岁以下婴幼儿照护
|
||
remark String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([employeeId, month])
|
||
@@index([orgId, month])
|
||
}
|
||
|
||
// ========== 班次管理 ==========
|
||
|
||
model Shift {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String // 班次名称,如"早班"、"白班"、"夜班"
|
||
startTime String // 上班时间 HH:mm
|
||
endTime String // 下班时间 HH:mm
|
||
flexibleMinutes Int @default(0) // 弹性时长(分钟)
|
||
restMinutes Int @default(0) // 休息时长(分钟)
|
||
color String @default("#3b82f6") // 日历显示颜色
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
assignments ShiftAssignment[]
|
||
|
||
@@index([orgId])
|
||
}
|
||
|
||
// ========== 排班记录 ==========
|
||
|
||
model ShiftAssignment {
|
||
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)
|
||
shiftId String
|
||
shift Shift @relation(fields: [shiftId], references: [id], onDelete: Cascade)
|
||
date DateTime // 排班日期
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([employeeId, date])
|
||
@@index([orgId, date])
|
||
@@index([orgId, employeeId])
|
||
}
|
||
|
||
// ========== 休假记录(直接记录,无审批流程) ==========
|
||
|
||
model LeaveRecord {
|
||
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)
|
||
leaveType String // SICK=病假 / PERSONAL=事假 / ANNUAL=年假 / MATERNITY=产假 / OTHER=其他
|
||
startDate DateTime
|
||
endDate DateTime
|
||
days Float // 请假天数
|
||
reason String?
|
||
remark String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, employeeId])
|
||
@@index([orgId, startDate])
|
||
}
|
||
|
||
// ========== 休假审批流 ==========
|
||
|
||
model LeaveRequest {
|
||
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)
|
||
leaveType String // SICK=病假 / PERSONAL=事假 / ANNUAL=年假 / MATERNITY=产假 / OTHER=其他
|
||
startDate DateTime
|
||
endDate DateTime
|
||
days Float // 请假天数
|
||
reason String?
|
||
attachment String? // 附件URL(如病假条)
|
||
status String @default("PENDING") // PENDING=待审批 / APPROVED=已批准 / REJECTED=已驳回 / CANCELLED=已撤回
|
||
approverId String? // 审批人ID
|
||
approvedAt DateTime?
|
||
approveRemark String? // 审批意见
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, employeeId])
|
||
@@index([orgId, status])
|
||
@@index([orgId, startDate])
|
||
}
|
||
|
||
// ========== 自定义日历事件 ==========
|
||
|
||
model CalendarEvent {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
title String
|
||
date DateTime
|
||
endDate DateTime?
|
||
type String @default("CUSTOM") // CUSTOM=自定义, MEETING=会议, TEAM_BUILDING=团建, TRAINING=培训, INTERVIEW=面试
|
||
priority String @default("medium") // high/medium/low
|
||
location String?
|
||
description String?
|
||
employeeId String?
|
||
employee Employee? @relation(fields: [employeeId], references: [id], onDelete: SetNull)
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, date])
|
||
@@index([orgId, type])
|
||
}
|
||
|
||
// ========== 人工咨询服务 ==========
|
||
|
||
model Consultation {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
type String // LEGAL=法律咨询, ARBITRATION=仲裁代理, COURT=出庭服务
|
||
title String
|
||
description String
|
||
contactName String
|
||
contactPhone String
|
||
status String @default("PENDING") // PENDING=待处理, CONTACTED=已联系, COMPLETED=已完成, CANCELLED=已取消
|
||
aiConversationId String? // 关联 AI 会话
|
||
remark String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([orgId, type])
|
||
}
|
||
|
||
// ========== 用工办理工作流 ==========
|
||
model WorkProcess {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
type String // HIRE/ONBOARD/CUSTOM_CONTRACT/INFO_SUBMIT/CONFIRM/CHANGE/RENEW/SUSPEND/INCOME_CERT/TERMINATE/RESCIND/LEAVING_CERT/FLEXIBLE
|
||
title String
|
||
employeeId String?
|
||
employee Employee? @relation(fields: [employeeId], references: [id], onDelete: SetNull)
|
||
status String @default("DRAFT") // DRAFT/PENDING_APPROVAL/APPROVED/REJECTED/EXECUTING/COMPLETED/CANCELLED
|
||
formData Json // 表单数据 JSON
|
||
documents Json? // 生成的文书列表 [{name, content, type}]
|
||
approverId String?
|
||
approvedAt DateTime?
|
||
remark String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([orgId, type])
|
||
@@index([orgId, createdBy])
|
||
}
|
||
|
||
// ========== 企业自建文本模板 ==========
|
||
model EnterpriseTemplate {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String
|
||
category String // CONTRACT/RULES/NOTICE/AGREEMENT/OTHER
|
||
description String?
|
||
content String @db.Text
|
||
variables String[] // 变量名列表
|
||
status String @default("ACTIVE") // ACTIVE/ARCHIVED
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, category])
|
||
}
|
||
|
||
// ========== 考勤发布记录 ==========
|
||
model AttendancePublish {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
month String // YYYY-MM
|
||
title String // 如"2026年7月考勤表"
|
||
status String @default("PUBLISHED") // PUBLISHED/CANCELLED
|
||
publishDate DateTime @default(now())
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([orgId, month])
|
||
@@index([orgId, month])
|
||
}
|
||
|
||
// ========== 员工特殊状态台账(三期/工伤/医疗期等) ==========
|
||
model EmployeeSpecialStatus {
|
||
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 // PREGNANCY(三期) | WORK_INJURY(工伤) | MEDICAL_PERIOD(医疗期) | OTHER
|
||
status String @default("ACTIVE") // ACTIVE(进行中) | RESOLVED(已结束) | PENDING(待处理)
|
||
|
||
// 通用时间节点
|
||
startDate DateTime? // 开始日期
|
||
endDate DateTime? // 预计结束日期
|
||
actualEndDate DateTime? // 实际结束日期
|
||
|
||
// 三期专用
|
||
expectedDueDate DateTime? // 预产期
|
||
maternityLeaveStart DateTime? // 产假开始
|
||
maternityLeaveEnd DateTime? // 产假结束
|
||
nursingEndDate DateTime? // 哺乳期截止
|
||
|
||
// 工伤专用
|
||
injuryDate DateTime? // 受伤日期
|
||
injuryDescription String? // 伤情描述
|
||
certificationDate DateTime? // 工伤认定日期
|
||
certificationNo String? // 认定书编号
|
||
disabilityLevel Int? // 伤残等级(1-10级)
|
||
assessmentDate DateTime? // 鉴定日期
|
||
|
||
// 医疗期专用
|
||
medicalMonths Int? // 医疗期月数
|
||
medicalPeriodEnd DateTime? // 医疗期截止日
|
||
|
||
// 通用
|
||
description String? // 备注
|
||
attachments Json? // 附件列表 [{name, url}]
|
||
timeline Json? // 操作时间线 [{time, action, by}]
|
||
reminderDate DateTime? // 提醒日期
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([orgId, type])
|
||
@@index([employeeId])
|
||
}
|
||
|
||
// ========== 验收测试 ==========
|
||
model AcceptanceTest {
|
||
id String @id @default(uuid())
|
||
orgId String
|
||
verifierName String // 验收人姓名
|
||
results Json // { "1.1": "pass", "1.2": "fail", ... }
|
||
remarks Json // { "1.1": "备注内容", ... }
|
||
conclusionName String? // 结论验收人
|
||
conclusionDate String? // 结论日期
|
||
conclusionResult String? // pass | conditional | fail — 系统自动计算
|
||
conclusionIssues String? // 遗留问题(系统自动汇总)
|
||
screenshots Json? // { "0-0": "data:image/png;base64,...", ... }
|
||
signature1 String? // 验收人签名 dataURL
|
||
signature2 String? // 项目经理签名 dataURL
|
||
status String @default("DRAFT") // DRAFT | SUBMITTED
|
||
submittedAt DateTime?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([orgId, verifierName])
|
||
@@index([orgId, status])
|
||
}
|
||
|
||
// ========== 商业保险 ==========
|
||
model CommercialInsurancePlan {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String
|
||
type String // ACCIDENT | SUPPLEMENTARY_MEDICAL | EMPLOYER_LIABILITY | CRITICAL_ILLNESS | GROUP_LIFE | OTHER
|
||
provider String // 保险公司
|
||
policyNo String?
|
||
premium Float // 年保费
|
||
coverageAmount Float // 保额
|
||
effectiveFrom String // YYYY-MM-DD
|
||
effectiveTo String? // null = 长期
|
||
description String?
|
||
status String @default("ACTIVE") // ACTIVE | EXPIRED | CANCELLED
|
||
enrollments CommercialInsuranceEnrollment[]
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([orgId, type])
|
||
}
|
||
|
||
model CommercialInsuranceEnrollment {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
planId String
|
||
plan CommercialInsurancePlan @relation(fields: [planId], references: [id], onDelete: Cascade)
|
||
employeeId String
|
||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||
premium Float // 个人保费
|
||
effectiveFrom String // YYYY-MM-DD
|
||
effectiveTo String?
|
||
status String @default("ACTIVE") // ACTIVE | TERMINATED
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, planId])
|
||
@@index([employeeId])
|
||
}
|
||
|
||
// ========== 员工福利 ==========
|
||
model EmployeeBenefitPlan {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String
|
||
category String // TRANSPORT | MEAL | HOUSING | COMMUNICATION | HEALTH_CHECK | HOLIDAY | BIRTHDAY | OTHER
|
||
amount Float // 每月金额(或每次金额)
|
||
frequency String @default("MONTHLY") // MONTHLY | QUARTERLY | YEARLY | ONE_TIME
|
||
taxDeductible Boolean @default(false) // 是否税前扣除
|
||
description String?
|
||
status String @default("ACTIVE")
|
||
enrollments EmployeeBenefitEnrollment[]
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([orgId, category])
|
||
}
|
||
|
||
model EmployeeBenefitEnrollment {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
planId String
|
||
plan EmployeeBenefitPlan @relation(fields: [planId], references: [id], onDelete: Cascade)
|
||
employeeId String
|
||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||
effectiveFrom String // YYYY-MM
|
||
effectiveTo String? // null = 至今
|
||
amount Float? // 覆盖默认金额(个别调整)
|
||
status String @default("ACTIVE")
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, planId])
|
||
@@index([employeeId])
|
||
}
|
||
|
||
// ========== 电子签署(易签宝) ==========
|
||
model ESignRecord {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
contractId String? // 关联 LaborContract
|
||
employeeId String
|
||
employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade)
|
||
scene String @default("CONTRACT") // CONTRACT | RESIGNATION | POLICY | PAYSLIP | ONBOARDING
|
||
flowId String? // 易签宝流程ID
|
||
documentTitle String // 文件标题
|
||
documentContent String? // 文件内容(HTML/PDF base64)
|
||
status String @default("PENDING") // PENDING | SIGNING | COMPLETED | REJECTED | EXPIRED | CANCELLED
|
||
signUrl String? // 签署链接
|
||
signedPdfUrl String? // 签署完成后的PDF链接
|
||
initiatedBy String // 发起人(HR用户ID)
|
||
completedAt DateTime?
|
||
expiredAt DateTime?
|
||
callbackData Json? // 易签宝回调数据
|
||
remark String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([employeeId])
|
||
@@index([contractId])
|
||
}
|
||
|
||
// ==================== 组织架构 + 审批流 ====================
|
||
|
||
/// 部门(树形结构,自引用 parent)
|
||
model Department {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String
|
||
parentId String?
|
||
parent Department? @relation("DeptChildren", fields: [parentId], references: [id], onDelete: SetNull)
|
||
children Department[] @relation("DeptChildren")
|
||
level Int @default(0) // 层级(0=根)
|
||
sortOrder Int @default(0) // 同级排序
|
||
description String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
employees Employee[] @relation("DeptEmployees")
|
||
positions Position[]
|
||
|
||
@@unique([orgId, name, parentId])
|
||
@@index([orgId, parentId])
|
||
}
|
||
|
||
/// 岗位字典
|
||
model Position {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
name String
|
||
departmentId String?
|
||
department Department? @relation(fields: [departmentId], references: [id], onDelete: SetNull)
|
||
headcount Int @default(0) // 编制人数
|
||
level String? // 职级(如 P1/P2/M1)
|
||
description String?
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([orgId, name, departmentId])
|
||
@@index([orgId, departmentId])
|
||
}
|
||
|
||
/// 审批流配置
|
||
model ApprovalFlow {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
type String // LEAVE / TERMINATION / SALARY_CHANGE / OTHER
|
||
name String
|
||
enabled Boolean @default(true)
|
||
// 步骤配置 JSON: [{ step: 1, approverType: 'SUPERVISOR' | 'PERSON', approverId?: string, name: string }]
|
||
steps Json
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
instances ApprovalInstance[]
|
||
|
||
@@unique([orgId, type])
|
||
@@index([orgId])
|
||
}
|
||
|
||
/// 审批实例
|
||
model ApprovalInstance {
|
||
id String @id @default(cuid())
|
||
orgId String
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
flowId String
|
||
flow ApprovalFlow @relation(fields: [flowId], references: [id], onDelete: Cascade)
|
||
type String // 冗余 flow.type,便于查询
|
||
bizId String? // 关联业务记录ID(如 LeaveRequest.id)
|
||
bizType String? // 业务类型
|
||
status String @default("PENDING") // PENDING / APPROVED / REJECTED / CANCELLED
|
||
currentStep Int @default(1)
|
||
// 审批记录 JSON: [{ step, approverId, approverName, result, comment, timestamp }]
|
||
approvals Json @default("[]")
|
||
employeeId String?
|
||
employee Employee? @relation(fields: [employeeId], references: [id], onDelete: SetNull)
|
||
createdBy String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([orgId, status])
|
||
@@index([employeeId])
|
||
@@index([bizId])
|
||
}
|
||
|
||
// ==================== 客服工作台 ====================
|
||
|
||
/// 工单
|
||
model Ticket {
|
||
id String @id @default(cuid())
|
||
orgId String // 提交方租户
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
title String
|
||
content String
|
||
category String? // 分类(如:系统bug/功能咨询/数据问题)
|
||
priority String @default("NORMAL") // LOW / NORMAL / HIGH / URGENT
|
||
status String @default("OPEN") // OPEN / IN_PROGRESS / RESOLVED / CLOSED
|
||
assigneeId String? // 客服处理人 userId
|
||
createdBy String // 提交人 userId
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
messages TicketMessage[]
|
||
|
||
@@index([orgId, status])
|
||
@@index([assigneeId])
|
||
}
|
||
|
||
/// 工单回复
|
||
model TicketMessage {
|
||
id String @id @default(cuid())
|
||
ticketId String
|
||
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
||
content String
|
||
senderId String
|
||
senderRole String // USER / SUPPORT
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([ticketId])
|
||
}
|
||
|
||
/// 客服-客户会话
|
||
model ChatSession {
|
||
id String @id @default(cuid())
|
||
orgId String // 客户租户
|
||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||
supportUserId String // 客服 userId
|
||
lastMessage String?
|
||
lastMessageAt DateTime?
|
||
unreadByUser Int @default(0)
|
||
unreadBySupport Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
messages ChatMessage[]
|
||
|
||
@@unique([orgId, supportUserId])
|
||
@@index([supportUserId])
|
||
}
|
||
|
||
/// 会话消息
|
||
model ChatMessage {
|
||
id String @id @default(cuid())
|
||
sessionId String
|
||
session ChatSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||
content String
|
||
senderId String
|
||
senderRole String // USER / SUPPORT
|
||
read Boolean @default(false)
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([sessionId])
|
||
}
|