155 行
4.2 KiB
JavaScript
155 行
4.2 KiB
JavaScript
function buildAvatarUrl(employeeId, avatarPath) {
|
|
return avatarPath ? `/api/avatars/${employeeId}` : null;
|
|
}
|
|
|
|
function compareByEmployeeNo(left, right) {
|
|
return left.employee_no.localeCompare(right.employee_no, "en");
|
|
}
|
|
|
|
function mapDepartmentSummary(department) {
|
|
if (!department) {
|
|
return null;
|
|
}
|
|
return {
|
|
id: department.id,
|
|
name: department.name,
|
|
};
|
|
}
|
|
|
|
function mapExternalManager(employee, departmentId, activeEmployeeById) {
|
|
if (!employee.direct_manager_id) {
|
|
return null;
|
|
}
|
|
|
|
const manager = activeEmployeeById.get(employee.direct_manager_id) ?? null;
|
|
if (!manager) {
|
|
return null;
|
|
}
|
|
|
|
if (manager.department_id === departmentId) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
id: manager.id,
|
|
employeeNo: manager.employee_no,
|
|
name: manager.name,
|
|
department: mapDepartmentSummary(manager.department),
|
|
};
|
|
}
|
|
|
|
function mapOrganizationNode(employee, departmentId, activeEmployeeById, childrenByManagerId, stack) {
|
|
const nextStack = new Set(stack);
|
|
nextStack.add(employee.id);
|
|
|
|
const children = (childrenByManagerId.get(employee.id) ?? [])
|
|
.slice()
|
|
.sort(compareByEmployeeNo)
|
|
.filter((item) => !nextStack.has(item.id))
|
|
.map((item) => mapOrganizationNode(item, departmentId, activeEmployeeById, childrenByManagerId, nextStack));
|
|
|
|
return {
|
|
id: employee.id,
|
|
employeeNo: employee.employee_no,
|
|
name: employee.name,
|
|
title: employee.title,
|
|
status: employee.status,
|
|
avatarUrl: buildAvatarUrl(employee.id, employee.avatar_path),
|
|
department: mapDepartmentSummary(employee.department),
|
|
externalManager: mapExternalManager(employee, departmentId, activeEmployeeById),
|
|
children,
|
|
};
|
|
}
|
|
|
|
function buildDepartmentSection(department, employees, activeEmployeeById) {
|
|
const departmentId = department.id;
|
|
const departmentEmployeeById = new Map(employees.map((item) => [item.id, item]));
|
|
const childrenByManagerId = new Map();
|
|
|
|
for (const employee of employees) {
|
|
childrenByManagerId.set(employee.id, []);
|
|
}
|
|
|
|
for (const employee of employees) {
|
|
if (!employee.direct_manager_id) {
|
|
continue;
|
|
}
|
|
const manager = activeEmployeeById.get(employee.direct_manager_id);
|
|
if (!manager || manager.department_id !== departmentId) {
|
|
continue;
|
|
}
|
|
if (!departmentEmployeeById.has(manager.id)) {
|
|
continue;
|
|
}
|
|
childrenByManagerId.get(manager.id).push(employee);
|
|
}
|
|
|
|
const roots = employees
|
|
.filter((employee) => {
|
|
if (!employee.direct_manager_id) {
|
|
return true;
|
|
}
|
|
const manager = activeEmployeeById.get(employee.direct_manager_id);
|
|
return !manager || manager.department_id !== departmentId;
|
|
})
|
|
.slice()
|
|
.sort(compareByEmployeeNo)
|
|
.map((employee) => mapOrganizationNode(employee, departmentId, activeEmployeeById, childrenByManagerId, new Set()));
|
|
|
|
return {
|
|
department: mapDepartmentSummary(department),
|
|
roots,
|
|
};
|
|
}
|
|
|
|
async function listOrganizationDepartments(prisma) {
|
|
const employees = await prisma.employee.findMany({
|
|
where: { status: "active" },
|
|
select: {
|
|
id: true,
|
|
employee_no: true,
|
|
name: true,
|
|
title: true,
|
|
status: true,
|
|
avatar_path: true,
|
|
department_id: true,
|
|
direct_manager_id: true,
|
|
department: {
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const activeEmployeeById = new Map(employees.map((item) => [item.id, item]));
|
|
const departmentGroups = new Map();
|
|
|
|
for (const employee of employees) {
|
|
const departmentId = employee.department?.id ?? employee.department_id;
|
|
if (!departmentId || !employee.department) {
|
|
continue;
|
|
}
|
|
if (!departmentGroups.has(departmentId)) {
|
|
departmentGroups.set(departmentId, {
|
|
department: employee.department,
|
|
employees: [],
|
|
});
|
|
}
|
|
departmentGroups.get(departmentId).employees.push(employee);
|
|
}
|
|
|
|
return [...departmentGroups.values()]
|
|
.sort((left, right) => left.department.name.localeCompare(right.department.name, "zh-CN"))
|
|
.map((group) => buildDepartmentSection(group.department, group.employees, activeEmployeeById));
|
|
}
|
|
|
|
module.exports = {
|
|
listOrganizationDepartments,
|
|
__testables: {
|
|
buildDepartmentSection,
|
|
mapOrganizationNode,
|
|
},
|
|
};
|