Files
Edu/apps/student-portal/docs/02-architecture-design.md
SpecialX e691cd267d docs(teacher-portal): ai07 阶段1+2 拆分到4端portal的docs目录
删除合并版README,按portal拆分8份文档(每端01-understanding+02-architecture-design)

teacher-portal(shell/P2)+student-portal(remote/P3)+parent-portal(remote/P4)+admin-portal(remote/P6)

AI Agent: ai07 (4 portals)

Branch: docs/portals-stage1-stage2-design-ai07
2026-07-09 18:23:27 +08:00

34 KiB
Raw Blame History

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

AIai07TS/React · 学习场景域前端 remote 阶段:阶段 2 交付物 日期2026-07-09 关联:阶段 1 理解确认书004 架构影响地图 §5.4、pending-features P3teacher-portal 阶段 2 架构设计 状态:待 coord 交叉审查


1. 模块内部分层图student-portal Remote 视角)

graph TB
    subgraph Browser["浏览器(学生)"]
        URL[URL 路由 /student/*]
    end

    subgraph Shell["teacher-portalShell 宿主)"]
        AppShell[AppShell<br/>左栏导航 + 主内容区]
        RootLayout[RootLayout<br/>字体/令牌/i18n/Query Provider]
        SharedDeps["共享依赖暴露<br/>react/react-dom/@tanstack/react-query/zustand/nuqs<br/>ui-components/ui-tokens/contracts/hooks"]
    end

    subgraph RemoteStudent["student-portalRemote"]
        StudentPages["学习场景页面<br/>dashboard/homework/submit/exams/take<br/>diagnostic(P4)/weakness(P4)/notifications(P5)"]
        ExamTaking[ExamTaking 组件<br/>倒计时 + 自动保存]
        StudentApiClient[ApiClient 实例<br/>注入 student scope token]
    end

    subgraph Shared["共享层packages/"]
        UITokens[ui-tokens<br/>三层设计令牌]
        UIComponents[ui-components<br/>shadcn + A11y + ErrorBoundary]
        Contracts[contracts<br/>Permissions 常量 + 类型]
        Hooks[hooks<br/>usePermission/useAuth/useA11y]
        LibTS[shared-ts<br/>ApiClient/Logger/通用工具]
    end

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

    subgraph PushGW["push-gatewayP5"]
        WS[WebSocket /ws]
    end

    Browser --> URL
    URL --> RootLayout
    RootLayout --> AppShell
    AppShell -->|动态加载 Remote| RemoteStudent

    RemoteStudent -->|复用| SharedDeps
    RemoteStudent --> ExamTaking
    RemoteStudent --> StudentApiClient

    Shell --> UITokens
    Shell --> UIComponents
    Shell --> Contracts
    Shell --> Hooks
    Shell --> LibTS
    RemoteStudent --> UITokens
    RemoteStudent --> UIComponents
    RemoteStudent --> Contracts
    RemoteStudent --> Hooks

    StudentApiClient -->|fetch /api/v1/student/*| GW
    StudentApiClient -->|fetch /api/v1/iam/*| GW
    StudentApiClient -->|fetch /api/v1/notifications/*| GW

    RemoteStudent -.->|P5 WebSocket| WS
    WS -.->|推送事件| RemoteStudent

1.1 Remote 角色定位

student-portal 作为 Remote 子应用,由 teacher-portal Shell 动态加载。与 Shell 的职责分工:

职责 Shellteacher-portal Remotestudent-portal
RootLayout字体/令牌/Provider 提供 复用
AppShell左栏 + 主内容区) 提供 复用
共享依赖暴露singleton 提供 消费
登录页 提供 复用
路由表 /student/* 由 Remote 暴露 提供
学习场景页面
ApiClient 实例 提供 ApiClient 类 注入 student scope
业务组件ExamTaking 等)

1.2 MF 配置student-portal/next.config.js

// student-portal/next.config.jsRemote
const NextFederationPlugin = require("@module-federation/nextjs-mf");

const remotes = (isServer) => ({
  teacher: `teacher_app@http://localhost:3000/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
});

module.exports = {
  reactStrictMode: true,
  webpack(config, { isServer }) {
    config.plugins.push(
      new NextFederationPlugin({
        name: "student_app",
        filename: "static/chunks/remoteEntry.js",
        remotes: remotes(isServer),
        exposes: {
          "./pages": "./src/pages",
          "./ExamTaking": "./src/components/ExamTaking",
        },
        shared: {
          react: { singleton: true, requiredVersion: "^18.3.0" },
          "react-dom": { singleton: true, requiredVersion: "^18.3.0" },
          "@tanstack/react-query": { singleton: true },
          zustand: { singleton: true },
          nuqs: { singleton: true },
        },
        extraOptions: { exposePages: false },
      }),
    );
    return config;
  },
  async rewrites() {
    return [
      {
        source: "/api/v1/:path*",
        destination: `${process.env.API_GATEWAY_URL || "http://localhost:8080"}/api/v1/:path*`,
      },
    ];
  },
};

关键点

  • name: 'student_app'Remote 应用名,与 Shell 中 remotes.student 对应
  • exposes:暴露页面入口 ./pages 和 student 特有组件 ./ExamTaking
  • remotes.teacher:引用 Shell 提供的 AppShell 和 shared-deps
  • shared:与 Shell 一致的 singleton 策略,保证 React/Query/Zustand 单例
  • rewrites:与 Shell 一致的 /api/v1/* 代理到 api-gateway

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

前端不持有业务聚合根,仅持有"视图模型"ViewModel和"会话状态"。Session/Viewport/Permission 三模型与 Shell 共享,详见 teacher-portal 阶段 2 §2

2.1 会话状态Session

复用 Shell 的 Session 模型student-portal 不重复定义。详见 teacher-portal 文档 §2.1。

2.2 视口模型Viewport

复用 Shell 的 ViewportItem 模型,但 scope 字段值为 'student'。来源:GET /api/v1/student/viewportsstudent-bff 聚合 iam 视口配置。AppShell 按 scope='student' 过滤渲染学生端导航。

2.3 权限模型Permission

复用 Shell 的 PermissionState 模型。来源:GET /api/v1/iam/effective-permissions{ permissions, viewports, dataScope }。学生 dataScope 固定为 L0仅本人

2.4 student-portal 特有视图模型

// 考试作答草稿Zustand L3断网恢复用
interface ExamTakingDraft {
  examId: string;
  answers: Record<string, AnswerInput>; // questionId → answer
  startedAt: number;
  lastSavedAt: number | null;
  durationSeconds: number; // 考试时长(秒)
}

// 作业提交表单react-hook-form L5
interface HomeworkSubmitForm {
  homeworkId: string;
  answers: AnswerInput[];
  attachments?: File[]; // 附件(图片/PDF
  note?: string; // 学生备注
}

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

前端无数据库仅有缓存层。student-portal 缓存策略偏短(学生数据实时性要求高):

数据类型 存储 TTL 失效策略
Sessiontoken + user localStorage + Zustand 复用 Shellaccess 15min / refresh 7day401 自动 refresh
权限列表 TanStack Query cache 5min 复用 Shell角色变更事件 invalidate
视口列表scope=student TanStack Query cache 5min 复用 Shell
学生 Dashboard 聚合数据 TanStack Query cache 30s staleTime 30s事件触发 invalidate
我的作业列表 TanStack Query cache 30s staleTime 30s提交后 invalidate
我的考试列表 TanStack Query cache 30s staleTime 30sExamPublished 事件 invalidate
考试作答草稿 Zustand L3 + localStorage 卸载不销毁,自动保存(每 30s + blur 时)
学情诊断数据 TanStack Query cache 30s staleTime 30s实时性由 BFF 决定)
错题本列表 TanStack Query cache 30s staleTime 30s
通知列表P5 TanStack Query cache 30s staleTime 30sWebSocket 事件 invalidate
URL 状态(分页/筛选) nuqs 永久(可分享)
表单临时态(作业提交) react-hook-form 卸载即销毁

4. API 设计(前端 → 后端)

前端不设计后端 API仅声明消费的端点。详见 01-understanding.md §3.1

4.1 统一 API 请求层(复用 Shell ApiClient

student-portal 不重复实现 ApiClient 类,复用 packages/shared-ts/src/api-client.ts(详见 teacher-portal 阶段 2 §4.1)。

student-portal 仅注入不同 baseUrl/token 和 scope 标记:

// apps/student-portal/src/lib/api.ts
import { ApiClient } from "@edu/shared-ts";
import { useAuth } from "@edu/hooks";

export function useStudentApi(): ApiClient {
  const { getToken, logout } = useAuth();
  return useMemo(
    () =>
      new ApiClient({
        baseUrl: "", // 走 Next.js rewrites
        getToken,
        onUnauthorized: logout,
        scope: "student", // 日志/trace 标记
      }),
    [getToken, logout],
  );
}

职责(与 Shell 一致,由 shared-ts ApiClient 承担):

  • 自动注入 Authorization: Bearer ${token}
  • 401 自动 refresh token 一次,失败调 onUnauthorized
  • 解析 ActionStatesuccess=false 抛 ApiError
  • error.code 前缀路由 i18n key
  • 全局错误 toast除 401
  • 请求/响应 trace_id 透传(从响应头 X-Request-Id 提取)

4.2 TanStack Query 约定

// Query Key 命名:[scope, resource, ...args]
queryKey: ["student", "dashboard"];
queryKey: ["student", "homework", { status, page }];
queryKey: ["student", "exams", { status }];
queryKey: ["student", "exam-taking", examId];
queryKey: ["student", "diagnostic"];
queryKey: ["student", "weakness", { knowledgePointId }];
queryKey: ["student", "notifications", { unreadOnly }];
queryKey: ["session", "effective-permissions"]; // 复用 Shell
queryKey: ["session", "viewports", "student"]; // 复用 Shell

// Mutation 约定
const submitHomework = useMutation({
  mutationFn: (input) =>
    api.post(`/api/v1/student/homework/${input.homeworkId}/submit`, input),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["student", "homework"] });
    toast.success(t("homework.submitSuccess"));
  },
  onError: (e: ApiError) => toast.error(e.message),
});

const saveExamAnswer = useMutation({
  mutationFn: (input) =>
    api.post(`/api/v1/student/exams/${input.examId}/answers`, input),
  onSuccess: (data) => {
    // 更新本地草稿的 lastSavedAt
    examTakingStore.updateLastSavedAt(data.savedAt);
  },
  onError: (e: ApiError) => {
    // 草稿保留在本地,下次 blur/定时重试
    logger.warn("exam answer save failed", { examId: e.details });
  },
});

5. 事件设计

前端不发布 Kafka 事件,仅消费 WebSocket 推送P5

5.1 WebSocket 推送P5

事件 触发 student-portal 前端动作
NotificationRequested msg 服务投递 toast 提示 + 通知中心未读数 +1
ExamPublished 教师发布考试 toast + ["student","exams"] invalidate + dashboard invalidate
GradeRecorded 教师录入成绩 toast + ["student","grades"] invalidate
HomeworkDeadlineApproaching 作业截止前提醒 toast + 作业列表高亮 + ["student","homework"] invalidate

5.2 WebSocket 连接管理P5

// apps/student-portal/src/lib/ws.tsP5 实现)
export function useStudentWebSocket() {
  const { getToken } = useAuth();
  const queryClient = useQueryClient();

  useEffect(() => {
    const ws = new WebSocket(
      `${process.env.NEXT_PUBLIC_PUSH_GATEWAY_URL}/ws?token=${getToken()}`,
    );
    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data) as PushMessage;
      switch (msg.type) {
        case "NotificationRequested":
          toast.info(t(msg.payload.title));
          queryClient.invalidateQueries({
            queryKey: ["student", "notifications"],
          });
          break;
        case "ExamPublished":
          toast.info(t("exam.published", { name: msg.payload.examName }));
          queryClient.invalidateQueries({ queryKey: ["student", "exams"] });
          queryClient.invalidateQueries({ queryKey: ["student", "dashboard"] });
          break;
        case "GradeRecorded":
          toast.success(t("grade.recorded"));
          queryClient.invalidateQueries({ queryKey: ["student", "grades"] });
          break;
        case "HomeworkDeadlineApproaching":
          toast.warning(
            t("homework.deadlineApproaching", {
              name: msg.payload.homeworkName,
            }),
          );
          queryClient.invalidateQueries({ queryKey: ["student", "homework"] });
          break;
      }
    };
    return () => ws.close();
  }, [getToken, queryClient]);
}

5.3 考试作答自动保存SSE / HTTP 轮询)

考试作答的自动保存采用 HTTP POST每 30s + blur 时),不使用 SSE。SSE 仅 teacher-portal 用于 AI 流式出题student-portal 不涉及。

6. 横切关注点对齐清单

6.1 权限(前端等价)

路由 requiredPermission
/student/dashboard STUDENT_DASHBOARD_VIEW
/student/homework HOMEWORK_READ_OWN
/student/homework/:id/submit HOMEWORK_SUBMIT
/student/exams EXAMS_READ_OWN
/student/exams/:id/take EXAMS_TAKE
/student/diagnostic DIAGNOSTIC_READ_OWN
/student/weakness WEAKNESS_READ_OWN
/student/notifications NOTIFICATION_READ_OWN

完整权限点常量集中在 packages/contracts/src/permissions.ts待建立coord 负责 contractsai07 负责调用。L3 组件级视口用 <RequirePermission perm="HOMEWORK_SUBMIT"><Button>提交作业</Button></RequirePermission>。权限点后缀 _OWN 强调学生仅能操作自己的数据DataScope L0

6.2 错误码清单(前端 i18n 路由)

前缀 来源服务 i18n key 模式
IAM_ iam iam.error.{{code}}
CORE_EDU_ core-edu coreEdu.error.{{code}}
EXAMS_ core-edu exams.error.{{code}}
HOMEWORK_ core-edu homework.error.{{code}}
GRADES_ core-edu grades.error.{{code}}
BFF_STUDENT_ student-bff bff.error.{{code}}
GW_ api-gateway gateway.error.{{code}}
NETWORK_ 前端网络层 network.error.{{code}}

6.3 Logger

复用 packages/shared-ts/src/logger.ts(同 teacher-portal开发环境 console + 结构化,生产环境 → SentryP6。必含字段trace_iduser_idscope=studentpath

// 使用示例student-portal 内)
import { createLogger } from "@edu/shared-ts";
const logger = createLogger({ scope: "student" });
logger.info("exam taking started", { examId, durationSeconds });
logger.error("homework submit failed", { homeworkId, error: err.code });

6.4 MetricsWeb Vitals

指标 类型 上报
student_portal_lcp_seconds LCP next/web-vitalsPOST /api/v1/admin/web-vitals
student_portal_cls CLS 同上
student_portal_fid_seconds FID 同上
student_portal_ttfb_seconds TTFB 同上

P6 接入P3-P5 暂缓。

6.5 TracerOTel browser SDKP6

复用 packages/shared-ts/src/tracer.ts(同 teacher-portal。BatchSpanProcessor → OTLP exporter → collector → Tempo。自动埋点fetch、XMLHttpRequest、document load、user interaction。

6.6 健康检查

端点 用途 实现
GET /api/health Dockerfile HEALTHCHECK Next.js Route Handler返回 { status: 'ok', ts: Date.now() }
GET /api/ready K8s readinessProbe 检查 process.env.API_GATEWAY_URL 可达 + 内存 < 阈值

6.7 优雅关闭

Next.js 无长连接(除 WS无需特殊处理。WS 在 P5 由 push-gateway 管理,前端断线自动重连(指数退避,最多 5 次)。

7. 共享组件库(复用 Shell 暴露 + student 特有)

7.1 复用 Shell 暴露的组件packages/ui-components/

组件 用途 来源
AppShell 左侧栏 + 主内容区布局 teacher-portal Shell 暴露
RequirePermission L3 组件级视口控制(无权限不渲染 children Shell 暴露
ErrorBoundary React 渲染异常兜底fallback UI Shell 暴露
Loading 骨架屏Skeleton Shell 暴露
Empty 空态(插画 + 文案 + CTA Shell 暴露
Modal / Dialog 全局 ModalModalRoot + Zustand ui-store Shell 暴露shadcn/ui
Toast 全局 toast错误/成功/警告) Shell 暴露shadcn/ui sonner
Button / Input / Select / Textarea 基础表单 Shell 暴露shadcn/ui
DataTable 表格(排序/分页/筛选) Shell 暴露
Chart 图表封装recharts Shell 暴露
A11y 工具集 useA11yId / mergeA11yProps / describeInput / focus-trap / skip-link / visually-hidden / aria-status Shell 暴露
Form react-hook-form + zodResolver 封装 Shell 暴露

7.2 student-portal 特有组件(不暴露给 Shell

组件 用途 来源 是否暴露给 Shell
ExamTaking 考试作答(倒计时 + 自动保存 + 断网恢复) 新建 暴露 ./ExamTaking(供 Shell 路由复用)
HomeworkSubmit 作业提交表单react-hook-form + zodResolver 新建 内部使用
DiagnosticChart 学情诊断图表recharts 多维雷达 + 趋势线) 新建 内部使用
WeaknessList 错题本列表(按知识点聚合 + 掌握度标签) 新建 内部使用

7.3 不使用的组件(与 teacher-portal 差异)

  • 不使用 RichTextEditorTiptap:学生不作答富文本,作业提交用表单
  • 不使用 SSEViewer:学生不参与 AI 出题
  • 不使用 ChildSwitcher:学生无多子女切换(家长端特有)
  • 不使用 UserManagementTable:学生不管理用户

8. 共享 Hooks复用 Shell 暴露)

Hook 职责 来源
useAuth() 会话状态user/token/refresh/login/logout Shell 暴露
usePermission() 权限查询hasPermission/hasAny/hasAll + dataScope Shell 暴露
useViewports(scope) 视口列表(按 scope 过滤) Shell 暴露
useApi() ApiClient 实例(注入 token + 401 处理) Shell 暴露
useA11yId() 唯一 ARIA ID 生成 Shell 暴露
useAriaLive() aria-live 区域管理 Shell 暴露
useToast() 全局 toastZustand ui-store Shell 暴露

9. 设计令牌三层(复用 Shell 提供的 packages/ui-tokens/

student-portal 不独立维护设计令牌,复用 Shell 暴露的 packages/ui-tokens/(详见 teacher-portal 阶段 2 §9)。

强制规则project_rules §3.10,与 Shell 一致):

  • 禁止 #hex 字面量ESLint no-restricted-syntax
  • 禁止 'Inter'/'Fraunces'/'JetBrains Mono' 字面量ESLint design-tokens/no-hardcoded-fonts
  • 禁止 font-size: Npx(用 var(--font-size-1~9)
  • 禁止 Tailwind 任意值 w-[Npx](用 --space-* 或默认阶梯)

student-portal 的 ESLint flat config 与 Shell 共用,确保规则一致。

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

方向 对方服务 协议 接口/事件 用途 阶段
调用 api-gateway HTTP/REST /api/v1/* 代理 全部业务请求 P1+
调用 push-gateway WebSocket ws://push-gateway/ws 实时推送 P5
被调用 前端不暴露接口给其他服务
消费 student-bff HTTP经 Gateway GET /student/viewports 学生场景聚合 P3+
消费 iam HTTP经 Gateway /iam/* 登录/权限/视口 P2+
消费 core-edu HTTP经 Gateway /exams/* /homework/* /grades/* 教学核心(学生视角) P3+
消费 data-ana HTTP经 Gateway /analytics/* 学情分析 P4+
消费 msg HTTP经 Gateway /notifications/* 通知中心 P5+
依赖 coord 维护 packages/shared-proto TS 类型(仅 contracts 部分) P1+
依赖 coord 维护 packages/shared-ts(待建) ApiClient/Logger/通用工具 P3+
依赖 ai07 维护 packages/ui-tokens(待建) 三层设计令牌 P3+
依赖 ai07 维护 packages/ui-components(待建) shadcn + 共享组件 P3+
依赖 ai07 维护 packages/hooks(待建) usePermission/useAuth 等 P3+
依赖 coord 维护 packages/contracts(待建) Permissions 常量 + 类型 P3+

proto 不直接消费:前端不调用 gRPCBFF 把 gRPC 聚合为 REST 暴露给前端。前端仅消费 packages/contracts/src/permissions.ts 中的权限点常量TS 文件,非 proto 生成)。

11. 风险与假设

11.1 假设

  1. 假设 coord 建立 packages/shared-tspackages/contracts:包含 ApiClient、Logger、Permissions 常量、通用类型。若 coord 未建立ai07 自行在 apps/student-portal/src/shared/ 内实现,后续提取到 packages。
  2. 假设 ai04 student-bff 提供 GET /student/viewportsGET /student/dashboardGET /student/homeworkPOST /student/homework/:id/submitGET /student/diagnostic:返回 ActionState 结构。错误码前缀 BFF_STUDENT_(待 ai04 确认)。
  3. 假设 teacher-portal Shell 已就绪AppShell + 共享依赖暴露 + MF 配置P2 收尾完成。student-portal 作为 Remote 才能挂载。
  4. 假设 Next.js 14+ Module Federation 2.0 稳定@module-federation/nextjs-mf 在 Next.js App Router 下可用。若不稳定,降级为 4 端独立部署 + 各自 Shell重复实现 AppShell

11.2 技术风险

风险 影响 缓解
MF Remote SSR 对齐复杂 Remote 在 SSR 时需 Shell 提供上下文 优先 CSRSSR 仅用于首屏 dashboardMF 2.0 支持 SSR
共享依赖版本漂移 Remote 与 Shell 的 react/react-dom 版本不一致导致运行时错误 MF shared.singleton: true + CI 检查版本对齐
考试作答断网丢失 学生作答过程中断网,草稿丢失 Zustand L3 + localStorage 双写,每 30s + blur 时自动保存,重连后重试
考试倒计时不准 学生端时间与服务器时间偏差 倒计时基于服务器返回的 expiresAt,前端仅做展示,提交以服务器时间为准
缓存陈旧导致看到旧作业 学生看到已截止的作业 staleTime 30s + 截止时间客户端校验 + 提交时服务端二次校验
Token 刷新竞态 多请求同时 401 触发多次 refresh ApiClient 全局单例 + refresh promise 复用(复用 Shell
权限缓存陈旧 角色变更后前端 5min 内仍用旧权限 iam 角色变更发 Kafka 事件 → msg 推送 WebSocket → 前端 invalidate
TanStack Query 缓存膨胀 长时间使用后缓存项过多 gcTime 5min + staleTime 按数据类型分级

11.3 未决设计决策(需 coord 仲裁)

与 teacher-portal 相同的 4 项(详见 teacher-portal 阶段 2 §11.3

  1. packages 归属ui-tokens / ui-components / hooks 是 ai07 维护还是 coord 维护建议ai07 维护前端专属coord 仅维护 shared-ts / contracts(跨语言/跨服务)。
  2. GraphQL vs RESTstudent-bff 是 REST 还是 GraphQL前端 API 请求层是否需要 GraphQL clienturql/apollo建议P3-P4 用 REST若 BFF 切 GraphQL 再引入 urql。
  3. i18n key 命名error.{{service}}.{{code_snake_case}} 还是其他模式?建议:与 teacher-portal 对齐。
  4. MF 暴露粒度Shell 暴露整个 AppShell 还是更细粒度的组件?建议:暴露 AppShell 整体 + 各 Remote 自行决定内部布局。

12. coord 交叉审查所需信息

12.1 端口矩阵

dev 端口 生产端口 备注
student-portal 3001 3001 Remote 子应用

full-stack-runbook 端口矩阵对齐。

12.2 依赖的共享包(需 coord 建立)

路径 维护方 内容
shared-ts packages/shared-ts/ coord ApiClient、Logger、通用工具
contracts packages/contracts/ coord Permissions 常量、ActionState 类型、UserInfo 类型
ui-tokens packages/ui-tokens/ ai07建议 三层设计令牌
ui-components packages/ui-components/ ai07建议 shadcn + ErrorBoundary + RequirePermission
hooks packages/hooks/ ai07建议 usePermission、useAuth、useViewports

12.3 依赖的后端契约(需对应 AI 确认)

契约 提供方 当前状态
POST /iam/loginGET /iam/effective-permissionsGET /iam/me iam 已实现
GET /student/viewportsGET /student/dashboard student-bff 📐 待 ai04 设计
/exams/* /homework/* /grades/* core-edu 已实现P3
/analytics/* data-ana 已实现P4
/notifications/* + WebSocket 推送 msg + push-gateway 📐 待 P5

12.4 错误码前缀(前端 i18n 路由依赖)

前端不产生错误码,仅消费。需各服务确认错误码前缀不重叠:

前缀 服务 状态
IAM_ iam 已用
EXAMS_/HOMEWORK_/GRADES_ core-edu ⚠️ 待确认
BFF_STUDENT_ student-bff ⚠️ 待 ai04 确认
GW_ api-gateway 已用
NETWORK_ 前端 ai07 自有

12.5 不产生 Kafka 事件

前端不发布/消费 Kafka 事件。WebSocket 推送由 push-gateway 消费 Kafka 转发。

13. 实施路线ai07 自用)

P3student-portal 起步)

  1. apps/student-portal/Remote 角色)
  2. 配置 MFexposes: { './pages', './ExamTaking' }remotes: { teacher: ... }
  3. 实现 Dashboard + 我的作业 + 提交作业 + 我的考试 + 作答考试
  4. 复用 Shell 的 AppShell + 共享组件RequirePermission/ErrorBoundary/Loading/Empty/DataTable/Form/Chart
  5. 考试作答自动保存HTTP POST 每 30s + blur 时Zustand L3 + localStorage 双写草稿)
  6. 配置 Dockerfile 多阶段构建 + /api/health + /api/ready route
  7. 补 Vitest 单测 + Playwright E2E覆盖率 ≥ 80%

P4学情诊断 + 错题本)

  1. 实现学情诊断页面DiagnosticChart 多维雷达 + 趋势线)
  2. 实现错题本页面WeaknessList 按知识点聚合 + 掌握度标签)

P5推送 + 通知中心)

  1. student-portal 接入 WebSocketpush-gateway
  2. 实现通知中心页面(通知列表 + 未读数 + 标记已读)
  3. 处理 4 类推送事件NotificationRequested/ExamPublished/GradeRecorded/HomeworkDeadlineApproaching

P6硬化

  1. Web Vitals + OTel browser SDK 接入
  2. A11y WCAG 2.2 AA 审计
  3. 性能优化MF shared 单例验证、bundle 分析)

AI Agent: ai07 (student-portal remote) Branch: docs/student-portal-stage1-stage2-design-ai07 Coordinator: coord-ai