feat(teacher-bff): admin 命名空间 + 5 个 gRPC client + health probes + merge-resolvers + nextstep 文档

This commit is contained in:
SpecialX
2026-07-14 16:00:46 +08:00
parent 7b790f1276
commit 895a060491
50 changed files with 9739 additions and 334 deletions

View File

@@ -1,4 +1,4 @@
// GraphQL Yoga endpointB1 裁决P2 起直接 GraphQL
// GraphQL Yoga endpointB1 裁决P2 起直接 GraphQLARB-001 扁平命名合并 admin resolvers
// POST /graphql — GraphQL query/mutation endpoint
// GET /graphql — GraphQL Playground开发环境/ schema introspection
import { Controller, Post, Get, Req, Res, Inject } from "@nestjs/common";
@@ -9,21 +9,30 @@ import { logger } from "../shared/observability/logger.js";
import { loadSchema } from "./schema-loader.js";
import { buildGraphQLContext } from "./context.js";
import { TeacherService } from "../teacher/teacher.service.js";
import { AdminService } from "../admin/admin.service.js";
import { buildResolvers } from "../teacher/teacher.resolver.js";
import { buildAdminResolvers } from "../admin/admin.resolver.js";
import { mergeResolvers } from "./merge-resolvers.js";
@Controller("graphql")
export class GraphQLController {
// Yoga 实例类型复杂,用内联类型避免泛型不匹配
private readonly yoga: (req: Request, res: Response) => Promise<void>;
constructor(@Inject(TeacherService) service: TeacherService) {
const resolvers = buildResolvers(service);
constructor(
@Inject(TeacherService) teacherService: TeacherService,
@Inject(AdminService) adminService: AdminService,
) {
// ARB-001扁平命名合并 teacher + admin resolvers
// 顺序admin 先 → teacher 后Object.assign 后者覆盖前者)
// teacher.updateUser 需覆盖 admin.updateUserschema 统一为 User! 返回类型id 可选)
const teacherResolvers = buildResolvers(teacherService);
const adminResolvers = buildAdminResolvers(adminService);
const resolvers = mergeResolvers(adminResolvers, teacherResolvers);
const schema = loadSchema(resolvers);
const yoga = createYoga({
schema,
graphqlEndpoint: env.GRAPHQL_PATH,
// graphql-yoga v5request 是 WHATWG Requestheaders 需用 .get() 提取
context: ({ request }) => {
const req = {
headers: {
@@ -40,7 +49,6 @@ export class GraphQLController {
warn: (msg: unknown) => logger.warn({ yoga: msg }, "GraphQL Yoga"),
error: (msg: unknown) => logger.error({ yoga: msg }, "GraphQL Yoga"),
},
// GraphQL Playground开发环境启用
graphiql: env.NODE_ENV === "development",
});
@@ -51,7 +59,7 @@ export class GraphQLController {
logger.info(
{ graphqlEndpoint: env.GRAPHQL_PATH },
"GraphQL Yoga endpoint initialized",
"GraphQL Yoga endpoint initialized (teacher + admin resolvers merged)",
);
}

View File

@@ -1,10 +1,11 @@
// GraphQL 模块B1 裁决P2 起直接 GraphQL Yoga
// GraphQL 模块B1 裁决P2 起 GraphQL YogaARB-001 扁平命名合并 admin resolvers
import { Module } from "@nestjs/common";
import { GraphQLController } from "./graphql.controller.js";
import { TeacherModule } from "../teacher/teacher.module.js";
import { AdminModule } from "../admin/admin.module.js";
@Module({
imports: [TeacherModule],
imports: [TeacherModule, AdminModule],
controllers: [GraphQLController],
})
export class GraphQLModule {}

View File

@@ -0,0 +1,19 @@
// 深合并多个 IResolvers 对象ARB-001teacher + admin resolvers 扁平命名合并)
// 避免浅展开覆盖 Query/Mutation 字典
import type { IResolvers } from "@graphql-tools/utils";
/** 深合并多个 resolvers 对象的 Query/Mutation 字典 */
export function mergeResolvers(...resolvers: IResolvers[]): IResolvers {
const merged: Record<string, Record<string, unknown>> = {};
for (const resolver of resolvers) {
for (const [typeKey, typeResolvers] of Object.entries(resolver)) {
if (!merged[typeKey]) {
merged[typeKey] = {};
}
Object.assign(merged[typeKey], typeResolvers);
}
}
return merged as IResolvers;
}