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) {} private async loadActiveAccount(actorId: bigint) { const account = await this.prisma.hqAccount.findUnique({ where: { id: actorId }, select: { adminRole: true, status: true }, }); if (!account || account.status !== 'ACTIVE') { throw new ForbiddenException('HQ 账号不可用'); } return account; } async resolveAccess(actorId: bigint): Promise<{ keys: HqPermissionKey[]; isSuperAdmin: boolean; }> { const account = await this.loadActiveAccount(actorId); const userRows = await this.prisma.hqAccountPermission.findMany({ where: { hqAccountId: actorId }, select: { permissionKey: true }, }); const userKeys = userRows.map((r) => r.permissionKey); if (account.adminRole === 'SUPER_ADMIN') { // 超管拥有权限目录内全部项(含后续新增),另含危险操作与用户级附加项 return { isSuperAdmin: true, keys: expandHqPermissionKeys([ ...HQ_PERMISSION_CATALOG.map((p) => p.key), ...userKeys, ]), }; } const roleRows = await this.prisma.hqRolePermission.findMany({ where: { adminRole: account.adminRole }, select: { permissionKey: true }, }); const roleKeys = roleRows.length > 0 ? roleRows.map((r) => r.permissionKey) : [...(HQ_ROLE_DEFAULT_PERMISSIONS[account.adminRole] ?? [])]; return { isSuperAdmin: false, keys: expandHqPermissionKeys([...roleKeys, ...userKeys]), }; } async resolveEffectiveKeys(actorId: bigint): Promise { const { keys } = await this.resolveAccess(actorId); return keys; } } @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, isSuperAdmin } = await this.resolver.resolveAccess(user.actorId); req.hqPermissionKeys = keys; const required = this.reflector.getAllAndOverride(HQ_PERMISSIONS_KEY, [ context.getHandler(), context.getClass(), ]) ?? []; if (!required.length) return true; if (isSuperAdmin) 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; } }