From d4cc7e5e623f3dfa4582e22d28ce9582230099b3 Mon Sep 17 00:00:00 2001 From: selfrelease Date: Fri, 31 Jul 2026 10:59:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A3=8E=E9=99=A9=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E5=A4=A9=E6=95=B0=E6=94=B9=E4=B8=BA=E6=9C=88=E6=95=B0=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/routes/roster.routes.ts | 8 ++++---- backend/src/services/risk.service.ts | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/routes/roster.routes.ts b/backend/src/routes/roster.routes.ts index 7d7ec90..522b5f0 100644 --- a/backend/src/routes/roster.routes.ts +++ b/backend/src/routes/roster.routes.ts @@ -347,7 +347,7 @@ router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res, level: daysSinceHire > 365 ? 'DANGER' : 'HIGH', category: '劳动关系', title: '未签订书面劳动合同', - description: `入职已${daysSinceHire}天仍未签订书面劳动合同,超过30天未签合同将面临双倍工资赔偿风险(《劳动合同法》第82条)。${daysSinceHire > 365 ? '已满一年未签合同,视为已订立无固定期限劳动合同。' : ''}`, + description: `入职已${Math.round(daysSinceHire / 30)}个月仍未签订书面劳动合同,超过30天未签合同将面临双倍工资赔偿风险(《劳动合同法》第82条)。${daysSinceHire > 365 ? '已满一年未签合同,视为已订立无固定期限劳动合同。' : ''}`, }) } } @@ -414,7 +414,7 @@ router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res, level: 'MEDIUM', category: '薪酬发放', title: '无工资条记录', - description: `入职已${daysSinceHire}天但无任何工资条记录,在仲裁中难以证明已按时足额支付工资。建议尽快创建发薪批次并归档。`, + description: `入职已${Math.round(daysSinceHire / 30)}个月但无任何工资条记录,在仲裁中难以证明已按时足额支付工资。建议尽快创建发薪批次并归档。`, }) } @@ -684,7 +684,7 @@ router.get('/:id/evidence-chain/export', authMiddleware, async (req: AuthRequest const risks: any[] = [] if (employee.contracts.length === 0) { const days = Math.floor((today.getTime() - employee.hireDate.getTime()) / 86400000) - if (days > 30) risks.push({ level: days > 365 ? '危险' : '高', category: '劳动关系', title: '未签订书面劳动合同', description: `入职已${days}天仍未签订书面劳动合同,超过30天未签合同将面临双倍工资赔偿风险。${days > 365 ? '已满一年未签合同,视为已订立无固定期限劳动合同。' : ''}` }) + if (days > 30) risks.push({ level: days > 365 ? '危险' : '高', category: '劳动关系', title: '未签订书面劳动合同', description: `入职已${Math.round(days / 30)}个月仍未签订书面劳动合同,超过30天未签合同将面临双倍工资赔偿风险。${days > 365 ? '已满一年未签合同,视为已订立无固定期限劳动合同。' : ''}` }) } employee.contracts.forEach((c) => { if (!c.signDate) risks.push({ level: '高', category: '劳动关系', title: '合同未签字', description: `合同期限${c.startDate.toISOString().slice(0, 10)}至${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'},尚未签订。` }) @@ -698,7 +698,7 @@ router.get('/:id/evidence-chain/export', authMiddleware, async (req: AuthRequest if (c.probationMonths > 0 && c.probationSalary === 0) risks.push({ level: '中', category: '劳动关系', title: '试用期工资为0', description: `合同约定试用期${c.probationMonths}个月但试用期工资为0,违反《劳动合同法》第20条。` }) }) const daysSinceHire = Math.floor((today.getTime() - employee.hireDate.getTime()) / 86400000) - if (daysSinceHire > 30 && employee.payslips.length === 0) risks.push({ level: '中', category: '薪酬发放', title: '无工资条记录', description: `入职已${daysSinceHire}天但无任何工资条记录。` }) + if (daysSinceHire > 30 && employee.payslips.length === 0) risks.push({ level: '中', category: '薪酬发放', title: '无工资条记录', description: `入职已${Math.round(daysSinceHire / 30)}个月但无任何工资条记录。` }) employee.terminations.forEach((t) => { if (t.status !== 'COMPLETED') risks.push({ level: '高', category: '解聘记录', title: '离职流程未完成', description: `流程当前状态为「${termStatusMap[t.status] || t.status}」,尚未完成闭环。` }) if (t.reason === 'NEGOTIATED' && t.compensation === 0) risks.push({ level: '中', category: '解聘记录', title: '协商解除但补偿金为0', description: '解聘原因为协商解除但经济补偿金为0,面临2N赔偿风险。' }) diff --git a/backend/src/services/risk.service.ts b/backend/src/services/risk.service.ts index 4d741d8..dae8f27 100644 --- a/backend/src/services/risk.service.ts +++ b/backend/src/services/risk.service.ts @@ -165,7 +165,7 @@ export async function detectContractRisks(orgId: string) { employeeId: emp.id, type: 'CONTRACT', level: 'HIGH', - title: `${emp.name}入职${days}天未签合同,已视为无固定期限`, + title: `${emp.name}入职${Math.round(days / 30)}个月未签合同,已视为无固定期限`, description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)},超过1年未签订书面合同,法律上已视为无固定期限劳动合同。`, actionUrl: `/contracts?employee=${encodeURIComponent(emp.name)}`, }) @@ -174,7 +174,7 @@ export async function detectContractRisks(orgId: string) { employeeId: emp.id, type: 'CONTRACT', level: 'HIGH', - title: `${emp.name}入职${days}天未签合同`, + title: `${emp.name}入职${Math.round(days / 30)}个月未签合同`, description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)},超过30天未签订书面合同,需尽快补签。`, actionUrl: `/contracts?employee=${encodeURIComponent(emp.name)}`, }) @@ -183,7 +183,7 @@ export async function detectContractRisks(orgId: string) { employeeId: emp.id, type: 'CONTRACT', level: 'LOW', - title: `${emp.name}入职${days}天,尚未签合同`, + title: `${emp.name}入职${Math.round(days / 30)}个月,尚未签合同`, description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)},30天内需签订书面合同。`, actionUrl: `/contracts?employee=${encodeURIComponent(emp.name)}`, }) @@ -266,7 +266,7 @@ export async function detectOnboardingRisks(orgId: string) { employeeId: emp.id, type: 'ONBOARDING', level: daysSinceHire > 30 ? 'HIGH' : 'MEDIUM', - title: `${emp.name}入职手续未完成${daysSinceHire > 30 ? `(已超${daysSinceHire}天)` : ''}`, + title: `${emp.name}入职手续未完成${daysSinceHire > 30 ? `(已超${Math.round(daysSinceHire / 30)}个月)` : ''}`, description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)},尚未签订劳动合同,请尽快完成入职手续。`, actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`, }) @@ -1694,7 +1694,7 @@ export async function getAnnualValueReport(orgId: string, year: number) { // 辅助:根据风险标题归并到风险类别 key(去掉天数等动态部分) function getRiskCategoryKey(title: string): string { if (title.includes('未签合同') && title.includes('无固定期限')) return 'CONTRACT_UNSIGNED_OVER_YEAR' - if (title.includes('未签合同') && title.includes('天')) return 'CONTRACT_UNSIGNED' + if (title.includes('未签合同') && title.includes('个月')) return 'CONTRACT_UNSIGNED' if (title.includes('到期') && title.includes('未续签')) return 'CONTRACT_EXPIRED' if (title.includes('即将到期')) return 'CONTRACT_EXPIRING' if (title.includes('试用期')) return 'CONTRACT_PROBATION'