sysmgr-nest/src/roles/roles.service.ts
liuyun 7340658305 feat: enforce RBAC, audit/login logging, and seed script
收口 Phase 0 三处「声明未生效」:
- 三级鉴权落地:权限集存 Redis(key 前缀隔离多项目),@RequirePermissions 挂到全部受控接口,super_admin 角色绕过权限码与 data_scope
- 审计/登录日志自动落库:@AuditLog 装饰器 + 拦截器异步写入(写操作+登录登出),登录成功/失败均记 login_log(脱敏)
- seed 脚本:一键初始化租户 touyun/用户 touyun/超管 super_admin/全量资源树(48 资源全量授权)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 23:19:41 +08:00

47 lines
1.6 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { BaseCrudService } from '../common/base-crud.service.js';
import { PrismaService } from '../prisma/prisma.service.js';
import { RedisService } from '../common/redis/redis.service.js';
import { getRequestContext } from '../common/context/request-context.js';
import { AssignResourcesDto, CreateRoleDto, UpdateRoleDto } from './dto.js';
@Injectable()
export class RolesService extends BaseCrudService {
constructor(
prisma: PrismaService,
private readonly redis: RedisService,
) {
super(prisma, 'role');
}
createRole(dto: CreateRoleDto) {
return this.create({ ...dto });
}
updateRole(id: string, dto: UpdateRoleDto) {
return this.update(id, { ...dto });
}
async assignedResources(roleCode: string) {
const ctx = getRequestContext()!;
const rows = await this.prisma.client.roleResource.findMany({
where: { tenantCode: ctx.tenantCode, roleCode },
select: { resourceCode: true },
});
return rows.map((r) => r.resourceCode);
}
async assignResources(roleCode: string, dto: AssignResourcesDto) {
const ctx = getRequestContext()!;
await this.prisma.client.roleResource.deleteMany({ where: { tenantCode: ctx.tenantCode, roleCode } });
if (dto.resourceCodes.length > 0) {
await this.prisma.client.roleResource.createMany({
data: dto.resourceCodes.map((resourceCode) => ({ tenantCode: ctx.tenantCode, roleCode, resourceCode })),
});
}
// 授权变更:删除该租户全部用户权限缓存,下一次请求守卫重新聚合并回填
await this.redis.delByPrefix(`perm:${ctx.tenantCode}:`);
return null;
}
}