# 代码规范

**分析日期：** 2026-05-07

## 命名规范

**文件命名：**
- 组件文件：PascalCase + `.tsx` 扩展名（如 `DashboardShell.tsx`、`AddOutfitDialog.tsx`）
- 工具函数/库文件：kebab-case + `.ts` 扩展名（如 `error-handler.ts`、`client.ts`）
- 特殊情况：对话框类组件使用 kebab-case（如 `add-outfit-dialog.tsx`、`delete-confirmation-dialog.tsx`）

**函数命名：**
- 使用 camelCase（如 `handleLogin`、`fetchOutfits`、`mapBackendOutfit`）
- API 函数：动词 + 名词，camelCase（如 `getOutfits`、`createOutfit`、`updateOutfit`、`deleteOutfit`）
- 事件处理函数：`handle` + 事件名（如 `handleSubmit`、`handleChange`、`handleSendVerificationCode`）
- 工具函数：动词 + 目标（如 `formatDate`、`toDisplayOutfit`）

**变量命名：**
- 状态变量：camelCase（如 `email`、`password`、`isLoading`、`selectedOutfit`）
- 布尔值：`is` 或 `has` 前缀（如 `isLoading`、`isSubmitting`、`hasPermission`）
- 常量：UPPER_SNAKE_CASE（如 `TOAST_LIMIT`、`TOAST_REMOVE_DELAY`、`API_BASE_URL`）

**类型/接口命名：**
- PascalCase（如 `EmailLoginResponse`、`DashboardShellProps`、`ApiResponse<T>`）
- Props 接口：`组件名Props` 后缀（如 `ButtonProps`、`DashboardShellProps`）
- 联合类型：`RoleName`、`PermissionModule`

## 代码样式

**格式化：**
- 使用 TypeScript strict 模式（`tsconfig.json` 中 `"strict": true`）
- 目标：ES6（`target: "ES6"`）
- 模块化解析：`bundler` 模式（用于 Next.js）
- 无 ESLint/Prettier 配置文件（`next.config.mjs` 中 `eslint.ignoreDuringBuilds: true`）
- 代码风格通过 TypeScript 编译器和 Next.js 内置检查

**缩进和空格：**
- 使用 2 空格缩进（根据代码库一致性）
- 函数声明和块语句间使用一致空格

**类型注解：**
- 所有函数参数必须有类型注解
- 所有变量应有类型注解（特别是导出的接口和公共函数返回值）
- 使用 TypeScript 严格模式防止隐式 `any`

## 导入组织

**顺序：**
1. React 和 Next.js 核心库（`import React from "react"`、`import { useState } from "react"`）
2. Next.js 功能（`import { useRouter } from "next/navigation"`、`import Link from "next/link"`）
3. 第三方库（`import axios from "axios"`、`import Cookies from "js-cookie"`）
4. 本项目组件（`import { Button } from "@/components/ui/button"`）
5. 本项目库和工具（`import { cn } from "@/lib/utils"`）
6. 本项目 hooks（`import { useToast } from "@/components/ui/use-toast"`）
7. 类型导入（`import type { ApiResponse } from "./client"`）

**路径别名：**
- 项目配置 `@/*` 指向项目根目录
- 组件：`@/components/...`
- 库函数：`@/lib/...`
- Hooks：`@/hooks/...`
- 类型和接口：通过 `@/lib/api/types` 集中导入
- UI 组件：`@/components/ui/...`

## 错误处理

**模式：**
- 使用 `try-catch` 处理异步操作和 API 调用（见 `auth.ts`、`outfits.ts`）
- 创建自定义错误类 `ApiError` 扩展 `Error`，包含 `status` 属性（见 `error-handler.ts` 第 46-54 行）
- 统一的错误处理函数 `handleApiException()` 将不同类型的错误转换为用户友好的消息（见 `error-handler.ts` 第 79-90 行）
- 错误信息映射表 `errorMessages` 定义特定错误代码（`USER_NOT_FOUND`、`ROLE_EXISTS` 等）的消息

**API 错误处理：**
- 在 Axios 响应拦截器中处理 401 未授权错误：清除 token 并重定向到登录页面（见 `client.ts` 第 54-61 行）
- 对 API 失败使用 Radix Toast 组件显示错误提示（见 `error-handler.ts` 第 69-76 行、第 93-100 行）
- 统一的 `handleApiRequest()` 包装器函数处理成功/失败情况并自动显示 toast（见 `error-handler.ts` 第 102-144 行）

**日志：**
- 使用 `console.log()`、`console.warn()`、`console.error()` 进行调试和错误追踪
- API 请求/响应在拦截器中记录详细日志，包括 token 检查、请求头、响应状态（见 `client.ts` 第 20-66 行）
- 使用 emoji 表情增强日志可读性（`🔍 Token检查`、`✅ Token已添加`、`❌ 响应错误`）

## 日志记录

**框架：** `console`（浏览器原生）

**使用规范：**
- API 请求/响应日志在 Axios 拦截器中集中管理
- 所有认证流程加日志（token 检查、保存、清除）
- 错误信息带上上下文（URL、状态码、方法）
- 生产环境应考虑减少日志或使用第三方服务（未配置）

## 注释

**何时写注释：**
- JSDoc 风格注释用于公共 API 函数（见 `auth.ts` 第 5-7 行、第 24-30 行）
- 注释解释"为什么"而不是"是什么"
- 复杂业务逻辑或算法需要行内注释

**JSDoc/TSDoc 风格：**
```typescript
/**
 * 邮箱登录接口
 * @param email 邮箱
 * @param password 密码
 * @returns 包含token的响应
 */
export const emailLogin = async (email: string, password: string): Promise<EmailLoginResponse> => {
  // ...
}
```

**中文注释：** 所有注释使用中文（符合 CLAUDE.md 的语言偏好）

## 函数设计

**大小：** 
- 函数长度通常 20-50 行（见 `handleLogin` 在 `login/page.tsx` 第 28-63 行）
- 超过 100 行的函数应考虑拆分（如 `AddOutfitDialog` 组件中的多步表单分离为 Tabs）

**参数：**
- 单个参数优于多个参数
- 对象参数用于选项（如 `handleApiRequest()` 的 options 对象，第 104-110 行）
- 类型参数用于泛型函数（如 `handleResponse<T>()`、`ApiResponse<T>`）

**返回值：**
- 异步函数返回 `Promise<T>`
- 布尔查询函数返回 `boolean`（如 `hasPermission()`、`isAuthenticated()`）
- 数据获取返回具体类型或 `null`（见 `getOutfit()` 返回 `Promise<Outfit>`）

## 模块设计

**导出：**
- 公共 API 函数从 `lib/api/` 模块导出（如 `emailLogin`、`getOutfits`）
- 类型从 `lib/api/types.ts` 集中导出
- Utility 函数从 `lib/utils.ts` 导出（如 `cn()` 用于样式合并）

**Barrel 文件：**
- `lib/api/index.ts` 作为 barrel 文件，导出所有 API 函数以便统一导入
- 组件库采用 Radix UI + shadcn 风格，每个组件文件独立，无 barrel 文件

## React 和组件规范

**状态管理：**
- 使用 React Hooks（`useState`、`useEffect`、`useCallback`）进行本地状态管理
- 在客户端组件中集中管理表单状态（见 `add-outfit-dialog.tsx` 第 24-36 行）

**组件模式：**
- 所有表单和交互组件用 `"use client"` 指令标记
- Page 组件（`app/*/page.tsx`）使用 `"use client"` 支持交互
- 使用 Radix UI 和 shadcn 风格组件库，组件文件在 `components/ui/`
- Props 接口扩展原生 HTML 属性（如 `ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>`）

**样式：**
- 使用 Tailwind CSS 类名
- 使用 `cn()` 工具函数（基于 `clsx` + `tailwind-merge`）合并条件样式（见 `button.tsx` 第 47 行）
- 使用 `class-variance-authority` (CVA) 定义组件变体（见 `button.tsx` 第 7-34 行）
- 颜色、间距、圆角通过 Tailwind 配置变量（CSS 变量）定义

## 表单与验证

**表单状态：**
- 使用 `useState` 管理表单字段状态
- 表单提交通过 `handleSubmit()` 函数处理（见 `add-outfit-dialog.tsx` 第 38-56 行）

**验证：**
- 基本的客户端验证（如检查空值：`if (!formData.name || !formData.description)`）
- React Hook Form + Zod 在 package.json 中定义但代码中未广泛使用
- 服务器端验证通过 API 响应处理（返回 400/422 等错误状态）

**通知：**
- 成功/错误提示使用 Sonner toast 或 Radix Toast（通过 `useToast()` hook）
- toast 样式变体：`"default"`、`"destructive"`（见 `error-handler.ts` 第 71-75 行）

## 权限控制

**权限检查：**
- 运行时权限检查通过 `hasPermission(module)` 函数（见 `permissions.ts` 第 85-87 行）
- 权限矩阵定义在 `PERMISSION_MATRIX` 对象，映射角色到模块列表
- 路由保护：中间件 `middleware.ts` 检查 cookie 中的 token，无 token 重定向到登录
- 组件级权限检查：`DashboardShell` 使用 `hasPathPermission()` 处理访问拒绝（见 `dashboard-shell.tsx` 第 23-24 行）

**角色存储：**
- 登录后在 `localStorage` 中存储 `user_role` 字符串（见 `auth.ts` 第 58 行）
- 超级管理员标识存储为 `is_superuser`（见 `auth.ts` 第 53 行）
- token 同时存储在 `localStorage` 和 Cookie 中（见 `auth.ts` 第 49、63 行）

## 异步操作

**模式：**
- 使用 `async/await` 处理异步 API 调用
- 加载状态通过 `isLoading` 布尔值跟踪（见 `login/page.tsx` 第 19、30 行）
- 模拟延迟用于开发和测试（见 `client.ts` `simulateDelay()` 和 `mockResponse()` 函数）

## API 集成

**Axios 配置：**
- 在 `lib/api/client.ts` 创建 Axios 实例，基础 URL 来自环境变量 `NEXT_PUBLIC_API_BASE_URL`
- 请求拦截器自动注入 Authorization 头（Bearer token）
- 响应拦截器处理 401 错误并触发重新登录
- API 端点前缀：`/api/v1/admin/`

**适配器模式：**
- 后端返回数据结构与前端显示结构不同
- 使用 `mapBackendOutfit()` 等适配器函数转换数据（见 `outfits.ts` 第 5-22 行）
- 适配器在 `lib/api/adapters.ts` 中集中管理

---

*规范分析日期：2026-05-07*
