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

@@ -0,0 +1,11 @@
// Admin 模块P6 实现president §5.1admin-portal 复用 teacher-bff endpoint
import { Module } from "@nestjs/common";
import { AdminService } from "./admin.service.js";
import { ClientsModule } from "../clients/clients.module.js";
@Module({
imports: [ClientsModule],
providers: [AdminService],
exports: [AdminService],
})
export class AdminModule {}

View File

@@ -0,0 +1,408 @@
// Admin ResolverP6 重构ARB-001 裁决:扁平命名)
// admin-portal operations 直接消费根 Query/Mutation 字段(不再使用 AdminQuery/AdminMutation 命名空间)
// 对齐 admin-portal/src/lib/graphql-client.ts 中实际发送的 operations
import type { IResolvers } from "@graphql-tools/utils";
import { AdminService } from "./admin.service.js";
import type { GraphQLOperationContext } from "../graphql/context.js";
/**
* 构建 Admin Resolvers扁平命名挂载在根 Query/Mutation
* 与 teacher.resolver.ts 的 buildResolvers() 合并使用:
* const resolvers = mergeResolvers(buildResolvers(...), buildAdminResolvers(...))
*/
export function buildAdminResolvers(service: AdminService): IResolvers {
return {
Query: {
// ===== 用户与权限管理 =====
currentUser: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getCurrentUser(ctx.callCtx);
},
adminUsers: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminUsers(ctx.callCtx, args.filter as never);
},
adminUser: async (
_parent: unknown,
args: { id: string },
ctx: GraphQLOperationContext,
) => {
return service.getAdminUser(ctx.callCtx, args.id);
},
adminRoles: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.listAdminRoles(ctx.callCtx);
},
adminPermissions: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.listAdminPermissions(ctx.callCtx);
},
adminViewports: async (
_parent: unknown,
args: { scope?: string },
ctx: GraphQLOperationContext,
) => {
return service.listAdminViewports(ctx.callCtx, args.scope);
},
// ===== 组织架构 =====
adminOrganization: async (
_parent: unknown,
args: { parentId?: string },
ctx: GraphQLOperationContext,
) => {
return service.listAdminOrganizations(ctx.callCtx, args.parentId);
},
adminClasses: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminClasses(ctx.callCtx, args.filter as never);
},
adminTeachers: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminTeachers(ctx.callCtx, args.filter as never);
},
adminStudents: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminStudents(ctx.callCtx, args.filter as never);
},
// ===== 审计与日志 =====
auditLogs: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAuditLogs(ctx.callCtx, args.filter as never);
},
auditOverviewStats: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getAuditOverviewStats(ctx.callCtx);
},
auditTrend: async (
_parent: unknown,
args: { days?: number },
ctx: GraphQLOperationContext,
) => {
return service.getAuditTrend(ctx.callCtx, args.days);
},
dataChangeLogs: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listDataChangeLogs(ctx.callCtx, args.filter as never);
},
loginLogs: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listLoginLogs(ctx.callCtx, args.filter as never);
},
// ===== 仪表盘与设置 =====
adminDashboard: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getAdminDashboard(ctx.callCtx);
},
systemSettings: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getSystemSettings(ctx.callCtx);
},
// ===== 学校 / 年级 / 部门 / 学年 =====
adminSchools: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.listAdminSchools(ctx.callCtx);
},
adminGrades: async (
_parent: unknown,
args: { schoolId?: string },
ctx: GraphQLOperationContext,
) => {
return service.listAdminGrades(ctx.callCtx, args.schoolId);
},
adminGradeInsights: async (
_parent: unknown,
args: { gradeId: string },
ctx: GraphQLOperationContext,
) => {
return service.getAdminGradeInsights(ctx.callCtx, args.gradeId);
},
adminDepartments: async (
_parent: unknown,
args: { schoolId?: string },
ctx: GraphQLOperationContext,
) => {
return service.listAdminDepartments(ctx.callCtx, args.schoolId);
},
adminAcademicYears: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.listAdminAcademicYears(ctx.callCtx);
},
// ===== 公告 / 邀请码 / 文件 / AI =====
adminAnnouncements: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminAnnouncements(
ctx.callCtx,
args.filter as never,
);
},
adminInvitationCodes: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminInvitationCodes(
ctx.callCtx,
args.filter as never,
);
},
adminFiles: async (
_parent: unknown,
args: { filter?: unknown },
ctx: GraphQLOperationContext,
) => {
return service.listAdminFiles(ctx.callCtx, args.filter as never);
},
adminFileStats: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getAdminFileStats(ctx.callCtx);
},
aiProviders: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.listAiProviders(ctx.callCtx);
},
aiUsage: async (
_parent: unknown,
_args: unknown,
ctx: GraphQLOperationContext,
) => {
return service.getAiUsage(ctx.callCtx);
},
},
Mutation: {
// ===== 用户与角色管理 =====
createUser: async (
_parent: unknown,
args: { input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.createUser(ctx.callCtx, args.input as never);
},
updateUser: async (
_parent: unknown,
args: { id: string; input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.updateUser(ctx.callCtx, args.id, args.input as never);
},
toggleUserStatus: async (
_parent: unknown,
args: { id: string; status: string },
ctx: GraphQLOperationContext,
) => {
return service.toggleUserStatus(ctx.callCtx, args.id, args.status);
},
createRole: async (
_parent: unknown,
args: { input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.createRole(ctx.callCtx, args.input as never);
},
updateRolePermissions: async (
_parent: unknown,
args: { roleId: string; permissionCodes: string[] },
ctx: GraphQLOperationContext,
) => {
return service.updateRolePermissions(
ctx.callCtx,
args.roleId,
args.permissionCodes,
);
},
updateViewport: async (
_parent: unknown,
args: { id: string; input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.updateViewport(
ctx.callCtx,
args.id,
args.input as never,
);
},
// ===== 系统设置 =====
updateSystemSettings: async (
_parent: unknown,
args: { input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.updateSystemSettings(ctx.callCtx, args.input as never);
},
// ===== 公告管理 =====
createAnnouncement: async (
_parent: unknown,
args: { input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.createAnnouncement(ctx.callCtx, args.input as never);
},
publishAnnouncement: async (
_parent: unknown,
args: { id: string },
ctx: GraphQLOperationContext,
) => {
return service.publishAnnouncement(ctx.callCtx, args.id);
},
archiveAnnouncement: async (
_parent: unknown,
args: { id: string },
ctx: GraphQLOperationContext,
) => {
return service.archiveAnnouncement(ctx.callCtx, args.id);
},
toggleAnnouncementPin: async (
_parent: unknown,
args: { id: string },
ctx: GraphQLOperationContext,
) => {
return service.toggleAnnouncementPin(ctx.callCtx, args.id);
},
// ===== 邀请码 =====
generateInvitationCodes: async (
_parent: unknown,
args: { input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.generateInvitationCodes(
ctx.callCtx,
args.input as never,
);
},
deleteInvitationCodes: async (
_parent: unknown,
args: { ids: string[] },
ctx: GraphQLOperationContext,
) => {
return service.deleteInvitationCodes(ctx.callCtx, args.ids);
},
// ===== 文件管理 =====
deleteFiles: async (
_parent: unknown,
args: { ids: string[] },
ctx: GraphQLOperationContext,
) => {
return service.deleteFiles(ctx.callCtx, args.ids);
},
// ===== AI 提供商 =====
updateAiProvider: async (
_parent: unknown,
args: { id: string; input: unknown },
ctx: GraphQLOperationContext,
) => {
return service.updateAiProvider(
ctx.callCtx,
args.id,
args.input as never,
);
},
},
};
}

File diff suppressed because it is too large Load Diff