library-picturebook-activity/backend/src/menus/menus.service.ts
2025-11-23 14:04:20 +08:00

175 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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