R39: 完成员工档案汇报链查询
这个提交包含在:
@@ -8,6 +8,7 @@ const { FEATURE_FLAG_KEYS, evaluateFeatureFlag } = require("../services/featureF
|
||||
|
||||
const UUID_SCHEMA = z.string().uuid();
|
||||
const DATE_SCHEMA = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
||||
const REPORTING_CHAIN_MAX_DEPTH = 12;
|
||||
const PROFILE_PATCH_SCHEMA = z
|
||||
.object({
|
||||
departmentId: UUID_SCHEMA.optional(),
|
||||
@@ -121,6 +122,17 @@ function mapLifecycleEvent(item) {
|
||||
};
|
||||
}
|
||||
|
||||
function mapOrganizationNode(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
employeeNo: item.employee_no,
|
||||
name: item.name,
|
||||
title: item.title,
|
||||
status: item.status,
|
||||
department: item.department ? { id: item.department.id, name: item.department.name } : null,
|
||||
};
|
||||
}
|
||||
|
||||
async function getLifecycleBlockers(prisma, employeeId) {
|
||||
const [openAssetCount, pendingWorkflowTaskCount] = await Promise.all([
|
||||
prisma.assetCard.count({ where: { assignee_employee_id: employeeId } }),
|
||||
@@ -199,6 +211,59 @@ async function maybeAdvanceLifecycle(tx, employeeId) {
|
||||
return { autoChanged: false, completedOffboarding: false, blockers: null };
|
||||
}
|
||||
|
||||
async function buildEmployeeOrganization(prisma, employee) {
|
||||
const reportingChain = [];
|
||||
const seenEmployeeIds = new Set([employee.id]);
|
||||
let currentManagerId = employee.direct_manager_id ?? null;
|
||||
let depth = 0;
|
||||
|
||||
while (currentManagerId && depth < REPORTING_CHAIN_MAX_DEPTH) {
|
||||
if (seenEmployeeIds.has(currentManagerId)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const manager = await prisma.employee.findUnique({
|
||||
where: { id: currentManagerId },
|
||||
select: {
|
||||
id: true,
|
||||
employee_no: true,
|
||||
name: true,
|
||||
title: true,
|
||||
status: true,
|
||||
direct_manager_id: true,
|
||||
department: { select: { id: true, name: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!manager) {
|
||||
break;
|
||||
}
|
||||
|
||||
reportingChain.push(mapOrganizationNode(manager));
|
||||
seenEmployeeIds.add(manager.id);
|
||||
currentManagerId = manager.direct_manager_id ?? null;
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
const directReports = await prisma.employee.findMany({
|
||||
where: { direct_manager_id: employee.id },
|
||||
orderBy: [{ status: "asc" }, { name: "asc" }],
|
||||
select: {
|
||||
id: true,
|
||||
employee_no: true,
|
||||
name: true,
|
||||
title: true,
|
||||
status: true,
|
||||
department: { select: { id: true, name: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
reportingChain: [...reportingChain.reverse(), mapOrganizationNode(employee)],
|
||||
directReports: directReports.map(mapOrganizationNode),
|
||||
};
|
||||
}
|
||||
|
||||
async function buildEmployeeProfile(prisma, employeeId) {
|
||||
const [employee, checklistTasks, lifecycleEvents, assignedAssets, blockers] = await Promise.all([
|
||||
prisma.employee.findUnique({
|
||||
@@ -241,6 +306,8 @@ async function buildEmployeeProfile(prisma, employeeId) {
|
||||
throw new AppError({ code: "SYS_404", message: "Not Found", statusCode: 404 });
|
||||
}
|
||||
|
||||
const organization = await buildEmployeeOrganization(prisma, employee);
|
||||
|
||||
return {
|
||||
employee: {
|
||||
id: employee.id,
|
||||
@@ -270,6 +337,7 @@ async function buildEmployeeProfile(prisma, employeeId) {
|
||||
checklistTasks: checklistTasks.map(mapChecklistTask),
|
||||
blockers,
|
||||
},
|
||||
organization,
|
||||
assignedAssets: assignedAssets.map((item) => ({
|
||||
id: item.id,
|
||||
assetNo: item.asset_no,
|
||||
@@ -521,3 +589,9 @@ async function employeesRoutes(fastify) {
|
||||
}
|
||||
|
||||
module.exports = employeesRoutes;
|
||||
|
||||
module.exports.__testables = {
|
||||
buildEmployeeOrganization,
|
||||
buildEmployeeProfile,
|
||||
mapOrganizationNode,
|
||||
};
|
||||
|
||||
在新工单中引用
屏蔽一个用户