feat(teacher-bff): admin 命名空间 + 5 个 gRPC client + health probes + merge-resolvers + nextstep 文档
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// GraphQL Yoga endpoint(B1 裁决:P2 起直接 GraphQL)
|
||||
// GraphQL Yoga endpoint(B1 裁决:P2 起直接 GraphQL,ARB-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.updateUser(schema 统一为 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 v5:request 是 WHATWG Request,headers 需用 .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)",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// GraphQL 模块(B1 裁决:P2 起直接 GraphQL Yoga)
|
||||
// GraphQL 模块(B1 裁决:P2 起 GraphQL Yoga,ARB-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 {}
|
||||
|
||||
19
services/teacher-bff/src/graphql/merge-resolvers.ts
Normal file
19
services/teacher-bff/src/graphql/merge-resolvers.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// 深合并多个 IResolvers 对象(ARB-001:teacher + 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;
|
||||
}
|
||||
Reference in New Issue
Block a user