import { CanActivate, ExecutionContext, ForbiddenException, Injectable, SetMetadata, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { HQ_PERMISSION_CATALOG, HQ_ROLE_DEFAULT_PERMISSIONS, expandHqPermissionKeys, hasAnySystemSettingsPermission, type HqPermissionKey, } from '@dukang/shared-types'; import { PrismaService } from '../prisma/prisma.module'; import type { AuthUser } from './jwt-auth.guard'; export const HQ_PERMISSIONS_KEY = 'hq:permissions'; export const RequireHqPermissions = (...keys: string[]) => SetMetadata(HQ_PERMISSIONS_KEY, keys); /** 任意一项系统设置分组权限即可访问系统设置接口 */ export const RequireAnySystemSettings = () => SetMetadata(HQ_PERMISSIONS_KEY, ['__any_system_settings__']); @Injectable() export class HqPermissionsResolver { constructor(private readonly prisma: PrismaService) {} async resolveEffectiveKeys(actorId: bigint): Promise { const account = await this.prisma.hqAccount.findUnique({ where: { id: actorId }, select: { adminRole: true, status: true }, }); if (!account || account.status !== 'ACTIVE') { throw new ForbiddenException('HQ 账号不可用'); } if (account.adminRole === 'SUPER_ADMIN') { return HQ_PERMISSION_CATALOG.map((p) => p.key); } const [roleRows, userRows] = await Promise.all([ this.prisma.hqRolePermission.findMany({ where: { adminRole: account.adminRole }, select: { permissionKey: true }, }), this.prisma.hqAccountPermission.findMany({ where: { hqAccountId: actorId }, select: { permissionKey: true }, }), ]); const roleKeys = roleRows.length > 0 ? roleRows.map((r) => r.permissionKey) : [...(HQ_ROLE_DEFAULT_PERMISSIONS[account.adminRole] ?? [])]; return expandHqPermissionKeys([ ...roleKeys, ...userRows.map((r) => r.permissionKey), ]); } } @Injectable() export class HqPermissionGuard implements CanActivate { constructor( private readonly resolver: HqPermissionsResolver, private readonly reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { const req = context.switchToHttp().getRequest(); const user = req.user as AuthUser | undefined; if (!user || user.actorType !== 'HQ') { throw new ForbiddenException('需要 HQ 权限'); } const keys = await this.resolver.resolveEffectiveKeys(user.actorId); req.hqPermissionKeys = keys; const required = this.reflector.getAllAndOverride(HQ_PERMISSIONS_KEY, [ context.getHandler(), context.getClass(), ]) ?? []; if (!required.length) return true; if (required.includes('__any_system_settings__')) { if (!hasAnySystemSettingsPermission(keys)) { throw new ForbiddenException('无系统设置权限'); } return true; } if (!required.some((k) => keys.includes(k as HqPermissionKey))) { throw new ForbiddenException('权限不足'); } return true; } }