R46 A1-A7:落地绩效考核模块首版

这个提交包含在:
2026-03-29 16:30:29 +08:00
父节点 48f395e18d
当前提交 659e3153a3
共修改 19 个文件,包含 2652 行新增和 15 行删除
+62 -1
查看文件
@@ -6,6 +6,22 @@ function addDays(date, days) {
return next;
}
function toNumber(value) {
return value == null ? null : Number(value);
}
function buildPerformanceDistribution(rows) {
const countByScore = new Map(rows.map((item) => [toNumber(item.overall_score), item._count._all]));
const items = [];
for (let score = 1; score <= 5; score += 0.5) {
items.push({
score: Number(score.toFixed(1)),
count: countByScore.get(Number(score.toFixed(1))) ?? 0,
});
}
return items;
}
async function buildEmployeeStats(prisma, options = {}) {
const timeZone = options.timeZone ?? "Asia/Shanghai";
const todayString = currentDateString(timeZone);
@@ -14,7 +30,7 @@ async function buildEmployeeStats(prisma, options = {}) {
const next30DateTo = addDays(today, 30);
const activeEmployeeWhere = { status: "active" };
const [employmentStatusRows, departmentRows, departments, recentHires, recentDepartures, expiringContracts, validCertificateHolders, expiringCertificates] = await Promise.all([
const [employmentStatusRows, departmentRows, departments, recentHires, recentDepartures, expiringContracts, validCertificateHolders, expiringCertificates, activeCycle, completedCycle] = await Promise.all([
prisma.employee.groupBy({
by: ["employment_status"],
where: activeEmployeeWhere,
@@ -78,9 +94,53 @@ async function buildEmployeeStats(prisma, options = {}) {
},
},
}),
prisma.performanceReviewCycle.findFirst({
where: { status: "active" },
orderBy: [{ activated_at: "desc" }, { start_date: "desc" }],
}),
prisma.performanceReviewCycle.findFirst({
where: { status: "completed" },
orderBy: [{ completed_at: "desc" }, { end_date: "desc" }],
}),
]);
const departmentMap = new Map(departments.map((item) => [item.id, item.name]));
const performanceCycle = activeCycle ?? completedCycle ?? null;
let performanceSummary = {
cycle: null,
averageScore: null,
reviewedCount: 0,
distribution: [],
};
if (performanceCycle) {
const [reviewAggregate, reviewDistribution] = await Promise.all([
prisma.performanceReview.aggregate({
where: { cycle_id: performanceCycle.id },
_avg: { overall_score: true },
_count: { _all: true },
}),
prisma.performanceReview.groupBy({
by: ["overall_score"],
where: { cycle_id: performanceCycle.id },
_count: { _all: true },
}),
]);
performanceSummary = {
cycle: {
id: performanceCycle.id,
name: performanceCycle.name,
cycleType: performanceCycle.cycle_type,
status: performanceCycle.status,
startDate: performanceCycle.start_date?.toISOString?.()?.slice(0, 10) ?? null,
endDate: performanceCycle.end_date?.toISOString?.()?.slice(0, 10) ?? null,
},
averageScore: toNumber(reviewAggregate._avg.overall_score),
reviewedCount: reviewAggregate._count._all,
distribution: buildPerformanceDistribution(reviewDistribution),
};
}
return {
asOfDate: todayString,
@@ -117,6 +177,7 @@ async function buildEmployeeStats(prisma, options = {}) {
validHolderCount: validCertificateHolders.length,
expiringSoonCount: expiringCertificates,
},
performanceSummary,
};
}