import { CanActivate, ExecutionContext, ForbiddenException, Injectable, SetMetadata, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { HQ_DANGEROUS_PERMISSION_KEYS, HQ_ROLE_DEFAULT_PERMISSIONS, expandHqPermissionKeys, hasAnySystemSettingsPermission, hqBasePermissionKeys, 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 账号不可用'); } 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 expandHqPermissionKeys([ ...hqBasePermissionKeys(), ...HQ_DANGEROUS_PERMISSION_KEYS, ...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 expandHqPermissionKeys([...roleKeys, ...userKeys]); } } @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; } }