import { Injectable, NotFoundException, BadRequestException, ForbiddenException, } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateTenantDto } from './dto/create-tenant.dto'; import { UpdateTenantDto } from './dto/update-tenant.dto'; @Injectable() export class TenantsService { constructor(private prisma: PrismaService) {} /** * 检查当前用户所属租户是否为超级租户 */ private async checkSuperTenant(currentTenantId?: number): Promise { if (!currentTenantId) { throw new ForbiddenException('无法确定当前租户信息'); } const currentTenant = await this.prisma.tenant.findUnique({ where: { id: currentTenantId }, }); if (!currentTenant) { throw new ForbiddenException('当前租户不存在'); } if (currentTenant.isSuper !== 1) { throw new ForbiddenException('只有超级租户才能操作租户管理'); } } async create( createTenantDto: CreateTenantDto, creatorId?: number, currentTenantId?: number, ) { // 检查是否为超级租户 await this.checkSuperTenant(currentTenantId); const { menuIds, ...tenantData } = createTenantDto; // 检查租户编码是否已存在 const existingTenant = await this.prisma.tenant.findUnique({ where: { code: tenantData.code }, }); if (existingTenant) { throw new BadRequestException('租户编码已存在'); } // 如果提供了域名,检查域名是否已存在 if (tenantData.domain) { const existingDomain = await this.prisma.tenant.findUnique({ where: { domain: tenantData.domain }, }); if (existingDomain) { throw new BadRequestException('租户域名已存在'); } } return this.prisma.tenant.create({ data: { ...tenantData, creator: creatorId, menus: menuIds && menuIds.length > 0 ? { create: menuIds.map((menuId) => ({ menuId, })), } : undefined, }, include: { menus: { include: { menu: true, }, }, }, }); } async findAll(page: number = 1, pageSize: number = 10) { const skip = (page - 1) * pageSize; const [list, total] = await Promise.all([ this.prisma.tenant.findMany({ skip, take: pageSize, include: { menus: { include: { menu: true, }, }, _count: { select: { users: true, roles: true, }, }, }, orderBy: { createTime: 'desc', }, }), this.prisma.tenant.count(), ]); return { list, total, page, pageSize, }; } async findOne(id: number) { const tenant = await this.prisma.tenant.findUnique({ where: { id }, include: { menus: { include: { menu: true, }, }, _count: { select: { users: true, roles: true, }, }, }, }); if (!tenant) { throw new NotFoundException('租户不存在'); } return tenant; } async findByCode(code: string) { return this.prisma.tenant.findUnique({ where: { code }, include: { menus: { include: { menu: true, }, }, }, }); } async findByDomain(domain: string) { return this.prisma.tenant.findUnique({ where: { domain }, include: { menus: { include: { menu: true, }, }, }, }); } async update( id: number, updateTenantDto: UpdateTenantDto, modifierId?: number, currentTenantId?: number, ) { // 检查是否为超级租户 await this.checkSuperTenant(currentTenantId); const { menuIds, ...tenantData } = updateTenantDto; // 检查租户是否存在 await this.findOne(id); // 如果更新了code,检查是否冲突 if (tenantData.code) { const existingTenant = await this.prisma.tenant.findFirst({ where: { code: tenantData.code, id: { not: id }, }, }); if (existingTenant) { throw new BadRequestException('租户编码已存在'); } } // 如果更新了domain,检查是否冲突 if (tenantData.domain) { const existingDomain = await this.prisma.tenant.findFirst({ where: { domain: tenantData.domain, id: { not: id }, }, }); if (existingDomain) { throw new BadRequestException('租户域名已存在'); } } const data: any = { ...tenantData, modifier: modifierId, }; // 如果提供了 menuIds,更新菜单关联 if (menuIds !== undefined) { // 先删除所有现有菜单关联 await this.prisma.tenantMenu.deleteMany({ where: { tenantId: id }, }); // 创建新的菜单关联 if (menuIds.length > 0) { data.menus = { create: menuIds.map((menuId) => ({ menuId, })), }; } } return this.prisma.tenant.update({ where: { id }, data, include: { menus: { include: { menu: true, }, }, }, }); } async remove(id: number, currentTenantId?: number) { // 检查是否为超级租户 await this.checkSuperTenant(currentTenantId); // 检查租户是否存在 await this.findOne(id); // 检查要删除的租户是否为超级租户 const tenant = await this.prisma.tenant.findUnique({ where: { id }, }); if (tenant?.isSuper === 1) { throw new BadRequestException('不能删除超级租户'); } return this.prisma.tenant.delete({ where: { id }, }); } /** * 获取租户的菜单树(根据租户分配的菜单) */ async getTenantMenus(tenantId: number) { const tenant = await this.findOne(tenantId); if (!tenant) { throw new NotFoundException('租户不存在'); } // 获取租户分配的所有菜单ID const tenantMenus = await this.prisma.tenantMenu.findMany({ where: { tenantId }, include: { menu: true, }, }); 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); // 过滤:只保留被分配的菜单及其父菜单 const filterMenus = (menus: any[]): any[] => { return menus .filter((menu) => { // 如果菜单被分配,保留 if (menuIds.includes(menu.id)) { return true; } // 如果有子菜单被分配,保留 if (menu.children && menu.children.length > 0) { const filteredChildren = filterMenus(menu.children); return filteredChildren.length > 0; } return false; }) .map((menu) => { const filtered = { ...menu }; if (menu.children && menu.children.length > 0) { filtered.children = filterMenus(menu.children); } return filtered; }); }; return filterMenus(menuTree); } }