Files
NextEdu/docs/architecture/004_architecture_impact_map.md

36 KiB

Next_Edu 架构设计文档

版本: V3 (2026-07-07,从"影响地图"重构为"架构设计文档") 目标: 系统性展示项目架构设计、技术栈选型、跨模块协作机制、架构约束与决策 数据来源: arch.db(npm run arch:scan 生成) + 人工整理 配套文档: 各模块 README.md(模块级架构图与流程图) · coding-standards.md · known-issues.md


目录

  1. 项目概览
  2. 技术栈选型
  3. 分层架构
  4. 模块依赖全景图
  5. 认证与权限架构
  6. 数据访问与缓存架构
  7. 状态管理架构(5 层模型)
  8. 跨模块协作机制
  9. 核心业务流程
  10. 设计令牌体系
  11. API 与实时通信架构
  12. 架构约束与设计原则
  13. 架构决策记录
  14. 附录

1. 项目概览

Next_Edu 是一套 K12 智慧教务系统,采用单 Next.js 应用 + 严格模块化架构,服务四类角色(管理员/教师/学生/家长)的全流程教学活动。

1.1 系统边界

graph TB
    subgraph Users["用户"]
        Admin[管理员]
        Teacher[教师]
        Student[学生]
        Parent[家长]
    end

    subgraph NextEdu["Next_Edu 系统"]
        Web[Web 应用<br/>Next.js 16 App Router]
        API[REST API<br/>17 个路由]
        SSE[SSE 实时流<br/>AI 对话 + 通知]
    end

    subgraph External["外部依赖"]
        MySQL[(MySQL 8.0<br/>54 张表)]
        Redis[Redis<br/>可选,限流/缓存]
        AI[AI Provider<br/>OpenAI/Anthropic/...]
        Storage[文件存储<br/>本地/云]
        SMS[SMS/Email/WeChat]
    end

    Admin --> Web
    Teacher --> Web
    Student --> Web
    Parent --> Web

    Web --> MySQL
    Web --> Redis
    Web --> AI
    Web --> Storage
    Web --> SMS

    API --> MySQL
    SSE --> AI
    SSE --> MySQL

1.2 规模指标(arch.db)

指标 数值
模块数 35 个业务模块 + shared 基础设施层
源文件数 1030
符号数 5913
函数调用数 36845
数据库表 54
API 路由 17
权限点 61
系统角色 6 (admin/teacher/student/parent/grade_head/teaching_head)

2. 技术栈选型

2.1 核心框架

类别 技术 版本 选型理由
全栈框架 Next.js 16 (App Router) RSC + Server Actions + Edge middleware(proxy.ts)
语言 TypeScript 5.x strict 模式,禁止 any/as
数据库 MySQL 8.0 FULLTEXT 索引 + JSON 字段 + 事务
ORM Drizzle latest 类型安全 + 轻量 + 迁移可控
认证 NextAuth.js 4.x JWT + Credentials Provider + 2FA
运行时 Node.js 22 原生 fetch / Web Crypto

2.2 状态与数据获取(arch.db 技术标签统计)

graph LR
    subgraph State["状态管理 5 层模型"]
        L1[L1 URL<br/>nuqs<br/>16 处]
        L2[L2 Server<br/>TanStack Query<br/>18 处]
        L3[L3 Client Business<br/>Zustand slice<br/>22 处]
        L4[L4 Global UI<br/>Zustand ui-store<br/>+ ModalRoot]
        L5[L5 Form<br/>react-hook-form<br/>32 处<br/>+ zodResolver]
    end

    subgraph Optimistic["乐观更新"]
        Opt[useOptimistic<br/>13 处<br/>消息星标/成绩编辑/考勤提交]
    end

    L1 --> L2 --> L3 --> L4 --> L5
    L3 -.-> Opt

2.3 UI 与可视化

类别 技术 用途规模
样式 Tailwind CSS v4 双层令牌(Primitive + Semantic) + @theme inline
组件库 shadcn/ui 基于 Radix UI,自托管可定制
富文本 Tiptap 68 处,备课 + 试卷富文本编辑
图表 recharts 101 处,12 个图表类型
力导向图 自定义 force-graph 知识图谱(textbooks 模块)
图标 lucide-react 统一图标库

2.4 服务端基础设施(arch.db 技术标签统计)

技术 用途规模 说明
Drizzle ORM 1540 处 所有 DB 访问
cacheFn 1279 处 封装 React cache() + 自定义缓存层,替代 unstable_cache
Server Action 454 处 编排层:权限校验 + data-access 调用 + revalidate

3. 分层架构

项目采用严格三层架构,依赖方向必须单向: app → modules → shared

graph TB
    subgraph AppLayer["app/ 路由层"]
        direction LR
        Auth["(auth)/<br/>login/register/privacy/terms"]
        Dash["(dashboard)/<br/>admin/teacher/student/parent/management"]
        APIRoutes["api/<br/>17 个 REST 路由<br/>auth/ai/upload/files/search/..."]
    end

    subgraph ModulesLayer["modules/ 业务模块层 (35 个模块)"]
        direction LR
        Core["核心教学<br/>exams·homework·questions<br/>·textbooks·grades<br/>·lesson-preparation"]
        Mgmt["教学管理<br/>classes·school·scheduling<br/>·attendance·course-plans<br/>·standards"]
        User["用户沟通<br/>users·messaging·notifications<br/>·parent·audit·rbac<br/>·auth·onboarding"]
        Ext["扩展功能<br/>ai·dashboard·diagnostic<br/>·adaptive-practice·error-book<br/>·elective·proctoring<br/>·leave-requests<br/>·invitation-codes"]
        Other["其他<br/>files·settings·layout<br/>·student·search<br/>·announcements"]
    end

    subgraph SharedLayer["shared/ 基础设施层"]
        direction LR
        DB["db/<br/>schema.ts (54 表)<br/>relations.ts<br/>index.ts"]
        Lib["lib/<br/>auth-guard·permissions<br/>·audit-logger·change-logger<br/>·ai·rate-limit·excel<br/>·file-storage·session<br/>·permission-bitmap<br/>·route-permissions"]
        Hooks["hooks/<br/>use-permission<br/>·use-aria-live"]
        Components["components/<br/>ui/(shadcn)·a11y/<br/>·global-search<br/>·schedule·charts"]
        Types["types/<br/>permissions·action-state<br/>·errors"]
    end

    subgraph Root["根模块 (应用层)"]
        AuthCfg["src/auth.ts<br/>NextAuth 配置"]
        Proxy["src/proxy.ts<br/>Edge 中间件"]
        Env["src/env.mjs<br/>环境变量校验"]
    end

    AppLayer -->|"调用 Server Actions<br/>+ data-access"| ModulesLayer
    ModulesLayer -->|"依赖"| SharedLayer
    AppLayer -->|"直接使用"| SharedLayer

    Root -.->|"配置"| AppLayer
    Root -.->|"被 shared/lib/session.ts<br/>dynamic import"| SharedLayer

    style AppLayer fill:#e0f2fe
    style ModulesLayer fill:#fef3c7
    style SharedLayer fill:#dcfce7
    style Root fill:#fce7f3

3.1 分层规则

层级 允许的依赖 禁止的依赖
app/ modules/* 的 Server Actions + data-access;shared/* 直接访问 DB
modules/* shared/*;其他 modules/* 的 data-access 直接查询他模块 DB 表;反向依赖 shared
shared/* 自身内部 反向依赖 @/auth/@/proxy/modules/*(用 dynamic import 打破循环)

3.2 模块标准结构

graph LR
    subgraph Module["src/modules/[module]/"]
        Actions["actions.ts<br/>Server Actions 编排层<br/>(权限+调用+revalidate)"]
        DA["data-access.ts<br/>DB CRUD<br/>(可拆 data-access-*.ts)"]
        Schema["schema.ts<br/>Zod 验证 (可选)"]
        Types["types.ts<br/>类型定义"]
        Components["components/<br/>模块专属组件"]
        Hooks["hooks/<br/>模块专属 Hook (可选)"]
        Lib["lib/<br/>纯函数/类型守卫 (可选)"]
    end

    subgraph Flow["调用流向"]
        F1[Client Component] -->|"use server"| Actions
        F2[Server Component] -->|"直接调用"| DA
        Actions -->|"编排"| DA
        Actions -->|"权限校验"| RequirePerm[requirePermission]
        DA -->|"DB CRUD"| DB[(MySQL)]
    end

    Components --> Actions
    Hooks --> Actions
    Components --> Hooks

4. 模块依赖全景图

完整依赖查询: npm run arch:query -- sql "SELECT sm.name as source, tm.name as target, md.dep_type FROM module_deps md JOIN modules sm ON md.source_module_id=sm.id JOIN modules tm ON md.target_module_id=tm.id WHERE md.dep_type='data-access-call' AND sm.name<>'shared' AND tm.name<>'shared'"

4.1 核心依赖关系(mermaid)

graph TB
    subgraph Foundation["基础层(被广泛依赖)"]
        Shared[shared]
        Users[users]
        Classes[classes]
        School[school]
    end

    subgraph Content["内容层"]
        Textbooks[textbooks<br/>教材/知识点]
        Questions[questions<br/>题库]
        Standards[standards<br/>课程标准]
    end

    subgraph Core["核心教学层"]
        Exams[exams<br/>试卷]
        Homework[homework<br/>作业]
        Grades[grades<br/>成绩]
        LessonPrep[lesson-preparation<br/>备课]
    end

    subgraph Analytics["分析诊断层"]
        Diagnostic[diagnostic<br/>学情诊断]
        ErrorBook[error-book<br/>错题本]
        Adaptive[adaptive-practice<br/>自适应练习]
    end

    subgraph Aggregation["聚合层"]
        Dashboard[dashboard<br/>仪表盘]
        Parent[parent<br/>家长端]
    end

    subgraph Communication["沟通层"]
        Messaging[messaging]
        Notifications[notifications]
        Announcements[announcements]
    end

    subgraph Management["管理层"]
        Attendance[attendance]
        Scheduling[scheduling]
        CoursePlans[course-plans]
        Elective[elective]
        LeaveRequests[leave-requests]
        Rbac[rbac]
        Audit[audit]
        Onboarding[onboarding]
        Auth[auth]
        Settings[settings]
    end

    subgraph Extension["扩展层"]
        AI[ai]
        Files[files]
        Proctoring[proctoring]
        Search[search]
        Layout[layout]
        Student[student]
        InvitationCodes[invitation-codes]
    end

    %% 内容层依赖基础层
    Textbooks --> Classes
    Textbooks --> School
    Textbooks --> Users
    Questions --> Textbooks

    %% 核心教学层依赖内容层 + 基础层
    Exams --> Questions
    Exams --> Classes
    Exams --> School
    Homework --> Classes
    Homework --> School
    Homework --> Users
    Grades --> Classes
    Grades --> School
    Grades --> Users
    LessonPrep --> Textbooks
    LessonPrep --> Questions
    LessonPrep --> Exams
    LessonPrep --> Homework
    LessonPrep --> Classes
    LessonPrep --> Standards

    %% 分析层依赖核心层
    Diagnostic --> Questions
    Diagnostic --> Classes
    Diagnostic --> Users
    ErrorBook --> Questions
    ErrorBook --> Classes
    Adaptive --> Classes
    Adaptive --> Users

    %% 聚合层依赖一切
    Dashboard --> Classes
    Dashboard --> Homework
    Dashboard --> Questions
    Dashboard --> Textbooks
    Dashboard --> Users
    Dashboard --> Parent
    Parent --> Classes
    Parent --> Homework
    Parent --> Grades
    Parent --> School
    Parent --> Users

    %% 核心层依赖分析/通知层(闭环)
    Homework --> Diagnostic
    Homework --> ErrorBook
    Homework --> Notifications
    Grades --> Diagnostic
    Grades --> ErrorBook
    Grades --> Notifications
    Grades --> Attendance
    Grades --> Parent

    %% 沟通层
    Messaging --> Classes
    Messaging --> Users
    Messaging --> Files
    Messaging --> Parent
    Announcements --> Classes
    Announcements --> School
    Announcements --> Users

    %% 管理层
    Attendance --> Classes
    Attendance --> School
    Attendance --> Users
    Attendance --> Grades
    Attendance --> Parent
    Scheduling --> Classes
    Scheduling --> School
    Scheduling --> Users
    CoursePlans --> Classes
    CoursePlans --> School
    CoursePlans --> Users
    Elective --> Classes
    Elective --> School
    Elective --> Users
    LeaveRequests --> Classes
    LeaveRequests --> Attendance
    LeaveRequests --> Users
    LeaveRequests --> Parent
    Rbac --> Classes
    Rbac --> Parent
    Rbac --> School
    Settings --> Classes
    Settings --> Files
    Settings --> Homework
    Settings --> Onboarding
    Settings --> Users
    Auth --> Classes
    Onboarding --> Classes

    %% 扩展层
    AI --> Textbooks
    Proctoring --> Users
    Search --> Shared

4.2 依赖深度统计

层级 模块 特征
L0 基础层 shared · users · classes · school 被几乎所有模块依赖,自身不依赖业务模块
L1 内容层 textbooks · questions · standards 教学内容基础,被核心层依赖
L2 核心教学层 exams · homework · grades · lesson-preparation 业务核心,依赖内容层 + 基础层
L3 分析诊断层 diagnostic · error-book · adaptive-practice 依赖核心层数据,产出分析洞察
L4 聚合层 dashboard · parent 依赖一切,为前端提供聚合视图
横切层 messaging · notifications · audit · rbac · attendance · scheduling · ai · files 被核心层调用,提供横切功能

4.3 循环依赖处理

graph LR
    subgraph Problem["潜在循环(已解决)"]
        SharedLib["shared/lib/*<br/>{audit-logger, change-logger,<br/>auth-guard, login-logger}"]
        Session["shared/lib/session.ts<br/>单一入口"]
        AuthCfg["src/auth.ts<br/>NextAuth 配置"]
    end

    SharedLib -.->|"需要 session"| Session
    Session -.->|"dynamic import<br/>打破静态循环"| AuthCfg
    AuthCfg -.->|"正常依赖"| SharedLib

    style Session fill:#fef3c7

解决方式: shared/lib/* 不再静态 import @/auth,统一通过 session.ts 单一入口;session.ts 内部使用 dynamic import("@/auth") 打破模块级静态循环,运行时调用链保持不变,模块加载图无环。


5. 认证与权限架构

5.1 认证流程

sequenceDiagram
    participant U as 用户
    participant P as proxy.ts<br/>(Edge 中间件)
    participant Auth as src/auth.ts<br/>(NextAuth)
    participant DB as MySQL
    participant SA as Server Action
    participant Ctx as AuthContext

    U->>P: 访问任意路由
    P->>P: getToken() 读 JWT
    alt 未认证
        P-->>U: 重定向 /login
    else 已认证未引导
        P-->>U: 重定向 /onboarding
    else 已认证已引导
        P->>P: hasPermissionInBitmap(token.permissions, routePerm)
        alt 权限不足
            P-->>U: 重定向 /forbidden
        else 权限通过
            P-->>U: 注入 x-request-id,放行
        end
    end

    U->>Auth: 登录(credentials)
    Auth->>DB: 验证密码 + 2FA
    Auth->>DB: 查 roles + permissions
    Auth->>Auth: JWT callback<br/>resolvePermissions(roleNames)<br/>存入 token
    Auth-->>U: Session Cookie

    U->>SA: 调用 Server Action
    SA->>Ctx: requirePermission(PERM)
    Ctx->>Ctx: getSession() 读 session
    Ctx->>Ctx: resolveDataScope(userId, roles)
    Ctx->>DB: 查 classSubjectTeachers/<br/>grades/parentStudentRelations
    Ctx-->>SA: AuthContext{userId, roles,<br/>permissions, dataScope}
    SA->>SA: ctx.permissions.includes(PERM)?
    alt 权限不足
        SA-->>U: PermissionDeniedError
    else 权限通过
        SA->>DB: 执行业务(带 dataScope 过滤)
    end

5.2 权限系统架构

graph TB
    subgraph Definition["权限定义层"]
        Perms["Permissions 常量<br/>shared/types/permissions.ts<br/>61 个权限点<br/>格式: resource:action"]
        Roles["BUILTIN_ROLES<br/>6 个系统角色<br/>admin/teacher/student/<br/>parent/grade_head/teaching_head"]
        RolePerm["ROLE_PERMISSIONS_SEED<br/>角色→权限种子映射"]
    end

    subgraph Runtime["运行时解析层"]
        Bitmap["permission-bitmap.ts<br/>位图压缩(62 权限→8 字节)"]
        Catalog["permission-catalog.ts<br/>权限元数据目录"]
        Resolver["rbac/lib/<br/>data-scope-resolver.ts<br/>配置驱动 DATA_SCOPE_RULES"]
    end

    subgraph Enforcement["权限执行层(3 道防线)"]
        Edge["1. proxy.ts<br/>路由级 bitmap 检查<br/>(Edge Runtime)"]
        Server["2. requirePermission()<br/>Server Action 级断言"]
        Client["3. usePermission()<br/>.hasPermission()<br/>客户端条件渲染"]
    end

    subgraph Scope["数据范围过滤"]
        DataScope["DataScope 类型<br/>all / grade_managed /<br/>class_taught / class_members /<br/>children / owned"]
        Filter["行级过滤<br/>传递到 data-access"]
    end

    subgraph UI["UI 层"]
        Nav["layout/config/navigation.ts<br/>NavItem.permission 过滤侧边栏"]
        I18n["i18n/rbac.json<br/>权限显示名"]
    end

    Definition --> Runtime
    Runtime --> Enforcement
    Enforcement --> Scope
    Scope --> Filter
    Definition --> UI
    Runtime --> UI

5.3 权限点全量分布

资源 权限点 数量
exam create/read/update/delete/duplicate/publish/ai_generate/submit 8
homework create/grade/submit 3
question create/read/update/delete 4
textbook create/read/update/delete 4
class create/read/update/delete/enroll/schedule 6
school manage/grade_manage/user_manage 3
user read/update/delete/invite 4
其他 attendance/messaging/notification/audit/rbac/... 33
合计 61

6. 数据访问与缓存架构

6.1 数据访问分层

graph TB
    subgraph Client["客户端"]
        CC[Client Component]
    end

    subgraph Server["服务端"]
        SC[Server Component]
        SA[Server Action<br/>编排层]
        DA[data-access.ts<br/>DB CRUD]
        Cache[cacheFn<br/>请求级去重]
    end

    subgraph Infra["基础设施"]
        Drizzle[Drizzle ORM<br/>1540 处]
        MySQL[(MySQL 8.0<br/>54 张表)]
        ReactCache[React cache<br/>原生]
    end

    CC -->|"use server"| SA
    SC -->|"直接调用"| DA
    SA -->|"权限+编排"| DA
    DA -->|"包装"| Cache
    Cache -->|"封装"| ReactCache
    DA -->|"查询"| Drizzle
    Drizzle --> MySQL

6.2 缓存策略

层级 技术 用途规模 适用场景
请求级去重 cacheFn (封装 React cache()) 1279 处 同一请求内多次调用同一 data-access 函数
跨请求缓存 不使用 unstable_cache - 权限数据易变,风险高于收益
客户端缓存 TanStack Query 18 处 服务端动态数据,自动重试/乐观更新
URL 状态 nuqs 16 处 可分享/可刷新的筛选/分页状态

6.3 data-access 拆分模式

graph LR
    subgraph Small["小模块(单文件)"]
        Single["data-access.ts<br/>(< 800 行)"]
    end

    subgraph Medium["中模块(按职责拆分)"]
        DA1["data-access.ts<br/>核心 CRUD"]
        DA2["data-access-stats.ts<br/>统计查询"]
        DA3["data-access-graph.ts<br/>图谱查询"]
    end

    subgraph Large["大模块(按领域拆分)"]
        Barrel["index.ts<br/>barrel 导出"]
        D1["data-access-read.ts"]
        D2["data-access-write.ts"]
        D3["data-access-classes.ts"]
        D4["data-access-versions.ts"]
        D5["data-access-..."]
    end

    Small --> Medium --> Large

拆分信号: 单文件超过 800 行建议拆分,超过 1000 行硬性上限必须拆分。


7. 状态管理架构(5 层模型)

graph TB
    subgraph L1["L1 URL 状态"]
        NUQS["nuqs<br/>16 处<br/>可分享/可刷新"]
    end

    subgraph L2["L2 Server 状态"]
        TQ["TanStack Query<br/>18 处<br/>缓存/重试/乐观更新"]
    end

    subgraph L3["L3 Client Business 状态"]
        Zustand["Zustand slice<br/>22 处<br/>模块业务状态"]
    end

    subgraph L4["L4 Global UI 状态"]
        UIStore["Zustand ui-store<br/>全局 UI 状态"]
        Modal["ModalRoot<br/>命令式 registry"]
    end

    subgraph L5["L5 Form 状态"]
        RHF["react-hook-form<br/>32 处"]
        Zod["zodResolver<br/>Schema 验证"]
    end

    subgraph Optimistic["乐观更新层"]
        Opt["useOptimistic<br/>13 处<br/>+ useTransition"]
    end

    L1 --> L2 --> L3 --> L4 --> L5
    L3 -.->|"配合"| Optimistic
    L2 -.->|"配合"| Optimistic

7.1 状态层选用规则

场景 选用层 理由
筛选/分页/排序(可分享) L1 nuqs URL 即状态,刷新可恢复
服务端数据(列表/详情) L2 TanStack Query 自动缓存/重试/失效
复杂客户端业务(编辑器/画布) L3 Zustand slice 模块隔离,可拆分
全局弹窗/抽屉/主题 L4 Zustand ui-store 跨模块共享
表单(含验证) L5 react-hook-form 性能 + 验证一体化
即时反馈(星标/提交) useOptimistic React 19 原生,自动回滚

7.2 乐观更新适用场景

场景 是否乐观更新 理由
消息星标 纯 UI 状态,失败可回滚
成绩编辑 用户期望即时反馈
考勤提交 form action 模式,useFormStatus 依赖 promise
消息撤回 需服务端校验 2 分钟窗口
创建/删除 需服务端返回 ID/校验依赖

8. 跨模块协作机制

8.1 跨模块调用规则

graph LR
    subgraph ModuleA["模块 A"]
        ActionA[actions.ts]
        DAA[data-access.ts]
    end

    subgraph ModuleB["模块 B"]
        DAB[data-access.ts]
        DBB[(DB 表)]
    end

    ActionA -->|"✓ 允许:调用 B 的 data-access"| DAB
    DAA -->|"✓ 允许:调用 B 的 data-access"| DAB
    DAA -.->|"✗ 禁止:直查 B 的 DB 表"| DBB
    DAB --> DBB

8.2 聚合层模式(dashboard/parent)

sequenceDiagram
    participant Page as 聚合页<br/>(dashboard/parent)
    participant DA as 聚合 data-access
    participant M1 as 模块1 data-access
    participant M2 as 模块2 data-access
    participant M3 as 模块3 data-access
    participant DB as MySQL

    Page->>DA: getDashboardData(userId)
    DA->>DA: Promise.allSettled([</br>getM1Data(userId),</br>getM2Data(userId),</br>getM3Data(userId)</br>])
    par 并行查询
        DA->>M1: getM1Data
        M1->>DB: SELECT ...
        M1-->>DA: M1Result
    and
        DA->>M2: getM2Data
        M2->>DB: SELECT ...
        M2-->>DA: M2Result
    and
        DA->>M3: getM3Data
        M3->>DB: SELECT ...
        M3-->>DA: M3Result
    end
    DA->>DA: 容错处理(allSettled)
    DA-->>Page: { m1, m2, m3 }

关键约束:

  • 聚合层必须用 Promise.allSettled 而非 Promise.all,避免单模块失败导致整页白屏
  • 聚合 data-access 不直查业务表,只调用各模块 data-access
  • 聚合页优先用 Server Component,除非需要 useMemo 等客户端 Hook

8.3 事件驱动闭环(批改后联动)

flowchart TB
    A[教师批改作业] --> B[gradeHomeworkSubmission]
    B --> C{批改完成?}
    C -->|是| D[Promise.allSettled 并行闭环]
    D --> E1[error-book<br/>采集错题]
    D --> E2[diagnostic<br/>更新知识点掌握度]
    D --> E3[notifications<br/>通知学生]
    D --> E4[grades<br/>更新成绩统计]
    E1 --> F[闭环完成]
    E2 --> F
    E3 --> F
    E4 --> F

9. 核心业务流程

9.1 考试全生命周期(跨 4 模块)

sequenceDiagram
    autonumber
    participant T as 教师
    participant S as 学生
    participant Exams as exams 模块
    participant HW as homework 模块
    participant Grades as grades 模块
    participant Diag as diagnostic 模块
    participant DB as MySQL

    rect rgb(240, 249, 255)
    Note over T,Exams: 阶段 1: 教师创建考试
    T->>Exams: createExamAction
    Exams->>Exams: requirePermission(EXAM_CREATE)
    Exams->>DB: INSERT exams + examQuestions
    Exams->>T: ActionState{examId}
    end

    rect rgb(254, 249, 195)
    Note over T,HW: 阶段 2: 作业化下发
    T->>HW: createHomeworkAssignmentAction(sourceExamId)
    HW->>HW: requirePermission(HOMEWORK_CREATE)
    HW->>DB: INSERT homeworkAssignments(sourceExamId)
    end

    rect rgb(240, 253, 244)
    Note over S,HW: 阶段 3: 学生作答
    S->>HW: startHomeworkSubmissionAction
    HW->>DB: INSERT homeworkSubmissions
    S->>HW: saveHomeworkAnswerAction (多次)
    HW->>DB: INSERT homeworkAnswers
    S->>HW: submitHomeworkAction
    HW->>DB: UPDATE status=submitted
    end

    rect rgb(253, 235, 214)
    Note over T,HW: 阶段 4: 教师批改
    T->>HW: gradeHomeworkSubmissionAction
    HW->>HW: requirePermission(HOMEWORK_GRADE)
    HW->>DB: UPDATE homeworkAnswers(score, isCorrect)
    HW->>HW: Promise.allSettled 并行闭环
    par 闭环
        HW->>Diag: updateMasteryFromSubmission
        Diag->>DB: UPDATE knowledgePointMastery
    and
        HW->>Grades: 更新成绩统计
        Grades->>DB: UPDATE/INSERT gradeRecords
    end
    end

9.2 备课→发布作业流程

flowchart LR
    A[教师创建教案] --> B[lesson-preparation<br/>Tiptap + 节点树编辑]
    B --> C{选择发布}
    C --> D[publish-service<br/>处理教案]
    D --> E{发布类型}
    E -->|作业| F[homework<br/>createHomeworkAssignment]
    E -->|仅教案| G[存档供查阅]
    F --> H[学生收到作业通知]

9.3 AI 辅助流程(SSE 流式)

sequenceDiagram
    participant C as Client
    participant API as /api/ai/chat/stream
    participant AIService as ai/createAiService
    participant Provider as AI Provider
    participant DB as MySQL

    C->>API: POST messages[]
    API->>API: 鉴权 + 限流
    API->>AIService: createAiChatCompletion(stream)
    AIService->>Provider: stream chat completion
    loop 流式响应
        Provider-->>AIService: chunk
        AIService-->>API: chunk
        API-->>C: SSE event: data chunk
    end
    Provider-->>AIService: done
    AIService->>DB: 记录 usage + cost
    API-->>C: SSE event: [DONE]

10. 设计令牌体系

项目采用双层令牌架构(Primitive + Semantic),Tailwind v4 通过 @theme inline 暴露为业务可用的类。

10.1 令牌分层架构

graph TB
    subgraph L1["Layer 1: Primitive (原始)"]
        Prim["primitive.css<br/>原始色板(zinc/stone/indigo)<br/>字号/间距/阴影/字体家族<br/>业务代码不直接引用"]
    end

    subgraph L2["Layer 2: Semantic (语义)"]
        Light["semantic-light.css<br/>:root 语义令牌"]
        Dark["semantic-dark.css<br/>.dark 语义令牌"]
    end

    subgraph L3["Layer 3: 模块命名空间"]
        LP["lesson-preparation.css<br/>--lp-* 令牌<br/>(明暗双份)"]
    end

    subgraph L4["Layer 4: Tailwind 暴露"]
        Theme["tailwind-theme.css<br/>@theme inline<br/>暴露为 bg-*/text-*/font-*"]
    end

    subgraph Business["业务代码"]
        Code[TSX/TS<br/>唯一引用入口]
    end

    Prim --> Light
    Prim --> Dark
    Light --> Theme
    Dark --> Theme
    LP --> Theme
    Theme --> Code

10.2 令牌分类

分类 命名 Tailwind 类
shadcn 标准 --background/--foreground/--primary bg-background/text-foreground/bg-primary
扩展层级 --background-elevated/--text-secondary/--border-strong bg-background-elevated/text-text-secondary
chart --chart-1~5 bg-chart-1
sidebar --sidebar-* bg-sidebar
模块命名空间 --lp-paper/--lp-inline-node-text bg-lp-paper/text-lp-inline-node-text
字体家族 --font-family-sans/serif/mono font-sans/font-serif/font-mono
字号阶梯 --font-size-1~9 text-size-1~text-size-9
间距阶梯 --space-0~18 w-[length:var(--space-N)]
阴影阶梯 --shadow-1~6 shadow-1~shadow-6

10.3 强制约束

  • ESLint no-restricted-syntax: 禁止 #hex 字面量
  • 自定义规则 design-tokens/no-hardcoded-fonts: 禁止 'Inter'/'Fraunces'/'JetBrains Mono' 字面量
  • 白名单: primitive.css(令牌定义)、email-channel.ts(邮件 HTML)、manifest.ts(PWA)
  • 任意值 w-[Npx]// eslint-disable-next-line no-restricted-syntax -- <reason> 注释豁免

11. API 与实时通信架构

11.1 API 路由清单(17 个)

graph LR
    subgraph Auth["认证 (2)"]
        A1[/api/auth/[...nextauth]/]
        A2[/api/onboarding/status,<br/>/api/onboarding/complete]
    end

    subgraph AI["AI (2)"]
        AI1[/api/ai/chat<br/>同步]
        AI2[/api/ai/chat/stream<br/>SSE 流式]
    end

    subgraph Files["文件 (3)"]
        F1[/api/upload]
        F2[/api/files/[id]]
        F3[/api/files/batch-delete]
        F4[/api/export]
    end

    subgraph Other["其他 (5)"]
        O1[/api/search]
        O2[/api/import]
        O3[/api/notifications/stream<br/>SSE]
        O4[/api/proctoring/event]
        O5[/api/cron/audit-cleanup]
    end

    subgraph Monitor["监控 (2)"]
        M1[/api/client-error]
        M2[/api/web-vitals]
    end

11.2 响应信封规范

所有 API 统一采用 { success, message?, errorCode?, data? } 信封,与 ActionState<T> 对齐:

graph LR
    subgraph ErrorMap["错误→状态码映射"]
        E1[PermissionDeniedError] -->|403| S1[Forbidden]
        E2[NotFoundError] -->|404| S2[Not Found]
        E3[ValidationError] -->|400| S3[Bad Request]
        E4[BusinessError] -->|400| S3
        E5[其他] -->|500| S4[Internal Server Error]
    end

    subgraph Utils["工具函数"]
        HOF[withApiErrorHandler<br/>HOF 消除重复 try/catch]
        FromAction[apiFromAction<br/>errorCode 驱动状态码]
    end

11.3 SSE 实时通信

场景 端点
AI 对话流 /api/ai/chat/stream Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
Connection: keep-alive
通知推送 /api/notifications/stream 同上
错误格式 - { type: "error", message }
结束信号 - [DONE]

12. 架构约束与设计原则

12.1 硬性约束(违反即违规)

约束 检测方式 修复方式
严格三层架构,依赖单向 npm run arch:query -- violations 调整 import 路径
modules 间不直查对方 DB 表 arch:query violations 改调对方 data-access
shared 不反向依赖 auth/proxy/modules/* arch:query violations 用 dynamic import 打破循环
Server Action 必须 requirePermission() arch:query violations 补充权限校验
禁止 any/as(除 unknown→类型守卫) npx tsc --noEmit + ESLint 用类型守卫替代
禁止硬编码颜色/字体/字号 ESLint no-restricted-syntax 用设计令牌
单文件 ≤ 1000 行 arch:query violations 按职责拆分
禁止 dangerouslySetInnerHTML(未清洗) Code review 先 DOMPurify 清洗

12.2 性能原则

原则 实践
批量 SQL 优于循环 SQL INSERT db.insert().values([...]);UPDATE sql.join + CASE WHEN
递归删除批量收集 收集后代 ID + inArray 单次删除,禁止递归逐个
动态导入重 UI 库 xxx-inner.tsx + xxx.tsx lazy wrapper(tiptap/@xyflow/react/AI SDK)
请求级去重 cacheFn 包装 data-access 函数
细粒度 Zustand selector 单字段 selector 优于 useShallow
React 19 useOptimistic 替代手动 isPending 状态
FULLTEXT 索引 大表 MATCH AGAINST BOOLEAN MODE,小表 LIKE
内存 filter 改 SQL inArray(field, ids) 下推
统计走 SQL 聚合 COUNT(*)/SUM(CASE WHEN) 替代拉全表循环
chart props 模块级常量 12 个 recharts 图表提取 CHART_MARGIN

12.3 安全原则

原则 实现
JWT/session 存储 httpOnly + Secure + SameSite=Strict Cookie
环境变量校验 @t3-oss/env-nextjs + Zod(src/env.mjs)
服务端变量 不加 NEXT_PUBLIC_ 前缀
权限 3 道防线 proxy.ts(路由) → requirePermission(Action) → usePermission(客户端)
数据范围过滤 DataScope 行级过滤(all/grade_managed/class_taught/children/owned)
parent 双重校验 parentId + studentId 防跨家庭信息泄露
破坏性操作独立权限 AUDIT_LOG_PURGEAUDIT_LOG_READ
密码策略 validatePassword() + isAccountLocked() + rateLimit()
2FA TOTP(自研) + 备用码

13. 架构决策记录

13.1 关键决策

决策 选项 选择 理由
全栈框架 Next.js vs Remix vs 自研 Next.js 16 RSC + Server Actions + Edge middleware + 生态
ORM Drizzle vs Prisma vs TypeORM Drizzle 类型安全 + 轻量 + 迁移可控 + 无运行时开销
状态管理 Redux vs Zustand vs Jotai Zustand(5 层模型) 模块化 slice + 细粒度 selector + 无样板
缓存策略 unstable_cache vs cacheFn cacheFn(请求级) 权限数据易变,跨请求缓存风险高于收益
富文本 Tiptap vs Slate vs Lexical Tiptap 扩展生态 + SSR 友好 + 节点树支持
知识图谱 ReactFlow vs dagre vs 自定义 自定义 force-graph + graph-layout K12 前置依赖需力导向 + 层级布局,通用库不满足
认证 NextAuth vs Lucia vs 自研 NextAuth 4 JWT + Credentials + 2FA + 生态
中间件 middleware.ts vs proxy.ts proxy.ts (Next.js 16) Edge Runtime + 重命名规范
模块通信 事件总线 vs 直接调用 vs data-access data-access 调用 显式依赖 + 类型安全 + 易追踪
聚合层数据获取 Promise.all vs Promise.allSettled allSettled 单模块失败不导致整页白屏
乐观更新 手动 isPending vs useOptimistic useOptimistic (React 19) 原生 + 自动回滚 + useTransition 配合

13.2 架构演进历程

阶段 时间 变更
初始架构 2026-06 Next.js 16 + 三层架构 + 35 模块
V2 状态重构 2026-07-05 5 层状态模型 + notify 替换 + ModalRoot 命令式 registry
V2 API 规范化 2026-07-04 响应信封 + 错误→状态码 + SSE 工具 + withApiErrorHandler
V2 设计令牌 2026-07-04 双层令牌(Primitive + Semantic) + ESLint 强制约束
V2 权限重构 2026-07-07 as 断言违规修复 + 11 个类型守卫 + BLOCK_DATA_GUARDS registry
V3 文档体系 2026-07-07 arch.db 扫描器 + 004 瘦身 + 35 模块 README + known-issues 索引化

14. 附录

14.1 关键参数影响链

userId

graph LR
    A[auth.ts<br/>JWT callback] -->|"查询 users 表"| B[token.userId]
    B -->|"session.user.id"| C[Server/Client Components]
    B -->|"getAuthContext().userId"| D[Server Actions]
    D -->|"creatorId"| E[exams/homework 表写入]
    D -->|"teacherId"| F[getTeacherClasses<br/>getGradeManagedClasses]
    D -->|"resolveDataScope"| G[DataScope 计算]

permission

graph LR
    A[Permissions 常量<br/>61 个权限点] --> B[ROLE_PERMISSIONS_SEED<br/>角色→权限种子]
    B --> C[permission-bitmap.ts<br/>位图压缩]
    C --> D[auth.ts JWT callback<br/>resolvePermissions 合并多角色]
    D --> E[proxy.ts<br/>路由级 bitmap 检查]
    D --> F[requirePermission<br/>Server Action 断言]
    D --> G[usePermission<br/>.hasPermission 客户端]
    D --> H[NavItem.permission<br/>侧边栏过滤]

DataScope

类型 适用角色 数据范围
all admin 全部数据
grade_managed grade_head 管辖年级的数据
class_taught teacher 所教班级的数据
class_members student 所在班级的数据
children parent 关联孩子的数据
owned 所有角色 自己创建的数据

14.2 arch.db 查询入口

# 全量扫描(修改源码后须重新执行)
npm run arch:scan

# 自定义 SQL 查询
npm run arch:query -- sql "<SQL>"

# 查询模块依赖
npm run arch:query -- module <module-name>

# 查询符号引用链
npm run arch:query -- ref <symbol-name>

# 查询技术使用
npm run arch:query -- tech <tag-name>

# 架构违规检测
npm run arch:query -- violations

14.3 相关文档

文档 用途
各模块 README.md 模块级架构图与流程图(35 个)
coding-standards.md 编码规范
known-issues.md 已知问题速查(场景→技术映射)
roadmap/README.md 长远规划入口
roadmap/tech-debt.md 技术债清单
roadmap/decoupling.md 解耦路线图
audit/README.md 架构审查报告入口
audit/archive/ 历史审查报告归档(含 004 V1/V2、005 JSON)