Module Update
Some checks failed
CI / build-and-test (push) Failing after 1m31s
CI / deploy (push) Has been skipped

This commit is contained in:
SpecialX
2025-12-30 14:42:30 +08:00
parent f1797265b2
commit e7c902e8e1
148 changed files with 19317 additions and 113 deletions

332
ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,332 @@
# Next_Edu Architecture RFC
**Status**: PROPOSED
**Date**: 2025-12-22
**Author**: Principal Software Architect
**Version**: 1.0.0
---
## 1. 核心原则 (Core Principles)
本架构设计遵循以下核心原则,旨在构建一个高性能、可扩展、易维护的企业级在线教育平台。
1. **Vertical Slice Architecture (垂直切片)**: 拒绝传统的按技术分层Layered Architecture采用按业务功能分层。代码应根据“它属于哪个功能”而不是“它是什么文件”来组织。
2. **Type Safety First (类型安全优先)**: 全链路 TypeScript (Strict Mode)。从数据库 Schema 到 API 再到 UI 组件,必须保持类型一致性。
3. **Server-First (服务端优先)**: 充分利用 Next.js 15 App Router 的 RSC (React Server Components) 能力,减少客户端 Bundle 体积。
4. **Performance by Default (默认高性能)**: 严禁引入重型动画库,动效优先使用 CSS Native 实现。Web Vitals 指标作为 CI 阻断标准。
5. **Strict Engineering (严格工程化)**: CI/CD 流程标准化,代码风格统一,自动化测试覆盖。
---
## 2. 技术栈全景图 (Technology Panorama)
### 核心框架
* **Framework**: Next.js 15 (App Router)
* **Language**: TypeScript 5.x (Strict Mode enabled)
* *Correction*: 鉴于 Next.js App Router 的特性,`.tsx` 仅用于包含 JSX 的组件文件。所有的业务逻辑、Hooks、API 路由、Lib 工具函数必须使用 `.ts` 后缀,以明确区分“渲染层”与“逻辑层”。
* **Runtime**: Node.js 20.x (LTS)
### 数据层
* **Database**: MySQL 8.0+
* **ORM**: Drizzle ORM (轻量、无运行时开销、类型安全)
* **Driver**: `mysql2` (配合连接池) 或 `serverless-mysql` (针对特定 Serverless 环境)
* **Validation**: Zod (Schema 定义与运行时验证)
### UI/UX 层
* **Styling**: Tailwind CSS v3.4+
* **Components**: Shadcn/UI (基于 Radix UI 的 Headless 组件拷贝)
* **Icons**: Lucide React
* **Animations**: CSS Transitions / Tailwind `animate-*` / `tailwindcss-animate`
* *Complex Interactions*: Framer Motion (仅限按需加载 `LazyMotion`)
### 身份验证与授权
* **Auth**: Auth.js v5 (NextAuth)
* *Decision Driver*: 相比 ClerkAuth.js 提供了完全的**数据所有权**和**无 Vendor Lock-in**。
* *Enterprise Needs*: 允许自定义 Session 结构(如注入 `Role` 字段)并直接对接现有 MySQL 数据库,满足复杂的企业级权限管理需求。
### 状态管理
* **Server State**: TanStack Query v5 (仅用于复杂客户端轮询/无限加载)
* **URL State (Primary)**: Nuqs (Type-safe search params state manager)
* *Principle*: 绝大多数状态筛选、分页、Tab应存在 URL 中,以支持分享和书签。
* **Global Client State (Secondary)**: Zustand
* *Usage*: 仅限极少数全局交互状态(如播放器悬浮窗、全局 Modal
* *Anti-pattern*: **严禁使用 Redux**。避免不必要的样板代码和 Bundle 体积。
### 基础设施 & DevOps
* **CI/CD**: GitHub Actions (Strictly v3)
* **Linting**: ESLint (Next.js config), Prettier
* **Package Manager**: pnpm (推荐) 或 npm
---
## 3. 项目目录结构规范 (Project Structure)
采用 **Feature-based / Vertical Slice** 架构。所有业务逻辑应封装在 `src/modules` 中。
文档存放位置:
* 架构设计文档: `docs/architecture/`
* API 规范文档: `docs/api/`
### 目录树 (Directory Tree)
```
Next_Edu/
├── .github/
│ └── workflows/
│ └── ci.yml # GitHub Actions (v3 strict)
├── docs/
│ └── architecture/ # 架构决策记录 (ADR)
├── drizzle/ # 数据库迁移文件 (Generated)
├── public/ # 静态资源
├── src/
│ ├── app/ # [路由层] 极薄,仅负责路由分发和布局
│ │ ├── (auth)/ # 路由组
│ │ ├── (dashboard)/
│ │ ├── api/ # Webhooks / External APIs
│ │ ├── layout.tsx
│ │ └── page.tsx
│ │
│ ├── modules/ # [核心业务层] 垂直切片
│ │ ├── courses/ # 课程模块
│ │ │ ├── components/ # 模块私有组件 (CourseCard, Player)
│ │ │ ├── actions.ts # Server Actions (业务逻辑入口)
│ │ │ ├── service.ts # 领域服务 (可选,复杂逻辑拆分)
│ │ │ ├── data-access.ts # 数据库查询 (DTOs)
│ │ │ └── types.ts # 模块私有类型
│ │ │
│ │ ├── users/ # 用户模块
│ │ ├── payments/ # 支付模块
│ │ └── community/ # 社区模块
│ │
│ ├── shared/ # [共享层] 仅存放真正通用的代码
│ │ ├── components/ # 通用 UI (Button, Dialog - Shadcn)
│ │ ├── lib/ # 通用工具 (utils, date formatting)
│ │ ├── db/ # Drizzle Client & Schema
│ │ │ ├── index.ts # DB 连接实例
│ │ │ └── schema.ts # 全局 Schema 定义 (或按模块拆分导出)
│ │ └── hooks/ # 通用 Hooks
│ │
│ ├── env.mjs # 环境变量类型检查
│ └── middleware.ts # 边缘中间件 (Auth check)
├── drizzle.config.ts # Drizzle 配置文件
├── next.config.mjs
└── package.json
```
---
## 4. 数据库层设计 (Database Strategy)
### 连接配置 (Connection Pooling)
在 Next.js 的 Serverless/Edge 环境中,直接连接 MySQL 可能导致连接数耗尽。我们采取以下策略:
1. **开发环境**: 使用 Global Singleton 模式防止 Hot Reload 导致连接泄露。
2. **生产环境**:
* 推荐使用支持 HTTP 连接或内置连接池的 Serverless MySQL 方案 (如 PlanetScale)。
* 若使用标准 MySQL必须配置连接池 (`connectionLimit`) 并合理设置空闲超时。
**代码示例 (`src/shared/db/index.ts`)**:
```typescript
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
import * as schema from "./schema";
// Global cache to prevent connection exhaustion in development
const globalForDb = globalThis as unknown as {
conn: mysql.Pool | undefined;
};
const poolConnection = globalForDb.conn ?? mysql.createPool({
uri: process.env.DATABASE_URL,
waitForConnections: true,
connectionLimit: 10, // 根据数据库规格调整
queueLimit: 0
});
if (process.env.NODE_ENV !== "production") globalForDb.conn = poolConnection;
export const db = drizzle(poolConnection, { schema, mode: "default" });
```
### Migration 策略
* 使用 `drizzle-kit` 进行迁移管理。
* 严禁在生产环境运行时自动执行 Migration。
* **流程**:
1. 修改 Schema (`schema.ts`).
2. 运行 `pnpm drizzle-kit generate` 生成 SQL 文件。
3. Review SQL 文件。
4. 在 CI/CD 部署前置步骤或手动运行 `pnpm drizzle-kit migrate`
### Server Components 中的数据查询
* **Colocation**: 查询逻辑应尽量靠近使用它的组件,或者封装在 `data-access.ts` 中。
* **Request Memoization**: 即使在一个请求中多次调用相同的查询函数Next.js 的 `cache` (或 React `cache`) 也会自动去重。
```typescript
// src/modules/courses/data-access.ts
import { cache } from 'react';
import { db } from '@/shared/db';
import { eq } from 'drizzle-orm';
import { courses } from '@/shared/db/schema';
// 使用 React cache 确保单次请求内的去重
export const getCourseById = cache(async (id: string) => {
return await db.query.courses.findFirst({
where: eq(courses.id, id),
});
});
```
---
## 5. UI/UX 动效规范 (Animation Guidelines)
### 核心策略
* **CSS Native First**: 90% 的交互通过 CSS `transition``animation` 实现。
* **Hardware Acceleration**: 确保动画属性触发 GPU 加速 (`transform`, `opacity`)。
* **Micro-interactions**: 关注 `:hover`, `:active`, `:focus-visible` 状态。
### 高性能通用组件示例 (Interactive Card)
这是一个符合规范的卡片组件,使用了 Tailwind 的 `group``transform` 属性实现丝滑的微交互,且没有 JS 运行时开销。
```tsx
// src/shared/components/ui/interactive-card.tsx
import { cn } from "@/shared/lib/utils";
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
export function InteractiveCard({ className, children, ...props }: CardProps) {
return (
<div
className={cn(
"group relative overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-sm",
// 核心动效:
// 1. duration-300 ease-out: 丝滑的时间函数
// 2. hover:shadow-md: 悬浮提升感
// 3. hover:-translate-y-1: 物理反馈
"transition-all duration-300 ease-out hover:-translate-y-1 hover:shadow-md",
// 消除 Safari 上的闪烁
"transform-gpu backface-hidden",
className
)}
{...props}
>
{/* 光泽效果 (Shimmer Effect) - 仅 CSS */}
<div
className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/5 to-transparent transition-transform duration-700 group-hover:translate-x-full"
aria-hidden="true"
/>
<div className="relative p-6">
{children}
</div>
</div>
);
}
```
---
## 6. CI/CD 配置文件模板 (GitHub Actions)
**警告**: 必须严格遵守 `v3` 版本限制。严禁使用 `v4`
文件路径: `.github/workflows/ci.yml`
```yaml
name: CI/CD Pipeline
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main", "develop" ]
env:
NODE_VERSION: '20.x'
jobs:
quality-check:
name: Quality & Type Check
runs-on: ubuntu-latest
steps:
# 强制使用 v3
- name: Checkout Code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm' # 或 'pnpm'
- name: Install Dependencies
run: npm ci
- name: Linting (ESLint)
run: npm run lint
- name: Type Checking (TSC)
# 确保没有 TS 错误
run: npx tsc --noEmit
test:
name: Unit Tests
needs: quality-check
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm run test
build-check:
name: Production Build Check
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Cache Next.js build
uses: actions/cache@v3
with:
path: |
~/.npm
${{ github.workspace }}/.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- name: Build Application
run: npm run build
env:
# 构建时跳过 ESLint/TS 检查 (因为已经在 quality-check job 做过了,加速构建)
NEXT_TELEMETRY_DISABLED: 1
```