175 lines
4.6 KiB
TypeScript
175 lines
4.6 KiB
TypeScript
|
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
|||
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|||
|
|
import { CreateMenuDto } from './dto/create-menu.dto';
|
|||
|
|
import { UpdateMenuDto } from './dto/update-menu.dto';
|
|||
|
|
import { AuthService } from '../auth/auth.service';
|
|||
|
|
|
|||
|
|
@Injectable()
|
|||
|
|
export class MenusService {
|
|||
|
|
constructor(
|
|||
|
|
private prisma: PrismaService,
|
|||
|
|
private authService: AuthService,
|
|||
|
|
) {}
|
|||
|
|
|
|||
|
|
async create(createMenuDto: CreateMenuDto) {
|
|||
|
|
return this.prisma.menu.create({
|
|||
|
|
data: createMenuDto,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async findAll() {
|
|||
|
|
return this.prisma.menu.findMany({
|
|||
|
|
where: {
|
|||
|
|
parentId: null,
|
|||
|
|
},
|
|||
|
|
include: {
|
|||
|
|
children: {
|
|||
|
|
orderBy: {
|
|||
|
|
sort: 'asc',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
orderBy: {
|
|||
|
|
sort: 'asc',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async findOne(id: number) {
|
|||
|
|
const menu = await this.prisma.menu.findUnique({
|
|||
|
|
where: { id },
|
|||
|
|
include: {
|
|||
|
|
children: true,
|
|||
|
|
parent: true,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!menu) {
|
|||
|
|
throw new NotFoundException('菜单不存在');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return menu;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async update(id: number, updateMenuDto: UpdateMenuDto) {
|
|||
|
|
return this.prisma.menu.update({
|
|||
|
|
where: { id },
|
|||
|
|
data: updateMenuDto,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async remove(id: number) {
|
|||
|
|
return this.prisma.menu.delete({
|
|||
|
|
where: { id },
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前用户的菜单(根据权限过滤)
|
|||
|
|
* @param userId 用户ID
|
|||
|
|
* @param tenantId 租户ID
|
|||
|
|
* @returns 过滤后的菜单树
|
|||
|
|
*/
|
|||
|
|
async findUserMenus(userId: number, tenantId: number) {
|
|||
|
|
// 获取用户的所有权限
|
|||
|
|
const userPermissions = await this.authService.getUserPermissions(userId);
|
|||
|
|
|
|||
|
|
// 获取租户分配的菜单ID
|
|||
|
|
const tenantMenus = await this.prisma.tenantMenu.findMany({
|
|||
|
|
where: { tenantId },
|
|||
|
|
});
|
|||
|
|
const menuIds = tenantMenus.map((tm) => tm.menuId);
|
|||
|
|
|
|||
|
|
if (menuIds.length === 0) {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取租户分配的所有菜单(包括父菜单)
|
|||
|
|
const allMenus = await this.prisma.menu.findMany({
|
|||
|
|
where: {
|
|||
|
|
OR: [
|
|||
|
|
{ id: { in: menuIds } },
|
|||
|
|
{ children: { some: { id: { in: menuIds } } } },
|
|||
|
|
],
|
|||
|
|
validState: 1, // 只获取有效的菜单
|
|||
|
|
},
|
|||
|
|
orderBy: {
|
|||
|
|
sort: 'asc',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 构建树形结构
|
|||
|
|
const buildTree = (menus: any[], parentId: number | null = null): any[] => {
|
|||
|
|
return menus
|
|||
|
|
.filter((menu) => menu.parentId === parentId)
|
|||
|
|
.map((menu) => ({
|
|||
|
|
...menu,
|
|||
|
|
children: buildTree(menus, menu.id),
|
|||
|
|
}));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 先构建树
|
|||
|
|
const menuTree = buildTree(allMenus);
|
|||
|
|
|
|||
|
|
// 过滤菜单:如果菜单有permission字段,检查用户是否有该权限;如果没有permission字段,则显示
|
|||
|
|
const filterMenus = (menus: any[]): any[] => {
|
|||
|
|
return menus
|
|||
|
|
.filter((menu) => {
|
|||
|
|
// 如果菜单没有设置权限要求,则显示
|
|||
|
|
if (!menu.permission) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
// 如果设置了权限要求,检查用户是否有该权限
|
|||
|
|
return userPermissions.includes(menu.permission);
|
|||
|
|
})
|
|||
|
|
.map((menu) => {
|
|||
|
|
const filtered = { ...menu };
|
|||
|
|
// 递归过滤子菜单
|
|||
|
|
if (menu.children && menu.children.length > 0) {
|
|||
|
|
filtered.children = filterMenus(menu.children);
|
|||
|
|
}
|
|||
|
|
return filtered;
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 过滤菜单树
|
|||
|
|
const filteredTree = filterMenus(menuTree);
|
|||
|
|
|
|||
|
|
// 移除没有子菜单且没有path的父菜单(空菜单)
|
|||
|
|
const removeEmptyParents = (menus: any[]): any[] => {
|
|||
|
|
return menus
|
|||
|
|
.map((menu) => {
|
|||
|
|
const hasChildren = menu.children && menu.children.length > 0;
|
|||
|
|
const hasPath = menu.path && menu.path.trim() !== '';
|
|||
|
|
|
|||
|
|
// 如果有子菜单,递归处理
|
|||
|
|
if (hasChildren) {
|
|||
|
|
const processedChildren = removeEmptyParents(menu.children);
|
|||
|
|
// 如果处理后还有子菜单,保留此菜单
|
|||
|
|
if (processedChildren.length > 0) {
|
|||
|
|
return {
|
|||
|
|
...menu,
|
|||
|
|
children: processedChildren,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
// 如果处理后没有子菜单,但有path,保留此菜单(作为叶子节点)
|
|||
|
|
if (hasPath) {
|
|||
|
|
return {
|
|||
|
|
...menu,
|
|||
|
|
children: [],
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
// 既没有子菜单也没有path,移除
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 叶子节点,保留
|
|||
|
|
return menu;
|
|||
|
|
})
|
|||
|
|
.filter((menu) => menu !== null);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return removeEmptyParents(filteredTree);
|
|||
|
|
}
|
|||
|
|
}
|