2024-09-03 20:19:33 +08:00
|
|
|
|
import { Injectable } from '@nestjs/common';
|
2024-12-30 08:26:40 +08:00
|
|
|
|
import {
|
|
|
|
|
db,
|
|
|
|
|
DepartmentMethodSchema,
|
|
|
|
|
DeptAncestry,
|
|
|
|
|
getUniqueItems,
|
|
|
|
|
ObjectType,
|
|
|
|
|
Prisma,
|
2025-01-06 08:45:23 +08:00
|
|
|
|
} from '@nice/common';
|
2024-12-30 08:26:40 +08:00
|
|
|
|
import { BaseTreeService } from '../base/base.tree.service';
|
|
|
|
|
import { z } from 'zod';
|
|
|
|
|
import { mapToDeptSimpleTree, getStaffsByDeptIds } from './utils';
|
|
|
|
|
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
2024-09-03 20:19:33 +08:00
|
|
|
|
@Injectable()
|
2024-12-30 08:26:40 +08:00
|
|
|
|
export class DepartmentService extends BaseTreeService<Prisma.DepartmentDelegate> {
|
|
|
|
|
constructor() {
|
|
|
|
|
super(db, ObjectType.DEPARTMENT, 'deptAncestry', true);
|
|
|
|
|
}
|
|
|
|
|
async getDescendantIdsInDomain(
|
|
|
|
|
ancestorId: string,
|
|
|
|
|
includeAncestor = true,
|
|
|
|
|
): Promise<string[]> {
|
|
|
|
|
// 如果没有提供部门ID,返回空数组
|
|
|
|
|
if (!ancestorId) return [];
|
|
|
|
|
// 获取祖先部门信息
|
|
|
|
|
const ancestorDepartment = await db.department.findUnique({
|
|
|
|
|
where: { id: ancestorId },
|
|
|
|
|
});
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 如果未找到部门,返回空数组
|
|
|
|
|
if (!ancestorDepartment) return [];
|
|
|
|
|
|
|
|
|
|
// 查询同域下以指定部门为祖先的部门血缘关系
|
|
|
|
|
const departmentAncestries = await db.deptAncestry.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
ancestorId: ancestorId,
|
|
|
|
|
descendant: {
|
|
|
|
|
domainId: ancestorDepartment.domainId,
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 提取子部门ID列表
|
2025-02-06 16:32:52 +08:00
|
|
|
|
const descendantDepartmentIds = departmentAncestries.map(
|
2024-12-30 08:26:40 +08:00
|
|
|
|
(ancestry) => ancestry.descendantId,
|
|
|
|
|
);
|
|
|
|
|
// 根据参数决定是否包含祖先部门ID
|
|
|
|
|
if (includeAncestor && ancestorId) {
|
|
|
|
|
descendantDepartmentIds.push(ancestorId);
|
|
|
|
|
}
|
|
|
|
|
return descendantDepartmentIds;
|
|
|
|
|
}
|
|
|
|
|
async getDescendantDomainIds(
|
|
|
|
|
ancestorDomainId: string,
|
|
|
|
|
includeAncestorDomain = true,
|
|
|
|
|
): Promise<string[]> {
|
|
|
|
|
if (!ancestorDomainId) return [];
|
|
|
|
|
// 查询所有以指定域ID为祖先的域的血缘关系
|
|
|
|
|
const domainAncestries = await db.deptAncestry.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
ancestorId: ancestorDomainId,
|
|
|
|
|
descendant: {
|
|
|
|
|
isDomain: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 提取子域的ID列表
|
2025-02-06 16:32:52 +08:00
|
|
|
|
const descendantDomainIds = domainAncestries.map(
|
2024-12-30 08:26:40 +08:00
|
|
|
|
(ancestry) => ancestry.descendantId,
|
|
|
|
|
);
|
|
|
|
|
// 根据参数决定是否包含祖先域ID
|
|
|
|
|
if (includeAncestorDomain && ancestorDomainId) {
|
|
|
|
|
descendantDomainIds.push(ancestorDomainId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return descendantDomainIds;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 获取指定DOM下的对应name的单位
|
2024-12-30 08:26:40 +08:00
|
|
|
|
* @param domainId
|
2024-09-03 20:19:33 +08:00
|
|
|
|
* @param name
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async findInDomain(domainId: string, name: string) {
|
2024-09-03 20:19:33 +08:00
|
|
|
|
const subDepts = await db.deptAncestry.findMany({
|
|
|
|
|
where: {
|
2024-12-30 08:26:40 +08:00
|
|
|
|
ancestorId: domainId,
|
2024-09-03 20:19:33 +08:00
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
descendant: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const dept = subDepts.find((item) => item.descendant.name === name);
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
return dept.descendant;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
private async setDomainId(parentId: string) {
|
|
|
|
|
const parent = await this.findUnique({ where: { id: parentId } });
|
|
|
|
|
return parent.isDomain ? parentId : parent.domainId;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async create(args: Prisma.DepartmentCreateArgs) {
|
|
|
|
|
if (args.data.parentId) {
|
|
|
|
|
args.data.domainId = await this.setDomainId(args.data.parentId);
|
|
|
|
|
}
|
|
|
|
|
const result = await super.create(args);
|
|
|
|
|
EventBus.emit('dataChanged', {
|
|
|
|
|
type: this.objectType,
|
|
|
|
|
operation: CrudOperation.CREATED,
|
|
|
|
|
data: result,
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
2024-12-30 08:26:40 +08:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async update(args: Prisma.DepartmentUpdateArgs) {
|
|
|
|
|
if (args.data.parentId) {
|
|
|
|
|
args.data.domainId = await this.setDomainId(args.data.parentId as string);
|
|
|
|
|
}
|
|
|
|
|
const result = await super.update(args);
|
|
|
|
|
EventBus.emit('dataChanged', {
|
|
|
|
|
type: this.objectType,
|
|
|
|
|
operation: CrudOperation.UPDATED,
|
|
|
|
|
data: result,
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
2024-12-30 08:26:40 +08:00
|
|
|
|
return result;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-12-30 08:26:40 +08:00
|
|
|
|
* 删除现有单位并清理DeptAncestry关系。
|
|
|
|
|
* @param data - 用于删除现有单位的数据。
|
|
|
|
|
* @returns 删除的单位对象。
|
2024-09-03 20:19:33 +08:00
|
|
|
|
*/
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async softDeleteByIds(ids: string[]) {
|
|
|
|
|
const descendantIds = await this.getDescendantIds(ids, true);
|
|
|
|
|
const result = await super.softDeleteByIds(descendantIds);
|
|
|
|
|
EventBus.emit('dataChanged', {
|
|
|
|
|
type: this.objectType,
|
|
|
|
|
operation: CrudOperation.DELETED,
|
|
|
|
|
data: result,
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
2024-12-30 08:26:40 +08:00
|
|
|
|
return result;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-12-30 08:26:40 +08:00
|
|
|
|
* 获取指定部门及其所有子部门的员工。
|
|
|
|
|
* @param deptIds - 要获取员工ID的部门ID数组。
|
|
|
|
|
* @returns 包含所有员工ID的数组。
|
2024-09-03 20:19:33 +08:00
|
|
|
|
*/
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async getStaffsInDepts(deptIds: string[]) {
|
|
|
|
|
const allDeptIds = await this.getDescendantIds(deptIds, true);
|
|
|
|
|
return await getStaffsByDeptIds(Array.from(allDeptIds));
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async getStaffIdsInDepts(deptIds: string[]) {
|
|
|
|
|
const result = await this.getStaffsInDepts(deptIds);
|
|
|
|
|
return result.map((s) => s.id);
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
/**
|
2024-12-30 08:26:40 +08:00
|
|
|
|
* 根据部门名称列表和域ID获取多个部门的ID。
|
|
|
|
|
*
|
|
|
|
|
* @param {string[]} names - 部门名称列表
|
|
|
|
|
* @param {string} domainId - 域ID
|
|
|
|
|
* @returns {Promise<Record<string, string | null>>} - 返回一个对象,键为部门名称,值为部门ID或null
|
2024-09-03 20:19:33 +08:00
|
|
|
|
*/
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async getDeptIdsByNames(
|
|
|
|
|
names: string[],
|
|
|
|
|
domainId: string,
|
|
|
|
|
): Promise<Record<string, string | null>> {
|
|
|
|
|
// 使用 Prisma 的 findMany 方法批量查询部门信息,优化性能
|
|
|
|
|
const depts = await db.department.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
// 查询条件:部门名称在给定的名称列表中
|
|
|
|
|
name: { in: names },
|
|
|
|
|
// 查询条件:部门在指定的域下(通过ancestors关系查询)
|
|
|
|
|
ancestors: {
|
|
|
|
|
some: {
|
|
|
|
|
ancestorId: domainId,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// 选择查询的字段:只查询部门的id和name字段
|
|
|
|
|
select: {
|
|
|
|
|
id: true,
|
|
|
|
|
name: true,
|
|
|
|
|
},
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 创建一个Map对象,将部门名称映射到部门ID
|
|
|
|
|
const deptMap = new Map(depts.map((dept) => [dept.name, dept.id]));
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 初始化结果对象,用于存储最终的结果
|
|
|
|
|
const result: Record<string, string | null> = {};
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 遍历传入的部门名称列表
|
|
|
|
|
for (const name of names) {
|
|
|
|
|
// 从Map中获取部门ID,如果不存在则返回null
|
|
|
|
|
result[name] = deptMap.get(name) || null;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 返回最终的结果对象
|
|
|
|
|
return result;
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async getChildSimpleTree(
|
|
|
|
|
data: z.infer<typeof DepartmentMethodSchema.getSimpleTree>,
|
|
|
|
|
) {
|
|
|
|
|
const { domain, deptIds, rootId } = data;
|
|
|
|
|
// 提取非空 deptIds
|
|
|
|
|
const validDeptIds = deptIds?.filter((id) => id !== null) ?? [];
|
|
|
|
|
const hasNullDeptId = deptIds?.includes(null) ?? false;
|
|
|
|
|
const [childrenData, selfData] = await Promise.all([
|
|
|
|
|
db.deptAncestry.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
...(deptIds && {
|
|
|
|
|
OR: [
|
|
|
|
|
...(validDeptIds.length
|
|
|
|
|
? [{ ancestorId: { in: validDeptIds } }]
|
|
|
|
|
: []),
|
|
|
|
|
...(hasNullDeptId ? [{ ancestorId: null }] : []),
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
ancestorId: rootId,
|
|
|
|
|
relDepth: 1,
|
|
|
|
|
descendant: { isDomain: domain },
|
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
descendant: { include: { children: true, deptStaffs: true } },
|
|
|
|
|
},
|
|
|
|
|
orderBy: { descendant: { order: 'asc' } },
|
|
|
|
|
}),
|
|
|
|
|
deptIds
|
|
|
|
|
? db.department.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
...(deptIds && {
|
|
|
|
|
OR: [
|
|
|
|
|
...(validDeptIds.length
|
|
|
|
|
? [{ id: { in: validDeptIds } }]
|
|
|
|
|
: []),
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
isDomain: domain,
|
|
|
|
|
},
|
|
|
|
|
include: { children: true },
|
|
|
|
|
orderBy: { order: 'asc' },
|
|
|
|
|
})
|
|
|
|
|
: [],
|
|
|
|
|
]);
|
|
|
|
|
const children = childrenData
|
|
|
|
|
.map(({ descendant }) => descendant)
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.map(mapToDeptSimpleTree);
|
|
|
|
|
const selfItems = selfData.map(mapToDeptSimpleTree);
|
|
|
|
|
return getUniqueItems([...children, ...selfItems], 'id');
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-12-30 08:26:40 +08:00
|
|
|
|
* 获取父级部门简单树结构的异步方法
|
|
|
|
|
*
|
|
|
|
|
* @param data - 包含部门ID、域和根ID的输入参数
|
|
|
|
|
* @returns 返回唯一的部门简单树结构数组
|
|
|
|
|
*
|
|
|
|
|
* 方法整体设计思路:
|
|
|
|
|
* 1. 并行查询父级部门ancestry和自身部门数据
|
|
|
|
|
* 2. 查询根节点的直接子节点
|
|
|
|
|
* 3. 通过自定义过滤函数筛选超出根节点层级的祖先节点
|
|
|
|
|
* 4. 将查询结果映射为简单树结构
|
|
|
|
|
* 5. 去重并返回最终结果
|
2024-09-03 20:19:33 +08:00
|
|
|
|
*/
|
2024-12-30 08:26:40 +08:00
|
|
|
|
async getParentSimpleTree(
|
|
|
|
|
data: z.infer<typeof DepartmentMethodSchema.getSimpleTree>,
|
|
|
|
|
) {
|
|
|
|
|
// 解构输入参数
|
|
|
|
|
const { deptIds, domain, rootId } = data;
|
|
|
|
|
// 并行查询父级部门ancestry和自身部门数据
|
|
|
|
|
// 使用Promise.all提高查询效率,减少等待时间
|
|
|
|
|
const [parentData, selfData] = await Promise.all([
|
|
|
|
|
// 查询指定部门的所有祖先节点,包含子节点和父节点信息
|
|
|
|
|
db.deptAncestry.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
descendantId: { in: deptIds }, // 查询条件:descendant在给定的部门ID列表中
|
|
|
|
|
ancestor: { isDomain: domain }, // 限定域
|
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
ancestor: {
|
|
|
|
|
include: {
|
|
|
|
|
children: true, // 包含子节点信息
|
|
|
|
|
parent: true, // 包含父节点信息
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
orderBy: { ancestor: { order: 'asc' } }, // 按祖先节点顺序升序排序
|
|
|
|
|
}),
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 查询自身部门数据
|
|
|
|
|
db.department.findMany({
|
|
|
|
|
where: { id: { in: deptIds }, isDomain: domain },
|
|
|
|
|
include: { children: true }, // 包含子节点信息
|
|
|
|
|
orderBy: { order: 'asc' }, // 按顺序升序排序
|
|
|
|
|
}),
|
|
|
|
|
]);
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 查询根节点的直接子节点
|
|
|
|
|
const rootChildren = await db.deptAncestry.findMany({
|
2024-09-03 20:19:33 +08:00
|
|
|
|
where: {
|
2024-12-30 08:26:40 +08:00
|
|
|
|
ancestorId: rootId, // 祖先ID为根ID
|
|
|
|
|
descendant: { isDomain: domain }, // 限定域
|
|
|
|
|
},
|
2024-09-03 20:19:33 +08:00
|
|
|
|
});
|
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
/**
|
|
|
|
|
* 判断祖先节点是否超出根节点层级的函数
|
|
|
|
|
*
|
|
|
|
|
* @param ancestor - 祖先节点
|
|
|
|
|
* @returns 是否超出根节点层级
|
|
|
|
|
*/
|
|
|
|
|
const isDirectDescendantOfRoot = (ancestor: DeptAncestry): boolean => {
|
|
|
|
|
return (
|
|
|
|
|
rootChildren.findIndex(
|
|
|
|
|
(child) => child.descendantId === ancestor.ancestorId,
|
|
|
|
|
) !== -1
|
|
|
|
|
);
|
|
|
|
|
};
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 处理父级节点:过滤并映射为简单树结构
|
|
|
|
|
const parents = parentData
|
|
|
|
|
.map(({ ancestor }) => ancestor) // 提取祖先节点
|
|
|
|
|
.filter(
|
|
|
|
|
(ancestor) => ancestor && isDirectDescendantOfRoot(ancestor as any),
|
|
|
|
|
) // 过滤有效且超出根节点层级的节点
|
|
|
|
|
.map(mapToDeptSimpleTree); // 映射为简单树结构
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 处理自身节点:映射为简单树结构
|
|
|
|
|
const selfItems = selfData.map(mapToDeptSimpleTree);
|
2024-09-03 20:19:33 +08:00
|
|
|
|
|
2024-12-30 08:26:40 +08:00
|
|
|
|
// 合并并去重父级和自身节点,返回唯一项
|
|
|
|
|
return getUniqueItems([...parents, ...selfItems], 'id');
|
|
|
|
|
}
|
2024-09-03 20:19:33 +08:00
|
|
|
|
}
|