文件
hyb-oa/server/src/services/organization.js
T

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,
},
};