feat(iam): graphql subgraph with dataloader and router auth guard

- GraphQLModule: Apollo Federation 2 subgraph at /graphql

- UserResolver/RoleResolver: @key with @ResolveReference using DataLoader (ADR-035)

- DataScopeResolver: ScopeToken for visible class/student IDs (ADR-041)

- RouterAuthGuard: validate Router-Authorization header (ADR-036)

- DataLoaderService: REQUEST-scoped userLoader + roleLoader

- IamRepository.batchFindRoles: batch query for DataLoader

- IamService.getVisibleClassIds/getVisibleStudentIds: dataScope calculation

- app.module.ts: register PermissionGuard + RouterAuthGuard as APP_GUARD
This commit is contained in:
SpecialX
2026-07-14 23:48:16 +08:00
parent 5fcb831a18
commit 6bed673d9f
12 changed files with 694 additions and 6 deletions

View File

@@ -11,6 +11,8 @@ import { HealthModule } from "./shared/health/health.module.js";
import { PermissionGuard } from "./middleware/permission.guard.js";
import { AuthMiddleware } from "./middleware/auth.middleware.js";
import { LifecycleService } from "./shared/lifecycle/lifecycle.service.js";
import { GraphqlModule } from "./graphql/graphql.module.js";
import { RouterAuthGuard } from "./graphql/router-auth.guard.js";
import { OutboxModule } from "@edu/shared-ts/outbox";
import { getDbInstance } from "./config/database.js";
import {
@@ -20,19 +22,22 @@ import {
} from "./config/kafka.js";
/**
* IAM 根模块。
* IAM 根模块v2.1
*
* 装配:
* - IamModule业务
* - HealthModule健康检查
* - OutboxModule事务性事件发布I5 裁决
* - GraphqlModuleApollo Federation 子图v2.1 新增
* - OutboxModule事务性事件发布I5 裁决v2.1 后投递由 Debezium 完成)
* - AuthMiddleware从 Gateway 注入的 x-user-* 头部解析用户身份)
* - PermissionGuardAPP_GUARDDB 驱动 + Redis 缓存I3 裁决)
* - RouterAuthGuardAPP_GUARD仅 /graphql 端点生效ADR-036
*/
@Module({
imports: [
IamModule,
HealthModule,
GraphqlModule,
OutboxModule.forRoot({
config: {
tableName: "iam_outbox",
@@ -48,6 +53,7 @@ import {
],
providers: [
{ provide: APP_GUARD, useClass: PermissionGuard },
{ provide: APP_GUARD, useClass: RouterAuthGuard },
LifecycleService,
],
})
@@ -56,6 +62,8 @@ export class AppModule implements NestModule, OnModuleInit {
async onModuleInit(): Promise<void> {
// 连接 Kafka producerOutbox 投递前置依赖)
// v2.1 注OutboxPublisher 轮询线程将被废弃M8由 Debezium 接管投递
// 此处保留 Kafka producer 连接用于其他场景(如直接发事件)
try {
await connectKafkaProducer();
this.logger.log("Kafka producer connected");