library-picturebook-activity/backend/src/tenants/tenants.service.ts

344 lines
8.3 KiB
TypeScript
Raw Normal View History

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