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 } 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[] } 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 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[] @@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]) }