docs: 适配企业级编码规范并补充配置
- 新增 docs/standards/coding-standards.md 编码规范文档(16 章节) - 适配当前项目: 单应用+模块化架构(非 Monorepo) - 保留 data-access.ts 模式(非 services/) - 使用 proxy.ts(Next.js 16 重命名) - 保留企业级行数规范(组件 500/800, Actions 800/1000) - 保留 Tailwind v4 CSS 变量设计令牌 - 保留 ActionState<T> 类型 - 含"与原规范的差异说明"附录(10 项差异及原因) - 更新 .trae/rules/project_rules.md: - 新增编码规范章节, 引用 coding-standards.md - 新增架构分层/模块结构/TS规则/命名/组件/Action/Tailwind/安全/提交规范 - 架构文档清单新增解耦路线图 - 新增 .prettierrc 配置(匹配现有代码风格: 双引号/无分号/2空格) - 更新 docs/README.md 新增编码规范章节 - 更新 work_log
This commit is contained in:
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"arrowParens": "always",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
| `docs/architecture/005_architecture_data.json` | AI 友好格式的结构化数据 |
|
||||
| `docs/architecture/006_k12_feature_checklist.md` | 标准功能模块清单 |
|
||||
| `docs/architecture/007_gap_audit_report.md` | 差距审计报告 |
|
||||
| `docs/architecture/audit/01_decoupling_roadmap.md` | 解耦路线图 |
|
||||
|
||||
### 需要同步图的场景
|
||||
|
||||
@@ -33,7 +34,11 @@
|
||||
- 修改 JSON 文档中对应的节点(`modules.*.exports`、`permissions`、`dependencyMatrix`、`routes`、`dbTables` 等)
|
||||
- 确保两个文档内容一致
|
||||
|
||||
## 代码质量规则
|
||||
## 编码规范
|
||||
|
||||
**详细规范见 `docs/standards/coding-standards.md`,以下为核心强制规则。**
|
||||
|
||||
### 代码质量规则
|
||||
|
||||
- 每次修改后运行 `npm run lint` 和 `npx tsc --noEmit` 确保零错误
|
||||
- Server Action 必须使用 `requirePermission()` 进行权限校验
|
||||
@@ -42,5 +47,77 @@
|
||||
- 配置文件、常量文件、类型定义文件:无限制
|
||||
- React 组件:建议 ≤ 500 行(复杂表单/大型表格可放宽至 800 行)
|
||||
- Server Actions / Data Access 模块:建议 ≤ 800 行
|
||||
- 工具函数:建议 ≤ 40 行
|
||||
- 自定义 Hook:建议 ≤ 80 行
|
||||
- 超过建议行数时应考虑拆分(如 data-access 拆分为多个按职责划分的文件)
|
||||
- 硬性上限:任何文件不超过 1000 行,超过必须拆分
|
||||
|
||||
### 架构分层规则
|
||||
|
||||
- 严格三层架构,依赖方向单向:`app → modules → shared`
|
||||
- `app/` 只能调用 `modules/` 的 Server Actions 和 data-access,不直接访问 DB
|
||||
- `modules/` 之间通过对方 data-access 通信,**不直接查询对方 DB 表**
|
||||
- `shared/` 是被依赖方,**不得反向依赖** `@/auth`、`@/proxy` 或任何 `modules/*`
|
||||
|
||||
### 模块标准结构
|
||||
|
||||
```
|
||||
src/modules/[module]/
|
||||
├─ actions.ts # Server Actions(编排层)
|
||||
├─ data-access.ts # 数据访问层(可拆分为 data-access-*.ts)
|
||||
├─ schema.ts # Zod 验证(可选)
|
||||
├─ types.ts # 类型定义
|
||||
├─ components/ # 模块专属组件
|
||||
└─ hooks/ # 模块专属 Hook(可选)
|
||||
```
|
||||
|
||||
### TypeScript 规则
|
||||
|
||||
- **禁止 `any`**:未知类型用 `unknown` 并做类型守卫
|
||||
- **禁止 `as` 断言**(除非从 `unknown` 转换或测试中,需注释原因)
|
||||
- **函数返回值必须显式标注**,特别是 `Promise<T>`
|
||||
- **仅用于类型的导入必须使用 `import type`**
|
||||
- **可选链后禁止跟非空断言 `!`**
|
||||
|
||||
### 命名规范
|
||||
|
||||
- 目录:kebab-case(`user-profile/`)
|
||||
- 组件文件:PascalCase(`UserProfile.tsx`)
|
||||
- Hook 文件:camelCase(`useAuth.ts`)
|
||||
- 变量/函数:camelCase,布尔值用 `is/has/can/should` 前缀
|
||||
- 常量:UPPER_SNAKE_CASE(`MAX_RETRY_COUNT`)
|
||||
- 类/接口:PascalCase,接口不加 `I` 前缀
|
||||
|
||||
### 组件规范
|
||||
|
||||
- 组件必须为纯函数,使用 `function` 声明
|
||||
- 页面组件(`page.tsx`)使用默认导出;其余组件使用具名导出
|
||||
- 默认服务端组件,需要交互时才添加 `"use client"`(必须位于文件第一行)
|
||||
- **不使用 `React.FC`**,直接用函数声明 + 显式标注 props 类型
|
||||
|
||||
### Server Action 规范
|
||||
|
||||
- 每个 Action 必须调用 `requirePermission()` 进行权限校验
|
||||
- 输入使用 Zod 验证,验证失败返回结构化错误
|
||||
- 返回值统一采用 `ActionState<T>` 类型
|
||||
- 使用 `revalidatePath` 精确刷新缓存
|
||||
|
||||
### Tailwind 规范
|
||||
|
||||
- 使用 `cn()` 工具函数管理条件类名
|
||||
- **禁止**字符串拼接动态类名(`bg-${color}-500`)
|
||||
- **禁止**使用任意值(`w-[137px]`),除非有充分理由并注释
|
||||
- 设计令牌在 `src/app/globals.css` 中使用 CSS 变量定义
|
||||
|
||||
### 安全规范
|
||||
|
||||
- **禁止 `dangerlySetInnerHTML`**(如必须使用,先用 DOMPurify 清洗)
|
||||
- JWT/session ID 存储在 httpOnly + Secure + SameSite=Strict 的 Cookie 中
|
||||
- 服务端环境变量不加 `NEXT_PUBLIC_` 前缀
|
||||
- 环境变量使用 `@t3-oss/env-nextjs` + Zod 校验(已实现于 `src/env.mjs`)
|
||||
|
||||
### 提交规范
|
||||
|
||||
- 使用 Conventional Commits 格式:`feat(scope): description`
|
||||
- 类型:`feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `perf`, `ci`
|
||||
- 提交前必须运行 `npm run lint` 和 `npx tsc --noEmit` 确保零错误
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
| [管理模块群审查](architecture/audit/management-modules-audit.md) | school/classes/users/audit 等管理模块审查 |
|
||||
| [新增模块和其他模块审查](architecture/audit/new-and-other-modules-audit.md) | diagnostic/elective/proctoring/notifications 等新增模块审查 |
|
||||
|
||||
## 编码规范
|
||||
|
||||
| 文档 | 用途 |
|
||||
|------|------|
|
||||
| [编码规范](standards/coding-standards.md) | 适配当前项目的企业级编码规范(TypeScript/React/Next.js/Tailwind/安全/测试/CI) |
|
||||
| [项目规则](../.trae/rules/project_rules.md) | AI 助手项目规则(架构图优先 + 核心强制规则) |
|
||||
|
||||
## 专题文档(活跃维护)
|
||||
|
||||
| 文档 | 用途 |
|
||||
|
||||
828
docs/standards/coding-standards.md
Normal file
828
docs/standards/coding-standards.md
Normal file
@@ -0,0 +1,828 @@
|
||||
# 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) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、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() {
|
||||
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 { ActionState } from "@/shared/types/action-state";
|
||||
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard";
|
||||
import { Permissions } from "@/shared/types/permissions";
|
||||
import { createExam, persistExamDraft } from "./data-access";
|
||||
|
||||
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": true,
|
||||
"singleQuote": true,
|
||||
"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 | 已实现,更简洁 |
|
||||
@@ -2,6 +2,44 @@
|
||||
|
||||
## 2026-06-17
|
||||
|
||||
### 编码规范适配与配置完善
|
||||
|
||||
#### 问题背景
|
||||
用户提供了一份通用 Next.js 企业级编码规范,需要结合当前项目实际情况进行适配。
|
||||
|
||||
#### 完成工作
|
||||
|
||||
##### 1. 创建适配后的编码规范文档
|
||||
- 新增 `docs/standards/coding-standards.md`(16 章节,全面适配当前项目)
|
||||
- 适配要点:
|
||||
- 项目结构:保留单应用 + 模块化架构(非 Monorepo)
|
||||
- 数据获取层:保留 `modules/[module]/data-access.ts`(非 `services/`)
|
||||
- 中间件:使用 `proxy.ts`(Next.js 16 重命名,非 `middleware.ts`)
|
||||
- 行数限制:保留企业级规范(组件 500/800,Actions 800/1000)
|
||||
- Tailwind:保留 v4 CSS 变量设计令牌方式
|
||||
- ActionState:保留现有 `ActionState<T>` 类型(非 `ActionResult` 联合类型)
|
||||
- 环境变量:保留 `@t3-oss/env-nextjs` + Zod(已实现)
|
||||
- 新增"与原规范的差异说明"附录,列出 10 项差异及原因
|
||||
|
||||
##### 2. 更新项目规则
|
||||
- 更新 `.trae/rules/project_rules.md`:
|
||||
- 新增"编码规范"章节,引用 `docs/standards/coding-standards.md`
|
||||
- 新增架构分层规则、模块标准结构、TypeScript 规则、命名规范、组件规范、Server Action 规范、Tailwind 规范、安全规范、提交规范
|
||||
- 架构文档清单新增解耦路线图
|
||||
- 行数规范新增工具函数 ≤40 行、自定义 Hook ≤80 行
|
||||
|
||||
##### 3. 补充缺失的配置文件
|
||||
- 新增 `.prettierrc`(匹配现有代码风格:双引号、无分号、2 空格、printWidth 100)
|
||||
- 配置 `prettier-plugin-tailwindcss` 插件(已在 devDependencies 中)
|
||||
|
||||
##### 4. 更新文档索引
|
||||
- `docs/README.md` 新增"编码规范"章节,登记 coding-standards.md 和 project_rules.md
|
||||
|
||||
#### 验证
|
||||
- 待验证 lint + tsc
|
||||
|
||||
---
|
||||
|
||||
### 架构全面审查与文档重构
|
||||
|
||||
#### 问题背景
|
||||
|
||||
Reference in New Issue
Block a user