diff --git a/backend/src/services/risk.service.ts b/backend/src/services/risk.service.ts index 7900a0a..4a6da43 100644 --- a/backend/src/services/risk.service.ts +++ b/backend/src/services/risk.service.ts @@ -1549,6 +1549,7 @@ export async function getAnnualValueReport(orgId: string, year: number) { auditActions, policiesPublished, monthlyRisks, + employeeRiskDetails, ] = await Promise.all([ prisma.riskItem.count({ where: { @@ -1611,10 +1612,77 @@ export async function getAnnualValueReport(orgId: string, year: number) { }).then((count) => ({ month: m + 1, count })), ), ), + // 按员工维度的已解决风险明细 + prisma.riskItem.findMany({ + where: { + orgId, status: 'RESOLVED', + resolvedAt: { gte: yearStart, lte: yearEnd }, + }, + include: { + employee: { + select: { id: true, name: true, department: true, monthlySalary: true, hireDate: true }, + }, + }, + orderBy: { estimatedLoss: 'desc' }, + }), ]) const lossAvoided = lossAvoidedAgg._sum.estimatedLoss || 0 + // 按员工维度汇总规避损失 + const RISK_AVOIDANCE_RATE = 0.3 + const employeeBreakdown: Record = {} + + for (const r of employeeRiskDetails) { + const empId = r.employeeId || '_unknown' + if (!employeeBreakdown[empId]) { + employeeBreakdown[empId] = { + employeeId: empId, + name: r.employee?.name || '未知员工', + department: r.employee?.department || '-', + riskCount: 0, + lossAvoided: 0, + adjustedLoss: 0, + details: [], + } + } + const emp = employeeBreakdown[empId] + emp.riskCount++ + emp.lossAvoided += r.estimatedLoss || 0 + + // 根据风险标题推断法律依据 + let legalBasis = '—' + const title = r.title || '' + if (title.includes('未签合同')) legalBasis = '第82条 双倍工资≤11个月' + else if (title.includes('到期') && title.includes('未续签')) legalBasis = '第47条 经济补偿N≤12个月' + else if (title.includes('孕期') || title.includes('哺乳期') || title.includes('工伤') || title.includes('医疗期')) legalBasis = '第87条 违法解除2N≤24个月' + else if (title.includes('试用期')) legalBasis = '第20条 试用期工资差额' + else if (title.includes('社保') || title.includes('公积金')) legalBasis = '滞纳金 每日0.05%' + else if (title.includes('工资') || title.includes('个税')) legalBasis = '拖欠工资/个税' + + emp.details.push({ + title: r.title, + estimatedLoss: r.estimatedLoss || 0, + legalBasis, + }) + } + + // 计算每个员工的打折后规避损失 + const employeeDetails = Object.values(employeeBreakdown) + .map((e) => ({ + ...e, + adjustedLoss: Math.round(e.lossAvoided * RISK_AVOIDANCE_RATE), + })) + .sort((a, b) => b.adjustedLoss - a.adjustedLoss) + // 月度事件时间轴 const timeline = monthlyRisks.map((mr) => ({ month: mr.month, @@ -1626,7 +1694,6 @@ export async function getAnnualValueReport(orgId: string, year: number) { // 1. 规避损失 = 已解决风险的预估损失之和 × 0.3(规避率30%,非每项风险都会演变为实际仲裁) // 2. 节约工时 = AI 查询 * 0.5h + 合同审查 * 1h + 算薪批次 * 2h + 制度公示 * 1h // 3. 节约成本 = 节约工时 * 100 元/h(HR 平均时薪) - const RISK_AVOIDANCE_RATE = 0.3 // 规避率:约30%的未处理风险会演变为实际劳动争议 const adjustedLossAvoided = Math.round(lossAvoided * RISK_AVOIDANCE_RATE) const aiTimeSaved = aiConversations * 0.5 + aiReviews * 1.0 const payrollTimeSaved = payrollBatches * 2.0 @@ -1671,6 +1738,8 @@ export async function getAnnualValueReport(orgId: string, year: number) { costSaved, timeSaved, lossAvoided, + adjustedLossAvoided, + employeeDetails, summary, } } diff --git a/frontend/src/pages/tools/AnnualValueReport.tsx b/frontend/src/pages/tools/AnnualValueReport.tsx index cc3a4fa..4b6b6d1 100644 --- a/frontend/src/pages/tools/AnnualValueReport.tsx +++ b/frontend/src/pages/tools/AnnualValueReport.tsx @@ -220,6 +220,62 @@ export default function AnnualValueReport() { + {/* 员工规避损失明细 */} + {report.employeeDetails && report.employeeDetails.length > 0 && ( + +

员工规避损失明细

+
+ + + + + + + + + + + + + + {report.employeeDetails.map((emp: any, idx: number) => ( + + + + + + + + + + ))} + + + + + + + + + + +
员工部门风险数原始损失规避率规避损失(×30%)风险明细
{emp.name}{emp.department}{emp.riskCount}¥{emp.lossAvoided.toLocaleString()}30%¥{emp.adjustedLoss.toLocaleString()} +
+ {emp.details.map((d: any, i: number) => ( +
+ {d.title} + · + {d.legalBasis} + · + ¥{d.estimatedLoss.toLocaleString()} +
+ ))} +
+
合计¥{(report.lossAvoided || 0).toLocaleString()}¥{(report.adjustedLossAvoided || 0).toLocaleString()}
+
+
+ )} + {/* 价值计算说明 */}

价值计算说明