import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { HQ_ADMIN_ROLE_VALUES, HQ_PERMISSION_CATALOG, HQ_ROLE_DEFAULT_PERMISSIONS, LEGACY_SYSTEM_SETTINGS_KEY, computeHqEffectivePermissionKeys, expandHqPermissionKeys, type HqAdminRoleValue, type HqPermissionKey, } from '@dukang/shared-types'; import { PrismaService } from '../../common/prisma/prisma.module'; import { serializeBigInt } from '../../common/decorators/current-user.decorator'; const VALID_PERMISSION_KEYS = new Set([ ...HQ_PERMISSION_CATALOG.map((p) => p.key), LEGACY_SYSTEM_SETTINGS_KEY, ]); const EDITABLE_ROLES = new Set( HQ_ADMIN_ROLE_VALUES.filter((r) => r !== 'SUPER_ADMIN'), ); function assertPermissionKeys(keys: string[]) { const invalid = keys.filter((key) => !VALID_PERMISSION_KEYS.has(key)); if (invalid.length) { throw new BadRequestException(`无效权限项: ${invalid.join(', ')}`); } } function asAdminRole(role: string): HqAdminRoleValue { if (!(HQ_ADMIN_ROLE_VALUES as readonly string[]).includes(role)) { throw new BadRequestException(`无效角色: ${role}`); } return role as HqAdminRoleValue; } @Injectable() export class AdminHqPermissionsService { constructor(private readonly prisma: PrismaService) {} catalog() { return { permissions: HQ_PERMISSION_CATALOG, roles: HQ_ADMIN_ROLE_VALUES.map((role) => ({ role, permissionKeys: HQ_ROLE_DEFAULT_PERMISSIONS[role], })), }; } async getRolePermissions(role: string) { const adminRole = asAdminRole(role); const rows = await this.prisma.hqRolePermission.findMany({ where: { adminRole }, select: { permissionKey: true }, }); const permissionKeys = rows.length > 0 ? expandHqPermissionKeys(rows.map((r) => r.permissionKey)) : [...(HQ_ROLE_DEFAULT_PERMISSIONS[adminRole] ?? [])]; return { role: adminRole, permissionKeys }; } async saveRolePermissions(role: string, permissionKeys: string[]) { const adminRole = asAdminRole(role); if (adminRole === 'SUPER_ADMIN') { throw new BadRequestException('超级管理员基础权限固定,危险操作请按用户单独授权'); } if (!EDITABLE_ROLES.has(adminRole)) { throw new BadRequestException(`无效角色: ${role}`); } assertPermissionKeys(permissionKeys); const normalized = expandHqPermissionKeys(permissionKeys); await this.prisma.$transaction([ this.prisma.hqRolePermission.deleteMany({ where: { adminRole } }), ...(normalized.length ? [ this.prisma.hqRolePermission.createMany({ data: normalized.map((permissionKey) => ({ adminRole, permissionKey })), }), ] : []), ]); return this.getRolePermissions(role); } async getAccountPermissions(accountId: bigint) { const account = await this.prisma.hqAccount.findUnique({ where: { id: accountId }, select: { id: true, name: true, phone: true, loginName: true, adminRole: true, status: true }, }); if (!account) throw new NotFoundException('HQ 账号不存在'); const userPerms = await this.prisma.hqAccountPermission.findMany({ where: { hqAccountId: accountId }, select: { permissionKey: true, effect: true }, }); const grantKeys = expandHqPermissionKeys( userPerms.filter((p) => p.effect !== 'DENY').map((p) => p.permissionKey), ); const denyKeys = expandHqPermissionKeys( userPerms.filter((p) => p.effect === 'DENY').map((p) => p.permissionKey), ); if (account.adminRole === 'SUPER_ADMIN') { const rolePermissionKeys = HQ_PERMISSION_CATALOG.map((p) => p.key) as HqPermissionKey[]; const effectivePermissionKeys = computeHqEffectivePermissionKeys( rolePermissionKeys, grantKeys, [], ); return serializeBigInt({ account, permissionKeys: grantKeys, rolePermissionKeys, grantKeys, denyKeys: [] as HqPermissionKey[], userPermissionKeys: grantKeys, effectivePermissionKeys, }); } const rolePerms = await this.getRolePermissions(account.adminRole); const effectivePermissionKeys = computeHqEffectivePermissionKeys( rolePerms.permissionKeys, grantKeys, denyKeys, ); return serializeBigInt({ account, permissionKeys: grantKeys, rolePermissionKeys: rolePerms.permissionKeys, grantKeys, denyKeys, userPermissionKeys: grantKeys, effectivePermissionKeys, }); } async saveAccountPermissions( accountId: bigint, grantKeysInput: string[], denyKeysInput: string[] = [], ) { const account = await this.prisma.hqAccount.findUnique({ where: { id: accountId } }); if (!account) throw new NotFoundException('HQ 账号不存在'); assertPermissionKeys(grantKeysInput); assertPermissionKeys(denyKeysInput); const grantKeys = expandHqPermissionKeys(grantKeysInput); const denyKeys = account.adminRole === 'SUPER_ADMIN' ? [] : expandHqPermissionKeys(denyKeysInput); const overlap = grantKeys.filter((k) => denyKeys.includes(k)); if (overlap.length) { throw new BadRequestException(`同一权限不能同时追加和撤销: ${overlap.join(', ')}`); } await this.prisma.$transaction([ this.prisma.hqAccountPermission.deleteMany({ where: { hqAccountId: accountId } }), ...(grantKeys.length || denyKeys.length ? [ this.prisma.hqAccountPermission.createMany({ data: [ ...grantKeys.map((permissionKey) => ({ hqAccountId: accountId, permissionKey, effect: 'GRANT' as const, })), ...denyKeys.map((permissionKey) => ({ hqAccountId: accountId, permissionKey, effect: 'DENY' as const, })), ], }), ] : []), ]); return this.getAccountPermissions(accountId); } }