# Next_Edu 编码规范

> 版本：1.0（2026-06-17 适配当前项目）
> 依据：Google TypeScript Style + Airbnb React + Next.js 16 + Tailwind v4 最佳实践
> 适用范围：Next_Edu K12 智慧教务系统（单应用 + 模块化架构）
> 关联文档：
> - [项目规则](../../.trae/rules/project_rules.md)
> - [架构影响地图](../architecture/004_architecture_impact_map.md)
> - [解耦路线图](../architecture/audit/01_decoupling_roadmap.md)

---

## 目录

1. [项目原则与理念](#一项目原则与理念)
2. [项目结构](#二项目结构)
3. [命名规范](#三命名规范)
4. [TypeScript 强制规范](#四typescript-强制规范)
5. [React 与 Next.js 组件规范](#五react-与-nextjs-组件规范)
6. [Tailwind CSS 规范](#六tailwind-css-规范)
7. [数据获取与状态管理](#七数据获取与状态管理)
8. [路由、代理与安全](#八路由代理与安全)
9. [错误处理与可观测性](#九错误处理与可观测性)
10. [测试规范](#十测试规范)
11. [Git 工作流与提交规范](#十一git-工作流与提交规范)
12. [CI/CD 流水线](#十二cicd-流水线)
13. [可访问性（A11y）规范](#十三可访问性a11y规范)
14. [文档与交付物](#十四文档与交付物)
15. [统一工具配置](#十五统一工具配置)
16. [代码审查清单](#十六代码审查清单)

---

## 一、项目原则与理念

1. **可读性优先于机巧**：代码首先是写给队友看的
2. **显式优于隐式**：避免魔法值、隐式类型转换、隐式全局副作用
3. **单一职责**：每个文件、函数、组件只做一件事，衡量标准是"能否用一句话描述它"
4. **防御性编程**：永远假设输入可能是 null/undefined 或非法格式
5. **工具强制一致性**：风格、格式、类型由 ESLint、Prettier、TypeScript 自动保证
6. **架构图优先**：任何任务开始前先查阅 [004 架构影响地图](../architecture/004_architecture_impact_map.md)，按图索骥
7. **模块封装**：模块间不直接查询对方 DB 表，必须通过 data-access 函数

---

## 二、项目结构

### 2.1 顶层结构（单应用 + 模块化）

本项目**不是 Monorepo**，采用单 Next.js 应用 + 严格模块化架构：

```
root/
├─ src/
│  ├─ app/                    # App Router 路由层
│  │  ├─ (auth)/              # 路由组：认证页面
│  │  ├─ (dashboard)/         # 路由组：业务页面（admin/teacher/student/parent）
│  │  ├─ api/                 # REST API 路由
│  │  ├─ globals.css          # Tailwind v4 指令 + CSS 变量设计令牌
│  │  ├─ layout.tsx           # 根布局（Provider 组合）
│  │  └─ page.tsx             # 首页
│  ├─ modules/                # 业务模块层（26 个模块）
│  │  ├─ exams/               # 每个模块标准结构见 2.2
│  │  ├─ homework/
│  │  ├─ classes/
│  │  └─ ...
│  ├─ shared/                 # 基础设施层（被依赖方，不反向依赖）
│  │  ├─ components/          # 共享组件（ui/ + a11y/ + 顶层）
│  │  ├─ db/                  # Drizzle ORM（schema.ts + relations.ts + index.ts）
│  │  ├─ hooks/               # 全局自定义 Hook
│  │  ├─ lib/                 # 纯工具函数（auth-guard, ai, permissions, ...）
│  │  └─ types/               # 公共类型定义
│  ├─ auth.ts                 # NextAuth 配置（根模块）
│  ├─ proxy.ts                # Next.js 16 代理（原 middleware.ts）
│  └─ env.mjs                 # 环境变量校验（@t3-oss/env-nextjs + Zod）
├─ tests/                     # 测试目录
│  ├─ e2e/                    # Playwright E2E 测试
│  ├─ integration/            # 集成测试
│  ├─ visual/                 # 视觉回归测试
│  └─ setup/                  # 测试 setup
├─ scripts/                   # 运维脚本（db/backup/security/dr）
├─ docs/                      # 文档（架构/审查/专题/设计）
├─ drizzle/                   # 数据库迁移
└─ .gitea/workflows/          # CI/CD 流水线
```

### 2.2 模块标准结构

每个业务模块遵循**统一结构**，职责分离：

```
src/modules/[module]/
├─ actions.ts              # Server Actions（编排层：权限校验 + 调用 data-access + revalidate）
├─ data-access.ts          # 数据访问层（DB CRUD，仅服务端，可拆分为多个 data-access-*.ts）
├─ schema.ts               # Zod 验证 schema（可选，按需）
├─ types.ts                # 模块类型定义
├─ components/             # 模块专属组件
│  └─ [feature]/           # 复杂功能可分子目录
└─ hooks/                  # 模块专属 Hook（可选）
```

**分层规则**（严格单向依赖）：

```
app/  ──▶  modules/  ──▶  shared/
                              ▲
                              │
                       禁止反向依赖
```

- `app/` 只能调用 `modules/` 的 Server Actions 和 data-access 函数，**不直接访问 DB**
- `modules/` 之间通过对方 data-access 通信，**不直接查询对方 DB 表**
- `shared/` 是被依赖方，**不得反向依赖** `@/auth`、`@/proxy` 或任何 `modules/*`
- `src/auth.ts` 和 `src/proxy.ts` 位于根目录，属于应用层

**文件拆分规则**（当 data-access 过大时）：

```
src/modules/classes/
├─ data-access.ts              # 核心 CRUD
├─ data-access-stats.ts        # 统计查询
├─ data-access-schedule.ts     # 课表查询
└─ data-access-grades.ts       # 成绩汇总
```

### 2.3 路由组织

- 使用路由组 `(group)` 组织相同布局的页面，不影响 URL
- 动态路由 `[slug]`、捕获所有 `[...slug]`、可选捕获 `[[...slug]]` 按需使用
- 路由命名一律使用**小写与连字符**（`user-profile`）
- 每个路由段应提供 `loading.tsx`（骨架屏）和 `error.tsx`（错误边界）

### 2.4 核心原则

- 页面组件（`page.tsx`）必须使用**默认导出**；布局、加载、错误使用**具名导出**
- 每个路由段都必须提供 `error.tsx`，不得出现未捕获异常导致白屏
- `loading.tsx` 必须提供骨架屏或最小可感知的加载状态，**不得使用全局 spin 遮罩**
- 服务端数据获取通过模块的 `data-access.ts`，标记 `import "server-only"` 以防客户端误用

---

## 三、命名规范

### 3.1 文件与目录

| 对象 | 命名风格 | 示例 |
|------|---------|------|
| 目录 | kebab-case | `user-profile/`, `class-detail/` |
| 组件文件 | PascalCase | `UserProfile.tsx`, `ExamForm.tsx` |
| Hook 文件 | camelCase | `useAuth.ts`, `useExamPreview.ts` |
| 工具函数文件 | camelCase | `formatCurrency.ts`, `auditLogger.ts` |
| 类型定义文件 | camelCase | `user.ts`, `permissions.ts` |
| 测试文件 | `*.test.ts` / `*.spec.ts` | `utils.test.ts`, `auth.spec.ts` |
| Server Action | `actions.ts` 或 `xxx-actions.ts` | `actions.ts`, `actions-analytics.ts` |
| Data Access | `data-access.ts` 或 `data-access-*.ts` | `data-access.ts`, `data-access-stats.ts` |
| 代理（Next.js 16） | `proxy.ts` | `src/proxy.ts` |
| 常量文件 | camelCase | `navigation.ts`, `permissions.ts` |
| 环境变量 | UPPER_SNAKE_CASE | `DATABASE_URL`, `NEXTAUTH_SECRET` |

### 3.2 变量、函数、类

- **变量**：camelCase。布尔值用 `is/has/can/should` 前缀（`isVisible`, `hasError`）
- **函数**：camelCase，动词开头（`fetchUser`, `handleSubmit`, `validateForm`）
- **常量**：UPPER_SNAKE_CASE（`MAX_RETRY_COUNT`, `API_BASE_URL`）
- **类与接口**：PascalCase。接口**不加** `I` 前缀（Google 风格）
- **类型别名**：PascalCase，如 `type UserId = string`
- **泛型参数**：使用描述性名称，如 `TData`, `TResponse`（避免单字母 `T`）
- **枚举**：推荐联合类型 + 字符串字面量（tree-shaking 友好）。如必须用枚举，成员名 PascalCase

### 3.3 组件与 Props

- 组件名必须为**多词**（`UserProfile` 而非 `Profile`），以免与 HTML 元素冲突
- Props 类型命名为 `组件名Props`（`UserProfileProps`），定义在组件文件顶部
- 事件回调 Props 使用 `on` 前缀（`onSave`, `onClose`）
- `children` 必须显式声明类型 `React.ReactNode`
- **不使用 `React.FC`**，直接用函数声明 + 显式标注 props 类型（Google 风格）

```tsx
// 推荐方式
interface UserCardProps {
  user: User;
  onSelect: (id: string) => void;
  children?: React.ReactNode;
}

export function UserCard({ user, onSelect, children }: UserCardProps): JSX.Element {
  // ...
}
```

---

## 四、TypeScript 强制规范

### 4.1 配置（tsconfig.json）

当前项目配置需升级以符合规范。**目标配置**：

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["dom", "dom.iterable", "ES2022"],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "jsx": "react-jsx",
    "incremental": true,
    "noEmit": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
```

**当前差异**（需逐步升级）：
- `target`: `ES2017` → `ES2022`
- 缺失 `noUncheckedIndexedAccess`（数组/对象索引返回 `T | undefined`）
- 缺失 `noImplicitReturns`（函数所有分支必须返回）
- 缺失 `noFallthroughCasesInSwitch`

### 4.2 类型规则

1. **禁止 `any`**：未知类型用 `unknown` 并做类型守卫。若极特殊情况必须使用，需 `// eslint-disable-next-line @typescript-eslint/no-explicit-any` 并注释原因

2. **优先 `interface` 描述对象形状**，`type` 用于联合、交叉、映射类型

3. **不使用 `as` 断言**，除非从 `unknown` 强制转换或在测试中（需注释原因）。可用 `satisfies` 保持类型推导

4. **函数返回值必须显式标注**，特别是 `Promise<T>`

5. **可选链后禁止跟非空断言 `!`**（`x?.y!` 是矛盾的）

6. **所有仅用于类型的导入必须使用 `import type`**

```typescript
import type { User, Permission } from "@/shared/types/permissions";
```

7. **避免 `object` 或 `{}` 作为类型**，使用 `Record<string, unknown>` 或具体接口

8. **泛型使用有意义的名称**；若函数只有一处使用，不一定需要泛型

### 4.3 导入顺序（强制执行）

```typescript
// 1. React 相关
import React from "react";
// 2. 第三方库
import { z } from "zod";
import { useQuery } from "@tanstack/react-query";
// 3. 内部绝对路径（使用别名 @/）
import { Button } from "@/shared/components/ui/button";
import { requirePermission } from "@/shared/lib/auth-guard";
// 4. 相对路径导入
import { formatDate } from "../utils";
import { getExams } from "./data-access";
// 5. 类型导入
import type { User } from "@/shared/types/permissions";
```

使用 `eslint-plugin-import` 规则 `import/order` 自动排序，分组间空一行。

---

## 五、React 与 Next.js 组件规范

### 5.1 组件定义

- 组件必须为**纯函数**，使用 `function` 声明（非箭头函数，Google 风格）
- 页面组件（`page.tsx`）使用**默认导出**；其余所有组件使用**具名导出**
- **禁止在渲染期间**修改外部变量、执行网络请求、读取/写入 DOM（除 ref 初始化）

### 5.2 服务端组件 vs 客户端组件

- **默认服务端组件**。只有需要交互（事件处理）、状态（`useState`/`useReducer`）、效果（`useEffect`）或浏览器 API 时，才在文件顶部添加 `"use client"` 指令
- `"use client"` 必须位于文件**第一行**，之后空一行再写代码
- 客户端组件应尽可能小而聚焦。将需要交互的局部提取为客户端组件，外层容器保持服务端渲染
- **禁止在服务端组件中使用** `useState`, `useEffect`, `onClick` 等客户端特性

```tsx
// 容器页面（服务端）
import { UserList } from "@/modules/users/components/user-list";
import { getUsers } from "@/modules/users/data-access";

export default async function UsersPage(): Promise<JSX.Element> {
  const users = await getUsers();
  return <UserList users={users} />;
}
```

### 5.3 组件拆分指南

本项目采用**企业级行数规范**（见 [项目规则](../../.trae/rules/project_rules.md)）：

| 文件类型 | 建议行数 | 硬性上限 |
|---------|---------|---------|
| 配置/常量/类型定义文件 | 无限制 | 无限制 |
| React 组件 | ≤ 500 行 | 800 行（复杂表单/大型表格） |
| Server Actions / Data Access | ≤ 800 行 | 1000 行 |
| 工具函数 | ≤ 40 行 | - |
| 自定义 Hook | ≤ 80 行 | - |

**超过建议行数时的拆分信号**：
1. **语义边界**：子模块能用一个明确名称独立描述其作用
2. **状态边界**：有独立的 `useState`/`useEffect` 逻辑，或生命周期明显不同
3. **复用潜力**：某段 UI 或逻辑可能在另一页面使用
4. **复杂度预警**：Hook 调用超过 3 个，或 JSX 嵌套层级超过 4 层
5. **可测试性**：若要对组件的一部分逻辑编写单元测试，说明该部分应该独立

### 5.4 Hook 规范

- 命名以 `use` 开头，驼峰式
- **单一职责**：一个 Hook 只做一件事
- 返回值使用**对象形式**（非数组），方便使用者按需提取

```ts
const { data, isLoading, error } = useUser(userId);
```

- 必须编写 JSDoc，描述用途、参数、返回值和可能副作用
- 所有 `useEffect` 必须提供**清理函数**（如订阅、定时器）
- `useEffect` 依赖数组必须**完整**，不得遗漏响应式变量。若确实需要忽略，用 `// eslint-disable-next-line react-hooks/exhaustive-deps` 并注释原因

---

## 六、Tailwind CSS 规范

### 6.1 核心策略

本项目使用 **Tailwind v4**，采用 **CSS 变量设计令牌**（在 `globals.css` 中定义），而非传统的 `tailwind.config.ts` 扩展。

- **移动优先**：所有类名从无前缀（移动端）开始，逐步通过 `sm:`, `md:`, `lg:`, `xl:`, `2xl:` 增强
- **类名组织顺序**：布局 → 盒模型 → 排版 → 背景 → 边框 → 效果 → 状态
- **可读性**：当单个元素类名超过 10 个时，考虑提取为组件

### 6.2 类名编写最佳实践

使用 `cn()` 工具函数（基于 `clsx` + `tailwind-merge`）管理条件类名：

```tsx
import { cn } from "@/shared/lib/utils";

<button
  className={cn(
    "inline-flex items-center rounded px-4 py-2",
    "text-sm font-medium text-white",
    variant === "primary" && "bg-primary hover:bg-primary/90",
    disabled && "cursor-not-allowed opacity-50"
  )}
/>
```

**禁止模式**：
- ❌ 禁止字符串拼接动态类名（`bg-${color}-500`），Tailwind 无法静态分析
- ❌ 禁止使用 `!important`（Tailwind 的 `!` 前缀在必须覆盖第三方样式时可使用，但需谨慎）
- ❌ 禁止使用任意值（`w-[137px]`），除非有充分理由并注释说明

### 6.3 设计令牌配置

本项目在 `src/app/globals.css` 中使用 CSS 变量定义设计令牌：

```css
:root {
  --background: 0 0% 100%;
  --foreground: 240 10% 3.9%;
  --primary: 240 5.9% 10%;
  --primary-foreground: 0 0% 98%;
  --destructive: 0 84.2% 60.2%;
  --border: 240 5.9% 90%;
  --radius: 0.5rem;
  /* ... */
}
```

**所有视觉设计决策**（颜色、字号、间距）必须体现在设计令牌中，组件中不使用硬编码值。

---

## 七、数据获取与状态管理

### 7.1 数据获取分层

本项目采用**模块化 data-access 层**（替代传统 `services/`）：

| 层级 | 位置 | 职责 | 标记 |
|------|------|------|------|
| 服务端数据 | `modules/[module]/data-access.ts` | DB CRUD + 查询 | `import "server-only"` |
| 客户端动态数据 | TanStack Query | 缓存、重试、乐观更新 | `"use client"` |
| Server Actions | `modules/[module]/actions.ts` | 编排：权限 + 调用 data-access + revalidate | `"use server"` |

**规则**：
- 服务端数据获取通过模块的 `data-access.ts` 函数
- 客户端动态数据统一使用 **TanStack Query v5+**，**禁止在 `useEffect` 中手写 fetch**
- 缓存策略：服务端请求必须显式设置 `next.revalidate` 或使用 `unstable_cache`，并注释缓存时长理由

### 7.2 Server Actions

**文件命名**：`actions.ts` 或 `xxx-actions.ts`（如 `actions-analytics.ts`），置于对应模块目录

**强制规则**：
1. 每个 Action 函数必须使用 `"use server"`（文件顶部或函数级）
2. **权限校验**：函数体内必须调用 `requirePermission()`，绝不信任客户端参数
3. **输入验证**：使用 Zod 定义 schema，在 Action 入口解析，验证失败返回结构化错误
4. **返回值**：统一采用 `ActionState<T>` 类型（已定义于 `@/shared/types/action-state`）

```typescript
// 当前 ActionState 定义
export type ActionState<T = void> = {
  success: boolean;
  message?: string;
  errors?: Record<string, string[]>;
  data?: T;
};
```

5. **错误处理**：Action 内所有错误必须捕获并转为 `ActionState` 返回，客户端不得捕获到未处理异常
6. **缓存刷新**：使用 `revalidatePath` 或 `revalidateTag` 精确刷新相关缓存，避免全站重新验证

**标准 Action 模板**：

```typescript
"use server";

import { revalidatePath } from "next/cache";
import { z } from "zod";
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard";
import { Permissions } from "@/shared/types/permissions";
import { createExam } from "./data-access";
import type { ActionState } from "@/shared/types/action-state";

const ExamCreateSchema = z.object({
  title: z.string().min(1),
  // ...
});

export async function createExamAction(
  _prev: ActionState,
  formData: FormData
): Promise<ActionState<{ id: string }>> {
  try {
    const ctx = await requirePermission(Permissions.EXAM_CREATE);
    const parsed = ExamCreateSchema.safeParse(Object.fromEntries(formData));
    if (!parsed.success) {
      return {
        success: false,
        message: "表单校验失败",
        errors: parsed.error.flatten().fieldErrors,
      };
    }
    const exam = await createExam(ctx.userId, parsed.data);
    revalidatePath("/teacher/exams");
    return { success: true, data: { id: exam.id }, message: "创建成功" };
  } catch (error) {
    if (error instanceof PermissionDeniedError) {
      return { success: false, message: "权限不足" };
    }
    return { success: false, message: "创建失败，请重试" };
  }
}
```

### 7.3 状态管理

| 场景 | 方案 |
|------|------|
| 局部 UI 状态 | `useState` / `useReducer` |
| 跨组件共享（小范围） | React Context |
| 跨组件共享（大范围） | Zustand（轻量、可选择订阅） |
| 全局状态 | 仅存放真正全局必要数据（认证信息、主题、通知列表） |
| URL 状态 | `nuqs`（已集成） |

**规则**：
- Context 拆分：一个 Context 只负责一类数据，避免无关状态变化引发不必要的渲染
- 业务数据一律通过路由参数或 TanStack Query 获取，**不存入全局状态**
- Zustand 的 `persist` 中间件必须处理版本迁移和敏感数据加密

---

## 八、路由、代理与安全

### 8.1 路由组织

- 使用路由组 `(group)` 组织相同布局的页面，不影响 URL
- 动态路由 `[slug]`、捕获所有 `[...slug]`、可选捕获 `[[...slug]]` 按需使用
- 路由命名一律使用**小写与连字符**（`user-profile`）

### 8.2 代理（proxy.ts）

> **Next.js 16 重要变更**：`middleware.ts` 已重命名为 `proxy.ts`。本项目使用 `src/proxy.ts`。

- 集中处理**身份验证**、**权限路由**、**API 权限**
- 代理逻辑必须**轻量**（执行时间 < 50ms），复杂逻辑委托给 API 或服务端组件
- 使用 `NextResponse` 提供统一错误响应，**不做重型数据库操作**

当前 `proxy.ts` 实现：
- 路由前缀 → 最低权限映射（`/admin` → `school:manage`，`/teacher` → `exam:read`，等）
- API 路由前缀 → 权限映射（`/api/ai/chat` → `ai:chat`）
- 未认证 → 重定向登录；权限不足 → 重定向默认页

### 8.3 安全规范

1. **XSS 防护**：JSX 默认转义，**禁止 `dangerlySetInnerHTML`**。如必须使用，必须先用 DOMPurify 清洗

2. **CSRF 防护**：所有状态变更操作（POST/PUT/DELETE）必须校验 Origin/Referer 头。Next.js Server Actions 默认通过 POST 发送，仍需应用层校验

3. **认证令牌**：JWT/session ID 存储在 `httpOnly`、`Secure`、`SameSite=Strict` 的 Cookie 中，前端不可读

4. **环境变量**：
   - 服务端变量**不加** `NEXT_PUBLIC_` 前缀
   - 客户端变量**必须加** `NEXT_PUBLIC_` 前缀，且仅暴露非敏感信息
   - 使用 `@t3-oss/env-nextjs` + Zod 在应用启动时验证环境变量（已实现于 `src/env.mjs`）

5. **权限校验**：
   - Server Action 必须使用 `requirePermission()` 进行权限校验
   - 前端组件**禁止使用** `role === "xxx"` 硬编码，统一使用 `usePermission().hasPermission()`

6. **依赖扫描**：CI 中集成 `npm audit` + Snyk + Trivy，高危漏洞阻断合并

---

## 九、错误处理与可观测性

1. **错误边界**：每个路由段必须有 `error.tsx`；全局兜底 `global-error.tsx`

2. **结构化日志**：服务端日志使用 `shared/lib/audit-logger.ts`、`change-logger.ts`、`login-logger.ts`，记录关键操作

3. **性能监控**：通过 `web-vitals` 库上报 CLS、FID、LCP 到监控平台；生产环境开启 Next.js 内置分析

4. **API 速率限制**：对公开 API 或 Server Action 实施速率限制（`shared/lib/rate-limit.ts`），防止滥用

5. **审计日志**：
   - 登录日志：`login-logger.ts` 记录所有登录尝试（成功/失败）
   - 数据变更日志：`change-logger.ts` 记录所有数据变更操作
   - 审计日志：`audit-logger.ts` 记录关键业务操作

---

## 十、测试规范

### 10.1 测试分层

| 层级 | 工具 | 覆盖率目标 | 位置 |
|------|------|-----------|------|
| 单元测试 | Vitest | 100%（工具函数） | 同目录 `*.test.ts` |
| 组件测试 | React Testing Library + Vitest | ≥ 80% | 同目录 `*.test.tsx` |
| 集成测试 | Vitest + jsdom | 关键流程 | `tests/integration/` |
| E2E 测试 | Playwright | 核心业务路径 | `tests/e2e/` |
| 视觉回归 | Playwright（visual-chromium 项目） | 关键页面 | `tests/visual/` |

### 10.2 编写规范

- 测试文件与源文件**同目录**，命名为 `*.test.ts(x)` 或 `*.spec.ts(x)`
- 使用 `describe`/`it` 结构，描述应说明预期行为：`it("should disable button while loading")`
- 查询元素优先使用 `byRole`（符合无障碍），其次 `byLabelText`；避免 `byTestId` 除非必要
- 异步交互必须用 `waitFor` 或 `findBy*`，**禁止固定 `setTimeout`**
- Mock 仅用于外部边界（API、数据库）；组件自身逻辑须真实运行

### 10.3 测试命令

```bash
npm run test:unit          # 单元测试
npm run test:integration   # 集成测试
npm run test:e2e           # E2E 测试
npm run test:visual        # 视觉回归测试
npm run test               # 全部测试
```

---

## 十一、Git 工作流与提交规范

### 11.1 分支策略

- `main` 分支受保护，禁止直接推送
- 功能分支：`feat/JIRA-123-add-user-avatar`
- 修复分支：`fix/JIRA-456-correct-total`
- 发布分支：`release/v1.2.0`
- 热修复：`hotfix/security-patch`

### 11.2 提交信息（Conventional Commits）

```
feat(dashboard): add revenue chart
fix(cart): handle empty cart on checkout
chore(deps): update next to 16.0.0
docs(readme): add local setup guide
style(button): adjust hover color
refactor(utils): extract date formatting
test(checkout): cover discount edge cases
```

- 类型必须是 `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `perf`, `ci` 之一
- 范围可选但推荐（小写，与功能域对应）
- 提交信息使用**中文或英文**均可，但需与项目现有风格一致

### 11.3 质量门禁

- **提交前**：必须运行 `npm run lint` 和 `npx tsc --noEmit` 确保零错误
- **pre-commit**（推荐配置 Husky + lint-staged）：对暂存文件执行 `eslint --fix`, `prettier --write`
- **commit-msg**（推荐配置 commitlint）：检查提交信息格式

---

## 十二、CI/CD 流水线

当前项目使用 **Gitea Actions**（`.gitea/workflows/`）：

| 工作流 | 文件 | 用途 |
|--------|------|------|
| CI | `ci.yml` | Lint + 类型检查 + 单元测试 + 集成测试 + 构建 + 安全扫描 |
| 安全扫描 | `security.yml` | 每周深度扫描（npm audit + Snyk + Trivy + OWASP ZAP） |
| 灾备演练 | `dr-drill.yml` | 每周灾备演练 |

**CI 必须包含**：
1. 安装依赖（`npm ci`，利用缓存）
2. Lint 检查（ESLint）
3. 类型检查（`tsc --noEmit`）
4. 单元测试（Vitest + 覆盖率报告）
5. 构建（`next build`，确保 standalone 输出）
6. 安全审计（`npm audit` + Snyk + Trivy）
7. E2E 测试（仅主分支，部署 staging 后运行 Playwright）

---

## 十三、可访问性（A11y）规范

**目标**：WCAG 2.2 AA 合规

### 13.1 强制规则

1. **ESLint 插件**：`eslint-plugin-jsx-a11y` 设置为 `error`（待集成）
2. **键盘导航**：所有交互元素可用 Tab 访问，Enter/Space 激活，Escape 关闭弹层。焦点管理必须合理（弹窗打开时焦点移入，关闭后复原）
3. **语义化 HTML**：使用 `<header>`, `<main>`, `<nav>`, `<footer>`，按钮必须是 `<button>`（非 `div`）
4. **图片**：必须提供 `alt` 属性；装饰性图片用 `alt=""`
5. **表单**：每个输入元素关联 `<label>`，必填项明确标识
6. **色彩对比度**：文本与背景至少 4.5:1（普通文本）或 3:1（大文本）
7. **动态内容**：使用 `aria-live` 区域通知屏幕阅读器

### 13.2 已实现的 A11y 工具

- `src/shared/lib/a11y.ts`：`useA11yId`, `mergeA11yProps`, `describeInput`, `loadingAria`
- `src/shared/hooks/use-aria-live.ts`：aria-live 区域管理 Hook
- `src/shared/components/a11y/`：`skip-link`, `visually-hidden`, `focus-trap`, `aria-status`
- UI 组件（table, dialog）已增强系统性 ARIA role

### 13.3 工具检查

- CI 中可集成 `@axe-core/playwright` 做自动化检查
- 视觉回归测试（`tests/visual/`）覆盖关键页面的渲染一致性

---

## 十四、文档与交付物

### 14.1 项目必写文档

| 文档 | 位置 | 状态 |
|------|------|------|
| README.md | 根目录 | ✅ 待更新（当前为默认模板） |
| 架构文档 | `docs/architecture/` | ✅ 已完善（001-007） |
| 文档索引 | `docs/README.md` | ✅ 已创建 |
| 工作日志 | `docs/work_log.md` | ✅ 持续维护 |
| CONTRIBUTING.md | 根目录 | ❌ 待创建 |
| CHANGELOG.md | 根目录 | ❌ 待创建 |

### 14.2 架构文档维护规则

**任何源码修改后，必须同步更新架构文档**：

| 修改场景 | 需更新文档 |
|---------|-----------|
| 新增/删除/重命名导出函数、组件、Hook、类型 | 004 + 005 |
| 修改函数签名（参数、返回类型） | 004 + 005 |
| 修改权限点或角色-权限映射 | 004 + 005 |
| 新增/删除数据库表 | 004 + 005 |
| 新增/删除路由页面或 API 路由 | 004 + 005 |
| 修改模块间依赖关系 | 004 + 005 |
| 新增模块 | 004 + 005 + 006 |

### 14.3 可交付物清单

- 源代码仓库（完整提交历史）
- CI/CD 配置文件（`.gitea/workflows/`）
- 数据库迁移脚本（`drizzle/`）
- 环境配置模板（`.env.example`）
- 测试报告与覆盖率数据
- 安全扫描报告（`scripts/security-scan.sh`）
- 运维手册（`docs/dr/`、`scripts/backup-*.sh`）

---

## 十五、统一工具配置

### 15.1 ESLint（当前配置 + 建议增强）

**当前配置**（`eslint.config.mjs`）：

```javascript
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  {
    rules: {
      "react-hooks/incompatible-library": "off",
    },
  },
  // ...
]);

export default eslintConfig;
```

**建议增强**（待逐步集成）：

```javascript
{
  extends: [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:jsx-a11y/recommended",
    "prettier"
  ],
  rules: {
    "@typescript-eslint/no-explicit-any": "error",
    "react/react-in-jsx-scope": "off",
    "react/function-component-definition": [2, { "namedComponents": "function-declaration" }],
    "import/order": ["error", {
      "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "type"],
      "newlines-between": "always"
    }]
  }
}
```

### 15.2 Prettier

**当前配置**（`.prettierrc`）：

```json
{
  "semi": false,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "arrowParens": "always",
  "plugins": ["prettier-plugin-tailwindcss"]
}
```

### 15.3 lint-staged（建议配置）

```json
{
  "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
  "*.{css,scss}": ["prettier --write"]
}
```

---

## 十六、代码审查清单

审查者必须逐一确认：

### 16.1 架构与设计

- [ ] 命名表意清晰，无歧义
- [ ] 类型安全：无 `any`，无多余断言
- [ ] 组件拆分合理，无巨型组件（>500 行需讨论，>800 行必须拆分）
- [ ] 单一职责：每个文件/函数/组件只做一件事
- [ ] 模块封装：无跨模块直接 DB 查询（通过 data-access 通信）

### 16.2 实现质量

- [ ] 使用 Tailwind 正确，设计令牌符合主题，无任意值
- [ ] 数据获取：客户端用 TanStack Query，服务端用 data-access
- [ ] Server Actions 有权限校验（`requirePermission`），输入 Zod 验证，返回 `ActionState`
- [ ] 错误边界完善：`error.tsx`, `loading.tsx`, 网络异常处理
- [ ] 可访问性：焦点、键盘、ARIA、对比度

### 16.3 安全与合规

- [ ] 无 XSS 风险（无 `dangerlySetInnerHTML`）
- [ ] Cookie 安全（httpOnly + Secure + SameSite）
- [ ] 环境变量未泄露（服务端变量无 `NEXT_PUBLIC_` 前缀）
- [ ] 权限校验到位（前端 `usePermission`，后端 `requirePermission`）

### 16.4 测试与文档

- [ ] 关键逻辑有单元测试，交互有组件测试
- [ ] 新增组件有 Storybook（如适用）
- [ ] 复杂逻辑有注释
- [ ] 架构文档已同步更新（004 + 005）
- [ ] 提交信息规范，无无关文件混入

---

## 附录：与原规范的差异说明

本规范基于通用 Next.js 企业级规范适配，主要差异：

| 项目 | 通用规范 | 本项目 | 原因 |
|------|---------|-------|------|
| 项目结构 | Monorepo (Turborepo) | 单应用 + 模块化 | 项目规模适中，模块化已满足需求 |
| 数据获取层 | `services/` | `modules/[module]/data-access.ts` | 模块封装更好，避免跨模块直查 DB |
| 中间件 | `middleware.ts` | `proxy.ts` | Next.js 16 重命名 |
| 组件行数限制 | 300 行 | 500 行（硬上限 800） | 企业级 K12 系统表单/表格较复杂 |
| Actions 行数限制 | 无 | 800 行（硬上限 1000） | 编排层包含权限+验证+调用，需更多空间 |
| Tailwind 配置 | `tailwind.config.ts` 扩展 | CSS 变量（Tailwind v4） | Tailwind v4 推荐方式 |
| ActionState | `ActionResult<T>` 联合类型 | `ActionState<T>` 对象类型 | 已有实现，保持兼容 |
| 状态管理 | Zustand + Context | Zustand + Context + nuqs | URL 状态用 nuqs 更适合 Next.js |
| 环境变量校验 | Zod 自定义 | `@t3-oss/env-nextjs` + Zod | 已实现，更简洁 |
