feat: 移除ROI,新增待处理风险列表

- 移除ROI横幅和说明,改为年度总价值概览
- 后端新增 pendingRisks 查询,返回PENDING状态风险
- 前端新增待处理风险表格,按员工去重展示
- summary 改为包含待处理风险数量
This commit is contained in:
selfrelease
2026-07-31 10:04:04 +08:00
parent de73d20d68
commit 8014c9a139
2 changed files with 109 additions and 17 deletions
+48 -7
View File
@@ -1553,6 +1553,7 @@ export async function getAnnualValueReport(orgId: string, year: number) {
policiesPublished,
monthlyRisks,
employeeRiskDetails,
pendingRisks,
] = await Promise.all([
prisma.riskItem.count({
where: {
@@ -1631,6 +1632,20 @@ export async function getAnnualValueReport(orgId: string, year: number) {
},
orderBy: { estimatedLoss: 'desc' },
}),
// 未解决的风险列表(PENDING状态),按预估损失降序,最多100条
prisma.riskItem.findMany({
where: {
orgId, status: 'PENDING',
estimatedLoss: { gt: 0 },
},
include: {
employee: {
select: { id: true, name: true, department: true, idCardHash: true },
},
},
orderBy: { estimatedLoss: 'desc' },
take: 100,
}),
])
// 按身份证号去重(同一人可能有多条 Employee 记录),无身份证号时回退到 employeeId
@@ -1763,16 +1778,42 @@ export async function getAnnualValueReport(orgId: string, year: number) {
policiesPublished,
}
const summary = roi >= 1000
? `${year} 年系统创造价值约 ${fmtMoney(totalValue)}ROI 达 ${roi}%,规避潜在损失 ${fmtMoney(lossAvoided)},节约 HR 工时 ${timeSaved} 小时`
: roi >= 300
? `${year} 年系统创造价值约 ${fmtMoney(totalValue)}ROI ${roi}%,处理风险 ${risksResolved} 项,AI 辅助 ${aiConversations + aiReviews}`
: `${year} 年系统已运行,处理风险 ${risksResolved} 项,AI 辅助 ${aiConversations + aiReviews} 次,建议加强使用深度`
const summary = `${year} 年规避潜在损失 ${fmtMoney(lossAvoided)},节约 HR 工时 ${timeSaved} 小时,仍有 ${pendingRisks.length} 项风险待处理`
// 未解决风险按员工去重+风险类别去重
const pendingBreakdown: Record<string, {
name: string
department: string
riskCount: number
totalLoss: number
details: { title: string; estimatedLoss: number; legalBasis: string }[]
}> = {}
for (const r of pendingRisks) {
const personKey = r.employee?.idCardHash || r.employee?.name || r.employeeId || '_unknown'
if (!pendingBreakdown[personKey]) {
pendingBreakdown[personKey] = {
name: r.employee?.name || '未知员工',
department: r.employee?.department || '-',
riskCount: 0,
totalLoss: 0,
details: [],
}
}
pendingBreakdown[personKey].riskCount++
pendingBreakdown[personKey].totalLoss += r.estimatedLoss || 0
pendingBreakdown[personKey].details.push({
title: r.title,
estimatedLoss: r.estimatedLoss || 0,
legalBasis: getLegalBasis(r.title),
})
}
const pendingRiskDetails = Object.values(pendingBreakdown)
.map(p => ({ ...p, details: p.details.sort((a, b) => b.estimatedLoss - a.estimatedLoss) }))
.sort((a, b) => b.totalLoss - a.totalLoss)
return {
year,
totalValue,
roi,
metrics,
timeline,
costSaved,
@@ -1780,6 +1821,7 @@ export async function getAnnualValueReport(orgId: string, year: number) {
lossAvoided,
adjustedLossAvoided,
employeeDetails,
pendingRiskDetails,
summary,
}
}
@@ -1795,7 +1837,6 @@ export async function saveAnnualValueReport(orgId: string, userId: string, year:
orgId,
year: result.year,
totalValue: result.totalValue,
roi: result.roi,
metrics: result.metrics as any,
timeline: result.timeline as any,
costSaved: result.costSaved,