feat(rbac): 数据权限扩展至组织树与审计/登录日志
- orgs: 列表按可见部门过滤 - audit-logs/login-logs: 按操作人所属部门过滤(self 按 userId/username) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
78e537fa73
commit
537ec4fd91
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
||||||
import { BaseCrudService } from '../common/base-crud.service.js';
|
import { BaseCrudService } from '../common/base-crud.service.js';
|
||||||
import { PrismaService } from '../prisma/prisma.service.js';
|
import { PrismaService } from '../prisma/prisma.service.js';
|
||||||
|
import { getRequestContext } from '../common/context/request-context.js';
|
||||||
import { ListAuditLogQuery } from './dto.js';
|
import { ListAuditLogQuery } from './dto.js';
|
||||||
|
|
||||||
export interface AuditLogEntry {
|
export interface AuditLogEntry {
|
||||||
@ -39,11 +40,28 @@ export class AuditLogsService extends BaseCrudService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listAuditLogs(q: ListAuditLogQuery) {
|
async listAuditLogs(q: ListAuditLogQuery) {
|
||||||
|
const ctx = getRequestContext();
|
||||||
const where: Record<string, unknown> = {};
|
const where: Record<string, unknown> = {};
|
||||||
if (q.username) where.username = { contains: q.username };
|
if (q.username) where.username = { contains: q.username };
|
||||||
if (q.module) where.module = { contains: q.module };
|
if (q.module) where.module = { contains: q.module };
|
||||||
if (q.status) where.status = q.status;
|
if (q.status) where.status = q.status;
|
||||||
|
// 数据权限:仅本人按 userId;否则按可见部门内的操作人
|
||||||
|
if (ctx && ctx.dataScope !== 'all') {
|
||||||
|
if (ctx.dataScope === 'self') {
|
||||||
|
where.userId = ctx.userId;
|
||||||
|
} else {
|
||||||
|
const userIds = ctx.orgCodes.length
|
||||||
|
? (
|
||||||
|
await this.prisma.client.user.findMany({
|
||||||
|
where: { tenantCode: ctx.tenantCode, orgCode: { in: ctx.orgCodes }, deletedAt: null },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
).map((u) => u.id)
|
||||||
|
: [];
|
||||||
|
where.userId = { in: userIds };
|
||||||
|
}
|
||||||
|
}
|
||||||
return this.paginate(q.page, q.pageSize, where);
|
return this.paginate(q.page, q.pageSize, where);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
||||||
import { BaseCrudService } from '../common/base-crud.service.js';
|
import { BaseCrudService } from '../common/base-crud.service.js';
|
||||||
import { PrismaService } from '../prisma/prisma.service.js';
|
import { PrismaService } from '../prisma/prisma.service.js';
|
||||||
|
import { getRequestContext } from '../common/context/request-context.js';
|
||||||
import { ListLoginLogQuery } from './dto.js';
|
import { ListLoginLogQuery } from './dto.js';
|
||||||
|
|
||||||
export interface LoginLogEntry {
|
export interface LoginLogEntry {
|
||||||
@ -31,10 +32,27 @@ export class LoginLogsService extends BaseCrudService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listLoginLogs(q: ListLoginLogQuery) {
|
async listLoginLogs(q: ListLoginLogQuery) {
|
||||||
|
const ctx = getRequestContext();
|
||||||
const where: Record<string, unknown> = {};
|
const where: Record<string, unknown> = {};
|
||||||
if (q.username) where.username = { contains: q.username };
|
if (q.username) where.username = { contains: q.username };
|
||||||
if (q.status) where.status = q.status;
|
if (q.status) where.status = q.status;
|
||||||
|
// 数据权限:仅本人按 username;否则按可见部门内的用户
|
||||||
|
if (ctx && ctx.dataScope !== 'all') {
|
||||||
|
if (ctx.dataScope === 'self') {
|
||||||
|
where.username = ctx.username;
|
||||||
|
} else {
|
||||||
|
const usernames = ctx.orgCodes.length
|
||||||
|
? (
|
||||||
|
await this.prisma.client.user.findMany({
|
||||||
|
where: { tenantCode: ctx.tenantCode, orgCode: { in: ctx.orgCodes }, deletedAt: null },
|
||||||
|
select: { username: true },
|
||||||
|
})
|
||||||
|
).map((u) => u.username)
|
||||||
|
: [];
|
||||||
|
where.username = { in: usernames };
|
||||||
|
}
|
||||||
|
}
|
||||||
return this.paginate(q.page, q.pageSize, where);
|
return this.paginate(q.page, q.pageSize, where);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export class OrgsController {
|
|||||||
@Get()
|
@Get()
|
||||||
@RequirePermissions(Permissions.org.list)
|
@RequirePermissions(Permissions.org.list)
|
||||||
list() {
|
list() {
|
||||||
return this.service.list({}, { sort: 'asc' });
|
return this.service.listOrgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import { BaseCrudService } from '../common/base-crud.service.js';
|
import { BaseCrudService } from '../common/base-crud.service.js';
|
||||||
import { PrismaService } from '../prisma/prisma.service.js';
|
import { PrismaService } from '../prisma/prisma.service.js';
|
||||||
|
import { getRequestContext } from '../common/context/request-context.js';
|
||||||
import { CreateOrgDto, UpdateOrgDto } from './dto.js';
|
import { CreateOrgDto, UpdateOrgDto } from './dto.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -9,6 +10,16 @@ export class OrgsService extends BaseCrudService {
|
|||||||
super(prisma, 'org');
|
super(prisma, 'org');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 组织树列表:按数据权限过滤(空 orgCodes=全部可见)。 */
|
||||||
|
listOrgs() {
|
||||||
|
const ctx = getRequestContext();
|
||||||
|
const where: Record<string, unknown> = {};
|
||||||
|
if (ctx && ctx.orgCodes.length > 0) {
|
||||||
|
where.code = { in: ctx.orgCodes };
|
||||||
|
}
|
||||||
|
return this.list(where, { sort: 'asc' });
|
||||||
|
}
|
||||||
|
|
||||||
async createOrg(dto: CreateOrgDto) {
|
async createOrg(dto: CreateOrgDto) {
|
||||||
await this.assertValidParent(dto.parentCode);
|
await this.assertValidParent(dto.parentCode);
|
||||||
return this.create({ ...dto });
|
return this.create({ ...dto });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user