feat: 员工特殊状态台账(三期/工伤/医疗期)完整实现
This commit is contained in:
@@ -305,6 +305,80 @@ export async function detectTerminationRisks(orgId: string) {
|
||||
return risks
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测特殊状态相关风险(三期/工伤/医疗期员工合同到期不得终止)
|
||||
*/
|
||||
async function detectSpecialStatusRisks(orgId: string) {
|
||||
const today = new Date()
|
||||
today.setHours(0, 0, 0, 0)
|
||||
|
||||
// 查找所有进行中的特殊状态记录
|
||||
const specialStatuses = await (prisma as any).employeeSpecialStatus.findMany({
|
||||
where: { orgId, status: 'ACTIVE' },
|
||||
include: {
|
||||
employee: {
|
||||
select: { id: true, name: true, status: true, contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const risks: { employeeId: string; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string }[] = []
|
||||
|
||||
for (const ss of specialStatuses) {
|
||||
const emp = ss.employee
|
||||
if (!emp || emp.status !== 'ACTIVE') continue
|
||||
const latestContract = emp.contracts[0]
|
||||
if (!latestContract || !latestContract.endDate) continue
|
||||
|
||||
const daysToExpire = daysBetween(latestContract.endDate, today)
|
||||
const typeLabel = ss.type === 'PREGNANCY' ? '三期' : ss.type === 'WORK_INJURY' ? '工伤' : ss.type === 'MEDICAL_PERIOD' ? '医疗期' : '特殊状态'
|
||||
|
||||
// 合同即将到期但处于特殊状态期间,不得终止
|
||||
if (daysToExpire >= 0 && daysToExpire <= 60) {
|
||||
risks.push({
|
||||
employeeId: emp.id,
|
||||
type: 'CONTRACT' as RiskType,
|
||||
level: 'HIGH' as RiskLevel,
|
||||
title: `${emp.name}处于${typeLabel}期间,合同${daysToExpire}天后到期,依法不得终止`,
|
||||
description: `${typeLabel}员工在合同到期时,用人单位不得依照《劳动合同法》第四十条、第四十一条终止合同,需顺延至相应情形消失。`,
|
||||
actionUrl: `/special-status?type=${ss.type}`,
|
||||
})
|
||||
}
|
||||
|
||||
// 工伤认定超期提醒(受伤30天内未认定)
|
||||
if (ss.type === 'WORK_INJURY' && ss.injuryDate && !ss.certificationDate) {
|
||||
const daysSinceInjury = daysBetween(today, new Date(ss.injuryDate))
|
||||
if (daysSinceInjury > 30) {
|
||||
risks.push({
|
||||
employeeId: emp.id,
|
||||
type: 'CONTRACT' as RiskType,
|
||||
level: 'HIGH' as RiskLevel,
|
||||
title: `${emp.name}工伤已${daysSinceInjury}天未完成认定,超30天限期`,
|
||||
description: `用人单位应自事故伤害发生之日起30日内提出工伤认定申请,逾期将影响工伤待遇。`,
|
||||
actionUrl: `/special-status?type=WORK_INJURY`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 医疗期即将到期
|
||||
if (ss.type === 'MEDICAL_PERIOD' && ss.medicalPeriodEnd) {
|
||||
const daysToMedicalEnd = daysBetween(new Date(ss.medicalPeriodEnd), today)
|
||||
if (daysToMedicalEnd >= 0 && daysToMedicalEnd <= 30) {
|
||||
risks.push({
|
||||
employeeId: emp.id,
|
||||
type: 'CONTRACT' as RiskType,
|
||||
level: daysToMedicalEnd <= 7 ? 'HIGH' as RiskLevel : 'MEDIUM' as RiskLevel,
|
||||
title: `${emp.name}医疗期${daysToMedicalEnd}天后到期,需提前处理`,
|
||||
description: `医疗期届满后,员工仍需治疗的,用人单位应按规定处理。医疗期满不能从事原工作的,可依法解除合同但需支付经济补偿。`,
|
||||
actionUrl: `/special-status?type=MEDICAL_PERIOD`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return risks
|
||||
}
|
||||
|
||||
export async function detectMonthlyTasks(orgId: string) {
|
||||
const setting = await prisma.notificationSetting.findUnique({ where: { orgId } })
|
||||
if (!setting) return []
|
||||
@@ -372,9 +446,10 @@ export async function runRiskDetection(orgId: string) {
|
||||
const terminationRisks = await detectTerminationRisks(orgId)
|
||||
const onboardingRisks = await detectOnboardingRisks(orgId)
|
||||
const monthlyTasks = await detectMonthlyTasks(orgId)
|
||||
const specialStatusRisks = await detectSpecialStatusRisks(orgId)
|
||||
|
||||
// 获取所有相关员工数据用于风险量化
|
||||
const allEmployeeIds = [...contractRisks, ...terminationRisks, ...onboardingRisks]
|
||||
const allEmployeeIds = [...contractRisks, ...terminationRisks, ...onboardingRisks, ...specialStatusRisks]
|
||||
.map(r => r.employeeId)
|
||||
.filter(Boolean) as string[]
|
||||
const employees = allEmployeeIds.length > 0
|
||||
@@ -383,7 +458,7 @@ export async function runRiskDetection(orgId: string) {
|
||||
const empMap = new Map(employees.map(e => [e.id, e]))
|
||||
|
||||
// 月度任务用 monthlyKeys 去重,其他任务用 existingKeys 去重
|
||||
const nonMonthlyRisks = [...contractRisks, ...terminationRisks, ...onboardingRisks]
|
||||
const nonMonthlyRisks = [...contractRisks, ...terminationRisks, ...onboardingRisks, ...specialStatusRisks]
|
||||
const toCreate = [
|
||||
...nonMonthlyRisks.filter((r) => !existingKeys.has(`${r.employeeId}:${r.type}:${r.actionUrl}`)),
|
||||
...monthlyTasks.filter((r) => !monthlyKeys.has(`${r.employeeId}:${r.title}`)),
|
||||
|
||||
Reference in New Issue
Block a user