/** * 员工特殊状态台账服务 * 管理三期(孕期/产期/哺乳期)、工伤、医疗期等特殊状态的跟踪和提醒 */ import prisma from '../lib/prisma' /** 特殊状态类型 */ export const STATUS_TYPES: Record = { PREGNANCY: { label: '三期', color: 'pink' }, WORK_INJURY: { label: '工伤', color: 'red' }, MEDICAL_PERIOD: { label: '医疗期', color: 'orange' }, OTHER: { label: '其他', color: 'gray' }, } /** 状态文本映射 */ export const STATUS_LABELS: Record = { ACTIVE: '进行中', PENDING: '待处理', RESOLVED: '已结束', } /** * 获取预警级别(基于提醒日期) * - red: 已过期或7天内到期 * - yellow: 30天内到期 * - green: 正常 */ export function getAlertLevel(reminderDate: Date | null, status: string): 'red' | 'yellow' | 'green' { if (status === 'RESOLVED') return 'green' if (!reminderDate) return 'green' const now = new Date() const days = Math.floor((reminderDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) if (days <= 7) return 'red' if (days <= 30) return 'yellow' return 'green' } /** * 根据三期数据自动计算关键日期 * - 产假: 产前15天 + 产后75天(共98天,难产+15天) * - 哺乳期: 至孩子满1周岁 */ export function calculatePregnancyDates(expectedDueDate: Date): { maternityLeaveStart: Date maternityLeaveEnd: Date nursingEndDate: Date } { // 产假开始: 预产期前15天 const maternityLeaveStart = new Date(expectedDueDate) maternityLeaveStart.setDate(maternityLeaveStart.getDate() - 15) // 产假结束: 预产期后75天(共98天) const maternityLeaveEnd = new Date(expectedDueDate) maternityLeaveEnd.setDate(maternityLeaveEnd.getDate() + 75) // 哺乳期截止: 孩子满1周岁 const nursingEndDate = new Date(expectedDueDate) nursingEndDate.setFullYear(nursingEndDate.getFullYear() + 1) return { maternityLeaveStart, maternityLeaveEnd, nursingEndDate } } /** * 根据工龄计算医疗期月数 * - 实际工作年限 < 5年: 本单位 < 3年 → 3个月, ≥ 3年 → 6个月 * - 实际工作年限 ≥ 5年: 本单位 < 3年 → 6个月, 3~5年 → 9个月, 5~10年 → 12个月, ≥10年 → 24个月 */ export function calculateMedicalMonths(totalWorkYears: number, companyYears: number): number { if (totalWorkYears < 5) { return companyYears < 3 ? 3 : 6 } if (companyYears < 3) return 6 if (companyYears < 5) return 9 if (companyYears < 10) return 12 return 24 } /** * 添加操作时间线条目 */ function addTimelineEntry(existing: any[], action: string, by: string): any[] { const entry = { time: new Date().toISOString(), action, by } return [...(existing || []), entry] } /** * 创建特殊状态记录 */ export async function createSpecialStatus(orgId: string, userId: string, data: any) { const employee = await prisma.employee.findFirst({ where: { id: data.employeeId, orgId } }) if (!employee) { throw { code: 'NOT_FOUND', message: '员工不存在' } } // 三期自动计算 let pregnancyData: any = {} if (data.type === 'PREGNANCY' && data.expectedDueDate) { const dates = calculatePregnancyDates(new Date(data.expectedDueDate)) pregnancyData = dates } // 医疗期自动计算截止日 let medicalData: any = {} if (data.type === 'MEDICAL_PERIOD' && data.startDate && data.medicalMonths) { const end = new Date(data.startDate) end.setMonth(end.getMonth() + data.medicalMonths) medicalData.medicalPeriodEnd = end } const record = await (prisma as any).employeeSpecialStatus.create({ data: { orgId, employeeId: data.employeeId, type: data.type, status: data.status || 'ACTIVE', startDate: data.startDate ? new Date(data.startDate) : null, endDate: data.endDate ? new Date(data.endDate) : null, expectedDueDate: data.expectedDueDate ? new Date(data.expectedDueDate) : null, maternityLeaveStart: pregnancyData.maternityLeaveStart || null, maternityLeaveEnd: pregnancyData.maternityLeaveEnd || null, nursingEndDate: pregnancyData.nursingEndDate || null, injuryDate: data.injuryDate ? new Date(data.injuryDate) : null, injuryDescription: data.injuryDescription || null, certificationDate: data.certificationDate ? new Date(data.certificationDate) : null, certificationNo: data.certificationNo || null, disabilityLevel: data.disabilityLevel || null, assessmentDate: data.assessmentDate ? new Date(data.assessmentDate) : null, medicalMonths: data.medicalMonths || null, medicalPeriodEnd: medicalData.medicalPeriodEnd || null, description: data.description || null, attachments: data.attachments || null, reminderDate: data.reminderDate ? new Date(data.reminderDate) : null, timeline: addTimelineEntry([], '创建记录', userId), createdBy: userId, }, }) // 同步更新 Employee 的布尔字段 await syncEmployeeFlags(employee.id, data.type, true) return record } /** * 更新特殊状态记录 */ export async function updateSpecialStatus(orgId: string, userId: string, id: string, data: any) { const existing = await (prisma as any).employeeSpecialStatus.findFirst({ where: { id, orgId }, }) if (!existing) { throw { code: 'NOT_FOUND', message: '记录不存在' } } const updateData: any = {} const fields = [ 'type', 'status', 'startDate', 'endDate', 'actualEndDate', 'expectedDueDate', 'maternityLeaveStart', 'maternityLeaveEnd', 'nursingEndDate', 'injuryDate', 'injuryDescription', 'certificationDate', 'certificationNo', 'disabilityLevel', 'assessmentDate', 'medicalMonths', 'medicalPeriodEnd', 'description', 'attachments', 'reminderDate', ] for (const f of fields) { if (data[f] !== undefined) { updateData[f] = data[f] === null ? null : (data[f] instanceof Date || f === 'injuryDescription' || f === 'certificationNo' || f === 'description' || f === 'type' || f === 'status' || f === 'attachments' ? data[f] : new Date(data[f])) } } // 三期自动计算 if (data.type === 'PREGNANCY' && data.expectedDueDate) { const dates = calculatePregnancyDates(new Date(data.expectedDueDate)) updateData.maternityLeaveStart = dates.maternityLeaveStart updateData.maternityLeaveEnd = dates.maternityLeaveEnd updateData.nursingEndDate = dates.nursingEndDate } // 医疗期自动计算截止日 if (data.type === 'MEDICAL_PERIOD' && data.startDate && data.medicalMonths) { const end = new Date(data.startDate) end.setMonth(end.getMonth() + data.medicalMonths) updateData.medicalPeriodEnd = end } // 记录时间线 const actions: string[] = [] if (data.status && data.status !== existing.status) { actions.push(`状态变更为: ${STATUS_LABELS[data.status] || data.status}`) } if (data.certificationDate && !existing.certificationDate) { actions.push('工伤认定完成') } if (data.assessmentDate && !existing.assessmentDate) { actions.push('伤残鉴定完成') } if (data.actualEndDate && !existing.actualEndDate) { actions.push('状态结束') } if (actions.length === 0) { actions.push('更新记录') } updateData.timeline = addTimelineEntry(existing.timeline, actions.join('; '), userId) const record = await (prisma as any).employeeSpecialStatus.update({ where: { id }, data: updateData, }) // 如果状态变为 RESOLVED,同步 Employee 布尔字段 if (data.status === 'RESOLVED' && existing.status !== 'RESOLVED') { await syncEmployeeFlags(existing.employeeId, existing.type, false) } return record } /** * 删除特殊状态记录 */ export async function deleteSpecialStatus(orgId: string, id: string) { const existing = await (prisma as any).employeeSpecialStatus.findFirst({ where: { id, orgId }, }) if (!existing) { throw { code: 'NOT_FOUND', message: '记录不存在' } } await (prisma as any).employeeSpecialStatus.delete({ where: { id } }) // 同步 Employee 布尔字段 if (existing.status === 'ACTIVE') { await syncEmployeeFlags(existing.employeeId, existing.type, false) } return { id } } /** * 同步 Employee 表的布尔标记字段 */ async function syncEmployeeFlags(employeeId: string, type: string, active: boolean) { const updateData: any = {} if (type === 'PREGNANCY') updateData.isPregnant = active if (type === 'MEDICAL_PERIOD') updateData.isInMedicalPeriod = active if (type === 'WORK_INJURY') updateData.isWorkInjured = active if (Object.keys(updateData).length > 0) { await prisma.employee.update({ where: { id: employeeId }, data: updateData }) } } /** * 获取需要提醒的特殊状态列表 */ export async function getPendingReminders(orgId: string) { const now = new Date() const soon = new Date() soon.setDate(soon.getDate() + 7) return (prisma as any).employeeSpecialStatus.findMany({ where: { orgId, status: 'ACTIVE', reminderDate: { gte: now, lte: soon }, }, include: { employee: { select: { id: true, name: true, department: true, phone: true } }, }, orderBy: { reminderDate: 'asc' }, }) }