// src/services/department.service.js const { Department, Company, Branch, User } = require("../models"); const alertService = require("./alert.service"); class DepartmentService { async findByCompanyId(companyId) { const departments = await Department.findAll({ where: { companyId }, include: [ { model: User, attributes: ["id", "name", "email"], }, ], }); const buildHierarchy = (items) => { const map = new Map(); const roots = []; items.forEach((item) => { map.set(item.id, { ...item.get({ plain: true }), children: [] }); }); items.forEach((item) => { if (item.parentId) { const parent = map.get(item.parentId); if (parent) { parent.children.push(map.get(item.id)); } } else { roots.push(map.get(item.id)); } }); return roots; }; return buildHierarchy(departments); } async findById(id) { return await Department.findByPk(id, { include: [ { model: Department, as: "parent", attributes: ["id", "name"], }, { model: Department, as: "children", attributes: ["id", "name"], }, { model: User, attributes: ["id", "name", "email"], }, ], }); } // 추가: 앱에서 사용할 부서 목록 조회 메서드 async findAll(currentUser, format = "flat", includeInactive = false) { let where = {}; // company_admin은 자신의 회사 부서만 조회 가능 if (currentUser.role === "company_admin") { where.companyId = currentUser.companyId; } // 활성 상태 필터링 if (!includeInactive) { where.isActive = true; } const departments = await Department.findAll({ where, include: [ { model: Company, attributes: ["id", "name"], }, { model: Branch, attributes: ["id", "name"], }, { model: Department, as: "parent", attributes: ["id", "name"], }, { model: User, attributes: ["id", "name"], }, ], order: [ ["parentId", "ASC NULLS FIRST"], ["name", "ASC"], ], }); // 트리 구조로 반환 if (format === "tree") { const buildHierarchy = (items, parentId = null) => { return items .filter((item) => item.parentId === parentId) .map((item) => ({ ...item.get({ plain: true }), children: buildHierarchy(items, item.id), // 추가 정보 계산 userCount: item.Users ? item.Users.length : 0, path: this.getDepartmentPath(items, item), })); }; return buildHierarchy(departments); } // 평면 구조로 반환 return departments.map((dept) => { const json = dept.get({ plain: true }); // 추가 정보 계산 json.path = this.getDepartmentPath(departments, dept); json.childCount = departments.filter( (d) => d.parentId === dept.id ).length; json.userCount = dept.Users ? dept.Users.length : 0; return json; }); } // 부서 경로 생성 헬퍼 메서드 getDepartmentPath(departments, department) { const path = []; let current = department; while (current) { path.unshift(current.name); current = departments.find((d) => d.id === current.parentId); } return path.join(" > "); } async hasChildren(id) { const childCount = await Department.count({ where: { parentId: id }, }); return childCount > 0; } async createDepartment(departmentData, user) { const department = await Department.create(departmentData); await alertService.createAlert({ type: "info", message: `부서 ${department.name}이(가) ${user.name}에 의해 생성되었습니다.`, companyId: department.companyId, }); return department; } async updateDepartment(id, updateData, user) { const department = await Department.findByPk(id); if (!department) throw new Error("Department not found"); // parentId가 undefined이면 업데이트에서 제외, 명확히 null일 경우만 null로 설정 if (updateData.parentId === undefined) { delete updateData.parentId; } else if (updateData.parentId === null) { updateData.parentId = null; } const updatedDepartment = await department.update(updateData); await alertService.createAlert({ type: "info", message: `부서 ${department.name}이(가) ${user.name}에 의해 수정되었습니다.`, companyId: department.companyId, }); return updatedDepartment; } async deleteDepartment(id, user) { const department = await Department.findByPk(id); if (!department) throw new Error("Department not found"); const departmentName = department.name; const companyId = department.companyId; await department.destroy(); await alertService.createAlert({ type: "info", message: `부서 ${departmentName}이(가) ${user.name}에 의해 삭제되었습니다.`, companyId: companyId, }); return true; } } module.exports = new DepartmentService();