Files
Edu/apps/admin-portal/docs/02-architecture-design.md

32 KiB
Raw Blame History

模块架构设计文档 — admin-portal

AIai16TS/React · 管理场景域前端 remote 阶段:阶段 2 交付物(仲裁后修订版 v2 日期2026-07-10 关联:


1. 模块内部分层图admin-portal Remote + Shell 引用 + Gateway + push-gateway

graph TB
    subgraph Browser["浏览器(系统/校管理员)"]
        URL[URL 路由 /admin/*]
    end

    subgraph Shell["teacher-portalShell 宿主,端口 4000"]
        AppShell[AppShell<br/>左栏导航 + 主内容区<br/>按 scope=admin 过滤视口]
        RootLayout[RootLayout<br/>字体/令牌/i18n Provider]
        Router[Next.js App Router<br/>动态加载 admin Remote]
        SharedDeps["共享依赖暴露singleton<br/>react/react-dom/urql/graphql<br/>@edu/ui-tokens/@edu/ui-components/@edu/hooks/@edu/contracts"]
        GraphQLProviderShell[GraphQLProvider<br/>Shell 暴露 urql client 单例]
    end

    subgraph RemoteAdmin["admin-portalRemote 子应用,端口 4003"]
        AdminApp[AdminApp<br/>MF Remote 入口 exposes ./AdminApp]
        AdminPages[管理场景页面<br/>dashboard/users/roles/permissions/viewports/organization<br/>classes/teachers/students/audit-logs/system]
        AdminComponents["admin 特有组件<br/>UserManagementTable/RolePermissionMatrix<br/>ViewportConfigEditor/OrganizationTree/NotificationPanel"]
        AdminHooks["admin 业务 Hooks<br/>useUsers/useRoles/usePermissions/useViewports<br/>useOrganization/useClasses/useTeachers/useStudents<br/>useAuditLogs/useDashboard/useSystemSettings/useWebSocket"]
    end

    subgraph Gateway["api-gateway"]
        GW[Gin 路由/鉴权/限流]
    end

    subgraph PushGateway["push-gateway"]
        WS[WebSocket :8081/ws]
    end

    subgraph Backend["后端服务"]
        IAM[iam<br/>用户/角色/权限/视口 CRUD + AuditEvent 发布]
        BFF[teacher-bff<br/>GraphQL admin 命名空间 聚合]
    end

    Browser --> URL
    URL --> RootLayout
    RootLayout --> AppShell
    AppShell --> Router
    Router -->|动态加载 /admin/*| AdminApp
    AdminApp --> AdminPages

    AdminPages -->|消费 singleton| SharedDeps
    SharedDeps --> GraphQLProviderShell
    AdminPages --> AdminComponents
    AdminPages --> AdminHooks

    AdminHooks -->|useGraphQuery/useGraphMutation| GraphQLProviderShell
    GraphQLProviderShell -->|POST /api/admin/graphql| GW
    GW --> BFF
    BFF --> IAM

    AdminHooks -->|useWebSocket| WS
    IAM -.->|Kafka edu.iam.audit.created| BFF

    AppShell -->|fetch /api/auth/login| GW

1.1 Remote 与 Shell 的职责边界

职责 归属 说明
RootLayout字体/令牌/Provider teacher-portal Shell admin-portal MF 模式复用standalone 模式自建
AppShell左栏 + 主内容区) admin-portal 内部 admin-shell.tsx11 项导航 + 跳过链接 A11y
路由表 /admin/* admin-portal src/app/admin/*/page.tsx
登录页 admin-portal standalone 模式 src/app/login/page.tsxmock 登录MF 模式复用 Shell /login
rewrites /api/admin/graphql admin-portal next.config.js rewrites 代理到 api-gateway
GraphQLProvider Shell 暴露 / 自建 MF 模式复用 Shell 单例ARB-004standalone 模式 graphql-provider.tsx 自建 urql client
管理场景页面 admin-portal Remote /admin/* 下的所有 page.tsx
管理特有组件 admin-portal Remote UserManagementTable 等 6 个
管理业务 Hooks admin-portal Remote 13 个业务 Hook + useWebSocket
共享组件库 Shell 暴露 @edu/ui-components
共享 Hooks Shell 暴露 @edu/hooks

1.2 MF 配置admin-portal/next.config.jsRemote 角色)

const NextFederationPlugin = require("@module-federation/nextjs-mf");

const nextConfig = {
  reactStrictMode: true,
  transpilePackages: ["@edu/ui-tokens", "@edu/ui-components", "@edu/hooks"],
  async rewrites() {
    const gatewayUrl = process.env.API_GATEWAY_URL || "http://localhost:8080";
    return [
      {
        source: "/api/admin/graphql",
        destination: `${gatewayUrl}/api/admin/graphql`,
      },
      { source: "/api/:path*", destination: `${gatewayUrl}/api/:path*` },
    ];
  },
  webpack(config, { isServer }) {
    if (process.env.NEXT_PUBLIC_MF_ENABLED === "true") {
      config.plugins.push(
        new NextFederationPlugin({
          name: "admin_app",
          filename: "static/chunks/remoteEntry.js",
          exposes: { "./AdminApp": "./src/app/admin-app.tsx" },
          remotes: {
            teacher: `teacher_app@http://localhost:4000/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
          },
          shared: {
            react: { singleton: true, requiredVersion: "^18.3.0" },
            "react-dom": { singleton: true, requiredVersion: "^18.3.0" },
            urql: { singleton: true },
            graphql: { singleton: true },
            "@edu/ui-tokens": { singleton: true },
            "@edu/ui-components": { singleton: true },
            "@edu/hooks": { singleton: true },
          },
          extraOptions: { exposePages: false },
        }),
      );
    }
    return config;
  },
};
module.exports = nextConfig;

说明

  • NEXT_PUBLIC_MF_ENABLED 环境变量控制 MF 启用standalone 模式默认 falseMF 模式设 true
  • admin-portal 暴露 ./AdminApp 模块(非旧文档的 ./pages),供 Shell 动态 import
  • shared 全部 singleton: true,确保 urql/graphql/react 单例(避免多实例报错)
  • admin-portal 自身实现 rewrites(代理 /api/admin/graphql 到 api-gatewaystandalone 模式可独立运行

2. 领域模型(前端视角)

前端不持有业务聚合根,仅持有"视图模型"ViewModel和"会话状态"。定义于 src/types/view-models.ts

2.1 视图模型清单

// 数据范围
type DataScope = "ALL" | "SCHOOL" | "GRADE" | "CLASS" | "DISTRICT";

// 用户管理
interface UserViewModel {
  id: string;
  email: string;
  name: string;
  roles: { id: string; name: string; code: string }[];
  status: "active" | "disabled" | "locked";
  dataScope: DataScope;
  organizationId: string | null;
  schoolName?: string;
  lastLoginAt: number | null;
  createdAt: number;
  updatedAt: number;
}

// 角色管理
interface RoleViewModel {
  id: string;
  name: string;
  code: string;
  description: string;
  permissions: PermissionViewModel[];
  userCount: number;
  dataScope: DataScope;
  isSystem: boolean;
  createdAt: number;
  updatedAt: number;
}

// 权限管理
interface PermissionViewModel {
  id: string;
  code: string;
  name: string;
  description: string;
  resource: string;
  action: string;
  isSystem: boolean;
}

// 视口配置
interface ViewportConfigViewModel {
  id: string;
  key: string;
  label: string;
  route: string;
  icon: string | null;
  sortOrder: number;
  requiredPermission: string | null;
  scope: "teacher" | "student" | "parent" | "admin";
  isVisible: boolean;
}

// 组织树
interface OrganizationNode {
  id: string;
  name: string;
  type: "school" | "grade" | "class";
  parentId: string | null;
  childrenCount: number;
  path: string;
}

// 学校设置
interface SystemSettingsViewModel {
  schoolName: string;
  schoolYear: string;
  semester: string;
  timezone: string;
  locale: string;
  // ... 更多设置项
}

// 班级/教师/学生admin 全局视角)
interface AdminClassViewModel {
  id: string;
  name: string;
  grade: string;
  headTeacher: string;
  studentCount: number;
  status: string;
}
interface AdminTeacherViewModel {
  id: string;
  email: string;
  name: string;
  subjects: string[];
  classes: string[];
  status: string;
}
interface AdminStudentViewModel {
  id: string;
  email: string;
  name: string;
  className: string;
  grade: string;
  status: string;
}

// 审计日志
interface AuditLogViewModel {
  id: string;
  userId: string;
  userName: string;
  action: string;
  resourceType: string;
  resourceId: string;
  beforeState: string | null;
  afterState: string | null;
  ip: string;
  userAgent: string;
  occurredAt: number;
}

// 管理仪表盘
interface AdminDashboardViewModel {
  totalUsers: number;
  totalTeachers: number;
  totalStudents: number;
  totalClasses: number;
  activeSessions: number;
  serviceHealth: {
    serviceName: string;
    status: "healthy" | "degraded" | "down";
    latencyMs: number;
  }[];
  recentActivity: {
    timestamp: number;
    action: string;
    user: string;
    resource: string;
  }[];
}

// WebSocket 通知
interface WsNotification {
  id: string;
  type: "audit_alert" | "abnormal_login" | "system_error" | "info";
  severity: "info" | "warning" | "error";
  title: string;
  message: string;
  timestamp: number;
}

2.2 当前用户与会话

interface CurrentUser {
  id: string;
  email: string;
  name: string;
  roles: string[];
  permissions: string[];
  dataScope: DataScope;
  schoolId: string | null;
  schoolName: string | null;
}

会话状态由 AuthProvider 管理token 存 localStorageedu_access_token),用户信息存 localStorageedu_user)。

3. 数据模型(前端缓存层)

admin-portal 无数据库,仅有 urql GraphQL client 缓存层。

数据类型 存储 缓存策略
Sessiontoken + user localStorage + React Context access token 持久化;用户信息持久化;登出清除
GraphQL 查询缓存 urql document cache 默认缓存mutation 后手动 invalidate
视口配置(拖拽态) React useState 本地编辑,保存时批量 mutation
表单临时态 react-hook-form 卸载即销毁
WebSocket 通知 React useState限 20 条) 新通知头部插入,超 20 条裁剪

缓存策略说明admin 数据低频变urql 默认 document cache 已满足;监控指标经 WebSocket 实时推送(不再轮询);视口配置支持本地编辑 + 批量保存。

4. API 设计GraphQL 为主)

4.1 GraphQL ClienturqlARB-004

standalone 模式src/lib/graphql-client.ts

import { createClient, fetchExchange, type Client } from "@urql/core";

const GRAPHQL_ENDPOINT = "/api/admin/graphql";

export function createGraphQLClient(): Client {
  return createClient({
    url: GRAPHQL_ENDPOINT,
    exchanges: [fetchExchange],
    fetchOptions: () => {
      const token =
        typeof window !== "undefined"
          ? localStorage.getItem("edu_access_token")
          : null;
      return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
    },
  });
}

MF 模式:复用 Shell 暴露的 GraphQLProvider + useGraphQLClientsingletonARB-004

urql 类型断言技巧urql 泛型与纯字符串 query 兼容性有限,用 as unknown as 从 unknown 转换:

// use-graphql.ts
const [executeMutation] = useMutation(mutation) as unknown as [
  (variables: V) => { toPromise: () => Promise<OperationResult<T, V>> },
  unknown,
];

4.2 通用 GraphQL Hookssrc/hooks/use-graphql.ts

export function useGraphQuery<T, V = Record<string, never>>(
  query: string,
  variables?: V,
): QueryResult<T> { ... }

export function useGraphMutation<T, V>(
  mutation: string,
): [MutationFn<T, V>, MutationResult] { ... }

4.3 业务 Hooks 清单

Hook 职责
useUsers(filter) 用户列表查询(含筛选/分页)
useUser(userId) 用户详情
useCreateUser() 创建用户 mutation
useUpdateUser() 更新用户 mutation
useToggleUserStatus() 启用/禁用用户 mutation
useUserFilter() 用户筛选状态(搜索/角色/状态/组织)
useRoles() 角色列表查询
useCreateRole() 创建角色 mutation
useUpdateRolePermissions() 更新角色权限 mutationRolePermissionMatrix 用)
usePermissions() 全量权限列表
useViewports() 视口配置列表
useUpdateViewport() 更新视口配置 mutation
useOrganization(parentId) 组织树查询(按 parentId 递归)
useClasses(filter) 班级列表查询
useClassFilter() 班级筛选状态
useTeachers(filter) 教师列表查询
useTeacherFilter() 教师筛选状态
useStudents(filter) 学生列表查询
useStudentFilter() 学生筛选状态
useAuditLogs(filter) 审计日志查询
useAuditLogFilter() 审计日志筛选状态
exportAuditLogsCsv(logs) 审计日志 CSV 导出BOM + UTF-8
useDashboard() 管理仪表盘聚合查询
useSystemSettings() 学校设置查询
useUpdateSystemSettings() 学校设置更新 mutation
useWebSocket(maxItems) WebSocket 实时通知mock 模式 30s 定时推送)

5. 事件设计

5.1 WebSocket 实时通知ARB-006

admin-portal 接入 push-gateway GET /ws,消费 3 类通知:

// src/hooks/use-websocket.ts
export function useWebSocket(maxItems = 20) {
  // 真实模式:连接 push-gateway NEXT_PUBLIC_WS_URL
  // mock 模式30s 定时器模拟推送
  // 返回:{ notifications, connected, dismiss, clear }
}
通知类型 severity 触发场景
audit_alert warning 敏感操作(权限变更/批量删除)
abnormal_login error 异地登录/异常时段登录
system_error error 服务降级/宕机

5.2 不消费 Kafka

admin-portal 不直接订阅 Kafka。审计日志经 teacher-bff 聚合后通过 GraphQL auditLogs Query 消费ARB-005

链路iam → Kafka edu.iam.audit.createdteacher-bff 消费 → GraphQL auditLogs Query → admin-portal

不再用轮询:旧文档的"60s 轮询监控 + 5min 轮询统计"已被 ARB-006 的 WebSocket 推送替换。

6. 横切关注点对齐清单

6.1 权限前端等价ARB-003

权限点常量定义于 src/lib/permissions.ts

  • ADMIN_PERMISSIONSadmin 自身资源(ADMIN_DASHBOARD_VIEW / ADMIN_SYSTEM_MANAGE / ADMIN_AUDIT_READ / ADMIN_CLASS_READ / ADMIN_TEACHER_READ / ADMIN_STUDENT_READ / ADMIN_ORG_MANAGE 等)
  • IAM_PERMISSIONS:跨服务资源(IAM_USER_READ / IAM_ROLE_READ / IAM_PERMISSION_READ / IAM_VIEWPORT_READ 等)
  • ROUTE_PERMISSIONS11 路由 → 权限点映射

权限校验:usePermission().hasPermission("XXX") Hook + AuthGuard 组件(路由级)。

6.2 错误码清单(前端 i18n 路由ARB-002

前缀 来源服务 i18n key 模式
IAM_ iam iam.error.{{code}}
BFF_TEACHER_ teacher-bff bff.error.{{code}}
GW_ api-gateway gateway.error.{{code}}
NETWORK_ 前端网络层 network.error.{{code}}
ADMIN_ admin 域 error.admin.*

6.3 Web Vitalssrc/lib/web-vitals.ts

指标 类型 上报方式
LCP 最大内容绘制 navigator.sendBeacon
CLS 累积布局偏移 同上
FCP 首次内容绘制 同上
INP 交互到下一绘制 同上
TTFB 首字节时间 同上

仅 production 启用(WebVitalsInitializer 组件控制)。

6.4 健康检查

端点 用途 实现
GET /api/health Liveness 返回 200 { status: "ok" }
GET /api/ready Readiness 检查 gateway 可达性2s 超时mock 模式直接 ready

6.5 A11yWCAG 2.2 AA

  • eslint-plugin-jsx-a11yerror 级)
  • skip-linkadmin-shell.tsx 跳过到主内容区)
  • focus-visible 样式(globals.css
  • 组织树:<button role="treeitem" aria-expanded aria-selected onKeyDown>
  • 通知面板:<li> 内嵌 <button aria-label>
  • Toastaria-live="polite" + aria-atomic="true"
  • 注:@next/eslint-plugin-next 14.x 与 ESLint 9 不兼容(context.getAncestors 已移除),已移除

6.6 横切关注点对齐清单(汇总)

对齐项 实现
权限校验 usePermission() + AuthGuardADMIN_*/IAM_* 前缀
错误码前缀 5 个前缀(IAM_/BFF_TEACHER_/GW_/NETWORK_/ADMIN_
logger console + 结构化(生产上报)
metrics Web Vitals v4 → navigator.sendBeacon
/health + /ready Next.js Route Handler
优雅关闭 N/ANext.js 无长连接)
测试覆盖率 ≥ 80% Vitest + @testing-library/reactvitest.config.ts 已配置)
Dockerfile 多阶段构建 node:22-alpine builder + runnerstandalone 输出EXPOSE 4003
Zod 输入验证 react-hook-form + zod
GlobalErrorFilter React ErrorBoundary + Toast 错误处理
设计令牌三层 globals.cssprimitive/semantic+ tailwind-theme
A11y eslint-plugin-jsx-a11y + skip-link + focus-visible
Module Federation next.config.js RemoteNEXT_PUBLIC_MF_ENABLED 控制

7. 共享组件库(复用 Shell + admin 特有)

7.1 复用 Shell 暴露的组件(@edu/ui-components

AppShell 风格、ErrorBoundary、RequirePermission、Loading、Empty、Button/Input/Select 等。

7.2 admin-portal 特有组件(新建)

组件 用途 实现要点
AdminShell 左栏 11 项导航 + 主内容区 + 跳过链接 内部组件11 项导航按 ROUTE_PERMISSIONS 过滤;纸面风格
UserManagementTable 用户管理表格(列表/筛选/分页) 列:邮箱/姓名/角色/状态/数据范围/最后登录;筛选:搜索/角色/状态
UserFormModal 用户表单弹窗(创建/编辑) react-hook-form + zod字段邮箱/姓名/角色/数据范围/组织
RolePermissionMatrix 角色-权限矩阵编辑器checkbox 网格) 行=角色,列=权限(按 resource 分组);全选/半选/单选;系统预置角色只读
OrganizationTree 组织树(递归 + A11y treeitem 按 parentId 懒加载;<button role="treeitem" aria-expanded aria-selected onKeyDown>
NotificationPanel 通知面板WebSocket 实时通知) 固定右上角;显示最近 20 条;点击触发 Toast + dismiss<li> 内嵌 <button aria-label>

7.3 通用 UI 组件(src/components/ui.tsx

PaperCard / PageHeader / Button4 variant/ Input / Select / Badge / Table / TableRow / TableCell / EmptyState / LoadingState / ErrorState / Pagination

7.4 不使用的组件(明确排除)

组件 归属 排除原因
RichTextEditor teacher-portal 管理端无富文本场景
ExamTaking student-portal 管理端无作答场景
ChildSwitcher parent-portal 管理端无多子女切换
PlatformMonitorGrafana iframe 已移除,改为管理仪表盘 + 服务健康表格

8. Mock 策略MSW + mock-socket

8.1 MSW handlerssrc/mocks/handlers.ts

开发期拦截全部 GraphQL/HTTP 请求:

  • /api/auth/login → mock 登录(admin@edu.test / admin123
  • graphql.operation → 按 operationName 分发 18 个 GraphQL operation mock
    • CurrentUser / AdminUsers / AdminUser / CreateUser / UpdateUser / ToggleUserStatus
    • AdminRoles / CreateRole / UpdateRolePermissions
    • AdminPermissions / AdminViewports / UpdateViewport
    • AdminOrganization / AdminClasses / AdminTeachers / AdminStudents
    • AuditLogs / AdminDashboard / SystemSettings / UpdateSystemSettings
  • 内存可变副本:支持增删改 + 自动审计记录(recordAudit
  • 分页:paginate(items, page, pageSize) 辅助函数

8.2 mock fixturessrc/mocks/fixtures.ts

  • mockCurrentUseradmin 角色permissions=["*"]
  • mockPermissions24 项权限点
  • mockRoles4 个角色admin/teacher/student/parent
  • mockUsers6 个用户
  • mockViewports7 个视口配置
  • mockOrganization12 节点school/grade/class
  • mockClasses / mockTeachers / mockStudents6/6/5 条
  • mockAuditLogs5 条审计日志
  • mockDashboard:仪表盘聚合数据
  • mockSystemSettings:学校设置

8.3 WebSocket mocksrc/hooks/use-websocket.ts

MSW 不拦截 WebSocket用定时器模拟推送

  • mock 模式30s 定时器推送 1 条通知(轮换 3 类audit_alert/abnormal_login/system_error
  • 真实模式:连接 NEXT_PUBLIC_WS_URLpush-gateway GET /ws

8.4 环境切换

NEXT_PUBLIC_API_MOCKING=enabled 启用 MSWdisabled 使用真实后端。

9. 设计令牌三层(src/app/globals.css

9.1 Layer 1 Primitive原始色板

:root {
  --paper: 0 0% 100%; /* 白纸 */
  --ink: 30 10% 15%; /* 墨色 */
  --ink-muted: 30 5% 45%;
  --rule: 30 10% 90%; /* 分隔线 */
  --accent: 210 60% 40%; /* 强调色 */
  --accent-light: 210 60% 95%;
  --danger: 0 70% 50%;
  --success: 140 50% 40%;
  --warning: 40 80% 50%;
}

9.2 Layer 2 Semantic

:root {
  --bg-paper: hsl(var(--paper));
  --color-ink: hsl(var(--ink));
  --color-ink-muted: hsl(var(--ink-muted));
  --color-rule: hsl(var(--rule));
  --color-accent: hsl(var(--accent));
  --color-accent-light: hsl(var(--accent-light));
  --color-danger: hsl(var(--danger));
  --color-success: hsl(var(--success));
  --color-warning: hsl(var(--warning));
}

9.3 Layer 3 Tailwind Themetailwind.config.js

paper / ink / accent / rule / danger / success / warning 色板映射为 bg-* / text-* 类。

9.4 强制规则project_rules §3.10

  • 禁止 #hex 字面量
  • 禁止 'Inter'/'Fraunces'/'JetBrains Mono' 字面量
  • 禁止 font-size: Npx
  • 禁止 Tailwind 任意值 w-[Npx]

10. 与其他模块的交互点(契约清单)

方向 对方服务 协议 接口/事件 用途 阶段
调用 api-gateway HTTP/GraphQL POST /api/admin/graphql(经 rewrites 全部业务 GraphQL 查询 P6+
调用 api-gateway HTTP POST /api/auth/login 登录standalone 模式) P6+
调用 push-gateway WebSocket GET /ws 实时通知ARB-006 P6+
消费 teacher-bff GraphQL经 Gateway admin 命名空间 18 operations 全部业务数据 P6+
消费 iam经 teacher-bff GraphQL间接 用户/角色/权限/视口 CRUD + AuditEvent 管理数据 P6+
依赖 coord 维护 packages/contracts Permissions 常量 + 类型 P6+
依赖 coord 维护 packages/shared-proto TS 类型(仅 contracts 部分) P6+
依赖 teacher-portal Shell Shell 暴露的 GraphQLProvider + 共享依赖 宿主环境MF 模式) P6+

proto 不直接消费:前端不调用 gRPCteacher-bff 把 gRPC 聚合为 GraphQL 暴露给前端。

11. 风险与假设

11.1 假设

  1. 假设 teacher-bff admin 命名空间 schema 就绪ARB-001contract §2.4 18 operations若 ai03 未补齐admin-portal 用 MSW mock 开发,上游就绪后切换。
  2. 假设 teacher-portal Shell 已就绪MF 模式依赖 Shell 暴露 GraphQLProvider + 共享依赖。standalone 模式可独立运行。
  3. 假设 push-gateway WebSocket 就绪ARB-006若未就绪用 mock-socket + 30s 定时器模拟。
  4. *假设 packages/contracts ADMIN* 权限点常量就绪_*coord 维护admin-portal src/lib/permissions.ts 引用,若 coord 未建立则本地定义。

11.2 技术风险

风险 影响 缓解
Shell 未就绪阻塞 admin MF 模式无法挂载 standalone 模式自建 Provider 链,独立可运行
teacher-bff admin schema 缺失 GraphQL 查询失败 MSW mock 全覆盖 18 operations上游就绪后切换
urql 类型兼容性 TypeScript 编译错误 as unknown as 从 unknown 转换(见 §4.1
ESLint 9 兼容性 @next/eslint-plugin-next 14.x 不兼容 移除该插件,用 jsx-a11y + tseslint 替代
WebSocket mock 不真实 开发期通知体验与生产不一致 30s 定时器 + 3 类通知轮换,接近真实推送频率
权限缓存陈旧 角色变更后前端仍用旧权限 登录时拉取最新权限admin-portal 本身是管理端,可主动刷新

12. 端口矩阵

dev 端口 生产端口 备注
admin-portal 4003 4003 Remote挂载到 Shell
teacher-portal Shell 4000 4000 Shell 宿主
api-gateway 8080 8080 GraphQL 代理
push-gateway 8081 8081 WebSocket
teacher-bff 3003 3003 GraphQL admin 命名空间

13. 实施路线(已完成)

P2-P6 全阶段交付(已完成)

  1. P2 骨架 Phase 1:配置文件 + 设计令牌 + 类型定义
  2. P2 骨架 Phase 2GraphQL client + auth + permissions + providers + AdminShell + MF Remote 入口 + standalone 壳
  3. P2 骨架 Phase 3MSW mockhandlers + fixtures + browser+ mock-socket WebSocket mock
  4. P3 业务用户管理useUsers/useUser/CRUD hooks + UserManagementTable + /admin/users 页面)
  5. P3 业务:角色权限矩阵 + 权限点管理 + 视口配置
  6. P4 业务:组织管理 + 学校设置 + 班级/教师/学生全局管理
  7. P5 业务:审计日志 + WebSocket 实时通知 + 管理仪表盘
  8. P6 硬化A11y + Web Vitals + Dockerfile + /api/health + /api/ready + 质量校验
  9. 文档回写01/02 文档对齐仲裁结果 + known-issues §2.16 + arch:scan

验收标准

  • 所有 11 个路由页面可访问,权限校验生效
  • MF Remote 可被 Shell 加载(暴露 ./AdminApp
  • standalone 模式独立可运行(自建 Provider 链 + mock 登录)
  • GraphQL 18 operations 全部实现MSW mock + 真实切换)
  • WebSocket 实时通知mock 模式 30s 定时推送)
  • Web Vitals 采集LCP/CLS/FCP/INP/TTFB → sendBeacon
  • A11y WCAG 2.2 AAeslint-plugin-jsx-a11y error 级 + skip-link + focus-visible
  • /api/health + /api/ready 返回 200
  • Dockerfile 多阶段构建node:22-alpinestandalone 输出EXPOSE 4003
  • pnpm run typecheck + pnpm run lint 通过0 errors

AI Agent: ai16 (admin-portal remote) Branch: admin-portal-arbitration-done-TaFkyb Coordinator: coord-ai