diff --git a/docs/Dialog.md b/.recycle/2026-01-21/docs/Dialog.md similarity index 100% rename from docs/Dialog.md rename to .recycle/2026-01-21/docs/Dialog.md diff --git a/docs/OptBtnGroups.md b/.recycle/2026-01-21/docs/OptBtnGroups.md similarity index 100% rename from docs/OptBtnGroups.md rename to .recycle/2026-01-21/docs/OptBtnGroups.md diff --git a/docs/Toolbar.md b/.recycle/2026-01-21/docs/Toolbar.md similarity index 100% rename from docs/Toolbar.md rename to .recycle/2026-01-21/docs/Toolbar.md diff --git a/demo/DEPLOY.md b/demo/部署指南.md similarity index 100% rename from demo/DEPLOY.md rename to demo/部署指南.md diff --git a/docs/AI代码规范模板.md b/docs/AI代码规范模板.md new file mode 100644 index 0000000..8039c44 --- /dev/null +++ b/docs/AI代码规范模板.md @@ -0,0 +1,2024 @@ +# AI 代码规范模板 + +> 本文档是通用的 AI 协作与代码规范模板,适用于 TypeScript、Java、React、Vue 等技术栈。 +> 使用时请根据项目实际情况进行调整。 + +--- + +## 📋 目录 + +### 第一部分:通用规范 +- [1. AI 协作基本原则](#1-ai-协作基本原则) +- [2. 通用编码规范](#2-通用编码规范) +- [3. 文件与文档管理](#3-文件与文档管理) + +### 第二部分:语言特定规范 +- [4. TypeScript 规范](#4-typescript-规范) +- [5. Java 规范](#5-java-规范) + +### 第三部分:框架特定规范 +- [6. React 规范](#6-react-规范) +- [7. Vue 规范](#7-vue-规范) + +### 第四部分:工程化规范 +- [8. Git 提交规范](#8-git-提交规范) +- [9. API 设计规范](#9-api-设计规范) +- [10. 测试规范](#10-测试规范) + +### 附录 +- [附录 A:代码审查清单](#附录-a代码审查清单) +- [附录 B:常见任务快速参考](#附录-b常见任务快速参考) +- [附录 C:模板使用说明](#附录-c模板使用说明) + +--- + +# 第一部分:通用规范 + +## 1. AI 协作基本原则 + +### 1.1 语言要求(强制) + +#### 输出语言 +- **所有输出必须使用中文**,包括: + - 代码注释(**强制:所有代码注释必须使用中文,解释清晰详细**) + - 文档说明 + - 与用户交流 + - 错误信息和日志输出 + +#### 思考语言 +- **思考过程也必须使用中文**: + - 分析问题时用中文思考 + - 解释代码逻辑时用中文 + - 讨论设计方案时用中文 + +#### 代码中的字符串 +- 根据业务需求决定(支持国际化的项目使用翻译键) +- 注释和文档必须使用中文 + +### 1.2 思考与沟通方式 + +#### 思考过程要求 +- 分析问题时,用中文描述问题、分析思路、得出结论 +- 解释代码时,用中文说明逻辑、设计意图、实现方式 +- 讨论方案时,用中文描述优缺点、选择理由 + +#### 工作流程 +1. **理解需求**:用中文理解用户需求,明确任务目标 +2. **分析问题**:用中文分析问题,梳理相关代码和架构 +3. **设计方案**:用中文设计解决方案,考虑边界情况 +4. **实现代码**:编写代码,添加中文注释 +5. **测试验证**:用中文描述测试结果和发现的问题 +6. **更新文档**:用中文更新相关文档 + +### 1.3 问答与开发流程 + +#### 用户询问"如何实现"时的处理流程 + +**当用户询问如何实现某个功能时,必须遵循以下流程:** + +1. **分析需求**:用中文理解用户需求,明确要实现的功能 + +2. **制定详细开发计划**:必须包含以下内容: + - **需要修改的文件列表** + - **需要新增的文件列表** + - **需要删除的文件列表** + - **每个文件的作用说明** + - **对整体结构的影响** + +3. **展示开发计划**:以清晰的格式展示给用户 + +4. **等待用户确认**:**在用户明确同意之前,绝对不能修改任何代码** + +5. **用户同意后执行**:只有在用户明确表示同意后,才能开始修改代码 + +#### 开发计划格式模板 + +```markdown +## 开发计划 + +### 需要修改的文件 +1. `src/xxx/xxx.ts` + - 修改:添加新的 `xxxMethod()` 方法 + - 作用:提供某某功能 + +### 需要新增的文件 +1. `src/xxx/index.ts` + - 作用:实现某某功能 + - 内容:包含 XxxClass 类 + +### 需要删除的文件 +1. `src/old/xxx.ts` + - 原因:已被新组件替代 + - **处理方式**:移动到 `.recycle/YYYY-MM-DD/` 文件夹 + +### 对整体结构的影响 +- **架构影响**:说明对现有架构的影响 +- **功能影响**:说明对现有功能的影响 +- **文档更新**:需要更新哪些文档 +- **风险点**:可能的风险和注意事项 + +请确认是否按照此计划进行开发? +``` + +#### 用户直接要求修改时的处理 + +**当用户直接要求修改代码时**(如"帮我修改XXX"、"实现XXX功能"),可以: +- 直接执行代码修改 +- 但仍需遵循其他规范 +- 删除文件时仍需移动到回收文件夹 + +--- + +## 2. 通用编码规范 + +### 2.1 代码可读性 + +#### 基本原则 +- **优先保证代码可读性**,有时可以不使用高级函数,使用低级函数 +- **必须添加详细的中文注释**,解释代码逻辑和设计意图 +- 函数、类、接口必须有文档注释(使用中文) +- 复杂逻辑必须添加行内注释说明 + +#### 代码组织 +- 单个文件不宜超过 **500 行** +- 单个函数不宜超过 **50 行** +- 单行代码不宜超过 **120 字符** +- 嵌套层级不宜超过 **4 层** + +### 2.2 命名规范 + +#### 通用命名规则 + +| 类型 | 命名方式 | 示例 | +|-----|---------|------| +| 类名 | 大驼峰 (PascalCase) | `UserManager`, `OrderService` | +| 接口名 | 大驼峰,可选 `I` 前缀 | `IUserService`, `UserRepository` | +| 类型/枚举 | 大驼峰 | `UserStatus`, `OrderType` | +| 函数/方法 | 小驼峰 (camelCase) | `getUserInfo`, `handleSubmit` | +| 变量 | 小驼峰 | `userName`, `isActive`, `itemCount` | +| 常量 | 全大写下划线 | `MAX_COUNT`, `API_BASE_URL` | +| 私有属性 | 下划线前缀或 `#` | `_cache`, `#privateField` | +| 文件名 | 小写短横线或小写 | `user-manager.ts`, `index.ts` | +| CSS 类名 | 小写短横线 (kebab-case) | `user-card`, `btn-primary` | + +#### 命名语义化 + +```typescript +// ✅ 好的命名 - 语义清晰 +const userList: User[] = []; +const isLoading: boolean = false; +const maxRetryCount: number = 3; +function calculateTotalPrice(items: Item[]): number { } +function validateUserInput(input: string): boolean { } + +// ❌ 不好的命名 - 语义不清 +const list: any[] = []; +const flag: boolean = false; +const num: number = 3; +function calc(arr: any[]): number { } +function check(str: string): boolean { } +``` + +#### 布尔值命名 +- 使用 `is`、`has`、`can`、`should`、`will` 等前缀 +- 例如:`isActive`、`hasPermission`、`canEdit`、`shouldUpdate` + +#### 函数命名 +- 动词开头,表明行为 +- 查询:`get`、`find`、`query`、`fetch`、`load` +- 创建:`create`、`add`、`insert`、`save` +- 更新:`update`、`modify`、`set`、`change` +- 删除:`delete`、`remove`、`clear`、`reset` +- 判断:`is`、`has`、`can`、`should`、`validate`、`check` +- 转换:`to`、`convert`、`parse`、`format` +- 事件:`handle`、`on`、`trigger`、`emit` + +### 2.3 注释规范 + +#### 文档注释(JSDoc / JavaDoc) + +**TypeScript / JavaScript:** +```typescript +/** + * 计算订单总价 + * + * @description 根据商品列表计算总价,包含折扣计算 + * @param items - 商品列表 + * @param discount - 折扣比例(0-1),默认为 1(无折扣) + * @returns 计算后的总价 + * @throws {Error} 当商品列表为空时抛出错误 + * @example + * const total = calculateTotal(items, 0.8); // 8折 + */ +function calculateTotal(items: CartItem[], discount: number = 1): number { + if (items.length === 0) { + throw new Error('商品列表不能为空'); + } + // 计算原价总和 + const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0); + // 应用折扣 + return subtotal * discount; +} +``` + +**Java:** +```java +/** + * 计算订单总价 + * + *

根据商品列表计算总价,包含折扣计算

+ * + * @param items 商品列表 + * @param discount 折扣比例(0-1),默认为 1(无折扣) + * @return 计算后的总价 + * @throws IllegalArgumentException 当商品列表为空时抛出 + * @since 1.0.0 + * @author AI Assistant + */ +public BigDecimal calculateTotal(List items, BigDecimal discount) { + // 实现逻辑 +} +``` + +#### 行内注释 +```typescript +// 单行注释:解释接下来一行或几行代码的作用 + +/* + * 多行注释: + * 用于解释复杂的逻辑块 + * 或临时禁用代码 + */ + +// TODO: 待完成的功能 +// FIXME: 需要修复的问题 +// HACK: 临时解决方案,需要优化 +// NOTE: 重要说明 +// DEPRECATED: 已废弃,请使用新方法 +``` + +#### 注释原则 +- **解释为什么,而不是是什么**:代码本身说明做什么,注释说明为什么 +- **保持注释与代码同步**:代码修改后必须更新注释 +- **不要注释显而易见的代码**:避免无意义的注释 +- **复杂业务逻辑必须注释**:特别是涉及特定业务规则的代码 + +### 2.4 错误处理 + +#### 基本原则 +- 所有异步操作必须有错误处理 +- 错误信息必须清晰、有意义,使用中文 +- 区分可恢复错误和不可恢复错误 +- 不要吞掉异常(空 catch 块) + +#### TypeScript 错误处理 +```typescript +// ✅ 正确的错误处理 +async function fetchUserData(userId: string): Promise { + try { + const response = await fetch(`/api/users/${userId}`); + + if (!response.ok) { + // 根据状态码抛出具体错误 + if (response.status === 404) { + throw new Error(`用户不存在:${userId}`); + } + throw new Error(`请求失败:${response.status}`); + } + + return await response.json(); + } catch (error) { + // 记录错误日志 + console.error('获取用户数据失败:', error); + // 重新抛出或返回默认值 + throw error; + } +} + +// ❌ 错误的错误处理 +async function badFetch(userId: string) { + try { + const res = await fetch(`/api/users/${userId}`); + return await res.json(); + } catch (e) { + // 空 catch 块,吞掉了错误 + } +} +``` + +#### Java 错误处理 +```java +// ✅ 正确的错误处理 +public User getUserById(Long id) { + try { + return userRepository.findById(id) + .orElseThrow(() -> new BusinessException("用户不存在:" + id)); + } catch (DataAccessException e) { + log.error("查询用户失败,ID:{}", id, e); + throw new ServiceException("数据库查询异常", e); + } +} + +// 自定义业务异常 +public class BusinessException extends RuntimeException { + private final String code; + + public BusinessException(String message) { + super(message); + this.code = "BUSINESS_ERROR"; + } + + public BusinessException(String code, String message) { + super(message); + this.code = code; + } +} +``` + +### 2.5 日志规范 + +#### 日志级别 +| 级别 | 使用场景 | +|-----|---------| +| `ERROR` | 错误,影响功能正常运行 | +| `WARN` | 警告,潜在问题但不影响运行 | +| `INFO` | 重要业务信息,如用户操作、接口调用 | +| `DEBUG` | 调试信息,开发环境使用 | +| `TRACE` | 详细追踪信息,极少使用 | + +#### 日志内容要求 +```typescript +// ✅ 好的日志 +logger.info(`用户登录成功,用户ID:${userId},IP:${clientIp}`); +logger.error(`订单创建失败,订单号:${orderId},原因:${error.message}`, error); +logger.warn(`库存不足,商品ID:${productId},当前库存:${stock},请求数量:${quantity}`); + +// ❌ 不好的日志 +logger.info('success'); // 信息不完整 +logger.error(error); // 缺少上下文 +console.log('here'); // 调试代码未清理 +``` + +#### 日志原则 +- 包含足够的上下文信息(ID、参数等) +- 敏感信息脱敏(密码、手机号等) +- 生产环境不使用 `console.log` +- 异常日志要包含堆栈信息 + +### 2.6 安全规范 + +#### 输入验证 +```typescript +// ✅ 验证所有外部输入 +function processUserInput(input: unknown): string { + // 类型检查 + if (typeof input !== 'string') { + throw new Error('输入必须是字符串'); + } + // 长度检查 + if (input.length > 1000) { + throw new Error('输入长度超出限制'); + } + // 内容清理 + return sanitize(input); +} +``` + +#### XSS 防护 +```typescript +// ❌ 危险:直接插入 HTML +element.innerHTML = userInput; + +// ✅ 安全:使用 textContent 或转义 +element.textContent = userInput; +// 或 +element.innerHTML = escapeHtml(userInput); +``` + +#### 敏感数据处理 +```typescript +// ❌ 不要在日志中输出敏感信息 +console.log(`用户密码:${password}`); + +// ✅ 脱敏处理 +console.log(`手机号:${phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}`); +``` + +--- + +## 3. 文件与文档管理 + +### 3.1 文件删除规范(强制) + +**严禁直接删除文件,必须遵循以下流程:** + +#### 步骤 1:创建回收文件夹 +在项目根目录创建 `.recycle/` 文件夹(如果不存在) + +#### 步骤 2:按日期组织 +- 在 `.recycle/` 下按日期创建子文件夹 +- 格式:`YYYY-MM-DD`(如 `2024-01-15`) +- 同一天删除的文件存放在同一个日期文件夹中 + +#### 步骤 3:移动文件而非删除 + +```bash +# ❌ 错误方式 +rm src/old-file.ts + +# ✅ 正确方式 +DATE=$(date +%Y-%m-%d) +mkdir -p .recycle/$DATE/src +mv src/old-file.ts .recycle/$DATE/src/old-file.ts +``` + +#### 步骤 4:保持目录结构 +在日期文件夹中保持原文件的目录结构 + +例如:`src/components/old.ts` → `.recycle/2024-01-15/src/components/old.ts` + +#### 回收文件夹结构示例 +``` +.recycle/ +├── 2024-01-15/ +│ ├── README.md # 可选:记录删除原因 +│ └── src/ +│ └── components/ +│ └── old-component.ts +└── 2024-02-20/ + └── src/ + └── services/ + └── old-service.ts +``` + +### 3.2 文档更新规则 + +#### 必须更新的情况 + +| 操作 | 需要更新的文档 | +|-----|--------------| +| 新增文件 | 在相关文档中添加文件描述 | +| 删除文件 | 从文档中移除对应条目 | +| 修改文件功能 | 更新对应文件的描述 | +| 新增组件/模块 | 添加说明,创建详细文档 | +| 修改 API | 同步更新 API 文档 | +| 修改架构 | 更新架构设计部分 | +| 修改配置 | 更新配置说明 | + +#### 更新方式 +1. 修改代码后,**立即**更新相关文档 +2. 确保文档与代码保持一致 +3. 如果发现文档过时,优先更新文档 + +--- + +# 第二部分:语言特定规范 + +## 4. TypeScript 规范 + +### 4.1 类型定义规范 + +#### 优先使用 interface 定义对象类型 +```typescript +// ✅ 推荐:使用 interface +interface UserInfo { + id: string; + name: string; + email: string; + age?: number; // 可选属性 + readonly createdAt: Date; // 只读属性 +} + +// 接口继承 +interface AdminUser extends UserInfo { + role: 'admin'; + permissions: string[]; +} +``` + +#### 使用 type 定义联合类型、交叉类型 +```typescript +// 联合类型 +type Status = 'pending' | 'processing' | 'completed' | 'failed'; +type ID = string | number; + +// 交叉类型 +type UserWithMeta = UserInfo & { + metadata: Record; +}; + +// 工具类型 +type PartialUser = Partial; +type RequiredUser = Required; +type ReadonlyUser = Readonly; +type UserKeys = keyof UserInfo; +``` + +### 4.2 避免 any,使用正确的类型 + +```typescript +// ❌ 避免使用 any +function processData(data: any): any { + return data.value; +} + +// ✅ 使用 unknown + 类型守卫 +function processData(data: unknown): string { + if (typeof data === 'object' && data !== null && 'value' in data) { + return String((data as { value: unknown }).value); + } + throw new Error('无效的数据格式'); +} + +// ✅ 使用泛型 +function processData(data: T): string { + return data.value; +} +``` + +### 4.3 泛型使用 + +```typescript +// 基础泛型 +function identity(value: T): T { + return value; +} + +// 泛型约束 +function getProperty(obj: T, key: K): T[K] { + return obj[key]; +} + +// 泛型接口 +interface Repository { + findById(id: string): Promise; + findAll(): Promise; + save(entity: T): Promise; + delete(id: string): Promise; +} + +// 泛型类 +class BaseService { + constructor(private repository: Repository) {} + + async getById(id: string): Promise { + return this.repository.findById(id); + } +} +``` + +### 4.4 模块导入导出 + +```typescript +// ✅ 导入顺序(按此顺序组织) +// 1. Node.js 内置模块 +import path from 'path'; +import fs from 'fs'; + +// 2. 第三方库 +import React from 'react'; +import axios from 'axios'; + +// 3. 项目内部模块 - 绝对路径 +import { UserService } from '@/services/user'; +import { formatDate } from '@/utils/date'; + +// 4. 项目内部模块 - 相对路径 +import { Button } from '../components/Button'; +import { useAuth } from './hooks/useAuth'; + +// 5. 类型导入 +import type { User, UserRole } from '@/types'; + +// 6. 样式文件 +import './styles.css'; +``` + +```typescript +// ✅ 导出方式 +// 命名导出 - 推荐用于工具函数、类型等 +export function formatDate(date: Date): string { } +export interface UserInfo { } +export const MAX_COUNT = 100; + +// 默认导出 - 推荐用于组件、类 +export default class UserService { } +export default function UserCard() { } + +// 重新导出 +export { UserService } from './user-service'; +export * from './types'; +export { default as Button } from './Button'; +``` + +### 4.5 异步编程 + +```typescript +// ✅ 使用 async/await +async function fetchUserData(userId: string): Promise { + try { + const response = await fetch(`/api/users/${userId}`); + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`); + } + return await response.json(); + } catch (error) { + console.error('获取用户数据失败:', error); + throw error; + } +} + +// ✅ 并行请求 +async function fetchMultipleUsers(ids: string[]): Promise { + const promises = ids.map(id => fetchUserData(id)); + return Promise.all(promises); +} + +// ✅ 带超时的请求 +async function fetchWithTimeout( + promise: Promise, + timeoutMs: number +): Promise { + const timeout = new Promise((_, reject) => { + setTimeout(() => reject(new Error('请求超时')), timeoutMs); + }); + return Promise.race([promise, timeout]); +} +``` + +### 4.6 枚举与常量 + +```typescript +// ✅ 字符串枚举 - 推荐 +enum OrderStatus { + Pending = 'PENDING', + Processing = 'PROCESSING', + Completed = 'COMPLETED', + Cancelled = 'CANCELLED' +} + +// ✅ const 枚举 - 编译时内联 +const enum Direction { + Up = 'UP', + Down = 'DOWN', + Left = 'LEFT', + Right = 'RIGHT' +} + +// ✅ 常量对象 + as const - 更灵活 +const HTTP_STATUS = { + OK: 200, + CREATED: 201, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + NOT_FOUND: 404, + INTERNAL_ERROR: 500 +} as const; + +type HttpStatusCode = typeof HTTP_STATUS[keyof typeof HTTP_STATUS]; +``` + +--- + +## 5. Java 规范 + +### 5.1 包结构与命名 + +#### 标准包结构 +``` +com.company.project +├── controller/ # 控制器层 +│ └── UserController.java +├── service/ # 服务层 +│ ├── UserService.java # 接口 +│ └── impl/ +│ └── UserServiceImpl.java # 实现 +├── repository/ # 数据访问层 +│ └── UserRepository.java +├── model/ # 数据模型 +│ ├── entity/ # 数据库实体 +│ │ └── User.java +│ ├── dto/ # 数据传输对象 +│ │ ├── UserDTO.java +│ │ └── UserCreateRequest.java +│ └── vo/ # 视图对象 +│ └── UserVO.java +├── config/ # 配置类 +│ └── WebConfig.java +├── common/ # 公共模块 +│ ├── exception/ # 异常类 +│ ├── util/ # 工具类 +│ └── constant/ # 常量类 +└── Application.java # 启动类 +``` + +#### 类命名规范 + +| 类型 | 命名规则 | 示例 | +|-----|---------|------| +| 控制器 | `Xxx Controller` | `UserController` | +| 服务接口 | `XxxService` | `UserService` | +| 服务实现 | `XxxServiceImpl` | `UserServiceImpl` | +| 数据访问 | `XxxRepository` / `XxxMapper` | `UserRepository` | +| 实体类 | `Xxx` | `User`, `Order` | +| DTO | `XxxDTO` / `XxxRequest` / `XxxResponse` | `UserDTO` | +| VO | `XxxVO` | `UserVO` | +| 工具类 | `XxxUtil` / `XxxUtils` / `XxxHelper` | `DateUtils` | +| 常量类 | `XxxConstants` | `ApiConstants` | +| 枚举 | `XxxEnum` / `XxxType` / `XxxStatus` | `OrderStatus` | +| 异常 | `XxxException` | `BusinessException` | + +### 5.2 类设计原则 + +#### 实体类 +```java +/** + * 用户实体 + * + * @author AI Assistant + * @since 1.0.0 + */ +@Data +@Entity +@Table(name = "t_user") +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 用户名 + */ + @Column(nullable = false, length = 50) + private String username; + + /** + * 邮箱 + */ + @Column(nullable = false, unique = true) + private String email; + + /** + * 状态:0-禁用,1-启用 + */ + private Integer status; + + /** + * 创建时间 + */ + @CreatedDate + private LocalDateTime createdAt; + + /** + * 更新时间 + */ + @LastModifiedDate + private LocalDateTime updatedAt; +} +``` + +#### 服务类 +```java +/** + * 用户服务实现 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + /** + * 根据ID获取用户 + * + * @param id 用户ID + * @return 用户信息 + * @throws BusinessException 用户不存在时抛出 + */ + @Override + @Transactional(readOnly = true) + public UserVO getUserById(Long id) { + log.info("查询用户信息,ID:{}", id); + + User user = userRepository.findById(id) + .orElseThrow(() -> new BusinessException("用户不存在:" + id)); + + return convertToVO(user); + } + + /** + * 创建用户 + * + * @param request 创建请求 + * @return 用户ID + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long createUser(UserCreateRequest request) { + log.info("创建用户,用户名:{}", request.getUsername()); + + // 检查用户名是否存在 + if (userRepository.existsByUsername(request.getUsername())) { + throw new BusinessException("用户名已存在"); + } + + // 创建用户实体 + User user = new User(); + user.setUsername(request.getUsername()); + user.setEmail(request.getEmail()); + user.setPassword(passwordEncoder.encode(request.getPassword())); + user.setStatus(1); + + // 保存并返回ID + User saved = userRepository.save(user); + log.info("用户创建成功,ID:{}", saved.getId()); + + return saved.getId(); + } +} +``` + +### 5.3 异常处理 + +#### 自定义异常体系 +```java +/** + * 基础业务异常 + */ +@Getter +public class BusinessException extends RuntimeException { + + private final String code; + private final String message; + + public BusinessException(String message) { + super(message); + this.code = "BUSINESS_ERROR"; + this.message = message; + } + + public BusinessException(String code, String message) { + super(message); + this.code = code; + this.message = message; + } +} + +/** + * 全局异常处理器 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理业务异常 + */ + @ExceptionHandler(BusinessException.class) + public Result handleBusinessException(BusinessException e) { + log.warn("业务异常:{}", e.getMessage()); + return Result.fail(e.getCode(), e.getMessage()); + } + + /** + * 处理参数校验异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result handleValidationException(MethodArgumentNotValidException e) { + String message = e.getBindingResult().getFieldErrors().stream() + .map(error -> error.getField() + ": " + error.getDefaultMessage()) + .collect(Collectors.joining(", ")); + log.warn("参数校验失败:{}", message); + return Result.fail("VALIDATION_ERROR", message); + } + + /** + * 处理未知异常 + */ + @ExceptionHandler(Exception.class) + public Result handleException(Exception e) { + log.error("系统异常:", e); + return Result.fail("SYSTEM_ERROR", "系统繁忙,请稍后重试"); + } +} +``` + +### 5.4 Controller 规范 + +```java +/** + * 用户管理接口 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/users") +@RequiredArgsConstructor +@Tag(name = "用户管理", description = "用户相关接口") +public class UserController { + + private final UserService userService; + + /** + * 获取用户详情 + * + * @param id 用户ID + * @return 用户信息 + */ + @GetMapping("/{id}") + @Operation(summary = "获取用户详情") + public Result getUser(@PathVariable Long id) { + return Result.success(userService.getUserById(id)); + } + + /** + * 获取用户列表 + * + * @param query 查询条件 + * @return 用户列表 + */ + @GetMapping + @Operation(summary = "获取用户列表") + public Result> listUsers(@Valid UserQuery query) { + return Result.success(userService.listUsers(query)); + } + + /** + * 创建用户 + * + * @param request 创建请求 + * @return 用户ID + */ + @PostMapping + @Operation(summary = "创建用户") + public Result createUser(@Valid @RequestBody UserCreateRequest request) { + return Result.success(userService.createUser(request)); + } + + /** + * 更新用户 + * + * @param id 用户ID + * @param request 更新请求 + * @return 操作结果 + */ + @PutMapping("/{id}") + @Operation(summary = "更新用户") + public Result updateUser( + @PathVariable Long id, + @Valid @RequestBody UserUpdateRequest request) { + userService.updateUser(id, request); + return Result.success(); + } + + /** + * 删除用户 + * + * @param id 用户ID + * @return 操作结果 + */ + @DeleteMapping("/{id}") + @Operation(summary = "删除用户") + public Result deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + return Result.success(); + } +} +``` + +### 5.5 统一响应格式 + +```java +/** + * 统一响应结果 + * + * @param 数据类型 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Result implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + private String code; + + /** + * 消息 + */ + private String message; + + /** + * 数据 + */ + private T data; + + /** + * 时间戳 + */ + private long timestamp = System.currentTimeMillis(); + + /** + * 成功响应 + */ + public static Result success(T data) { + Result result = new Result<>(); + result.setCode("SUCCESS"); + result.setMessage("操作成功"); + result.setData(data); + return result; + } + + /** + * 成功响应(无数据) + */ + public static Result success() { + return success(null); + } + + /** + * 失败响应 + */ + public static Result fail(String code, String message) { + Result result = new Result<>(); + result.setCode(code); + result.setMessage(message); + return result; + } +} +``` + +--- + +# 第三部分:框架特定规范 + +## 6. React 规范 + +### 6.1 组件设计原则 + +#### 函数组件 + TypeScript +```tsx +/** + * 用户卡片组件 + * + * @description 展示用户基本信息的卡片组件 + */ +interface UserCardProps { + /** 用户信息 */ + user: User; + /** 是否显示详情 */ + showDetails?: boolean; + /** 编辑回调 */ + onEdit?: (user: User) => void; + /** 删除回调 */ + onDelete?: (userId: string) => void; + /** 子元素 */ + children?: React.ReactNode; +} + +const UserCard: React.FC = ({ + user, + showDetails = false, + onEdit, + onDelete, + children +}) => { + // 组件逻辑 + return ( +
+ {/* 组件内容 */} +
+ ); +}; + +export default UserCard; +``` + +#### 组件文件结构 +``` +components/ +└── UserCard/ + ├── index.tsx # 组件主文件 + ├── UserCard.tsx # 组件实现(可选,复杂组件时分离) + ├── UserCard.module.css # 样式文件(CSS Modules) + ├── UserCard.test.tsx # 测试文件 + ├── types.ts # 类型定义(复杂时分离) + └── hooks.ts # 组件专用 Hooks(可选) +``` + +### 6.2 Hooks 使用规范 + +#### Hooks 使用顺序 +```tsx +function UserProfile({ userId }: { userId: string }) { + // 1. useState - 状态定义 + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 2. useRef - 引用 + const containerRef = useRef(null); + + // 3. useContext - 上下文 + const { theme } = useContext(ThemeContext); + + // 4. 自定义 Hooks + const { data, isLoading } = useQuery(['user', userId], fetchUser); + + // 5. useCallback - 缓存回调 + const handleEdit = useCallback(() => { + // 处理编辑 + }, [userId]); + + // 6. useMemo - 缓存计算结果 + const displayName = useMemo(() => { + return user ? `${user.firstName} ${user.lastName}` : ''; + }, [user]); + + // 7. useEffect - 副作用 + useEffect(() => { + // 副作用逻辑 + return () => { + // 清理函数 + }; + }, [userId]); + + // 8. 条件渲染和返回 + if (loading) return ; + if (error) return ; + + return
{/* 内容 */}
; +} +``` + +#### 自定义 Hook 规范 +```tsx +/** + * 用户数据获取 Hook + * + * @param userId 用户ID + * @returns 用户数据、加载状态、错误信息 + */ +function useUser(userId: string) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + async function fetchUser() { + try { + setLoading(true); + setError(null); + const data = await userApi.getUser(userId); + if (!cancelled) { + setUser(data); + } + } catch (err) { + if (!cancelled) { + setError(err instanceof Error ? err : new Error('未知错误')); + } + } finally { + if (!cancelled) { + setLoading(false); + } + } + } + + fetchUser(); + + // 清理函数:防止组件卸载后设置状态 + return () => { + cancelled = true; + }; + }, [userId]); + + return { user, loading, error }; +} +``` + +### 6.3 状态管理 + +#### 本地状态 vs 全局状态 +```tsx +// ✅ 本地状态:组件内部使用,不需要共享 +const [isOpen, setIsOpen] = useState(false); +const [inputValue, setInputValue] = useState(''); + +// ✅ 全局状态:多个组件共享,使用状态管理库 +// 例如:用户信息、主题设置、购物车等 +``` + +#### 使用 Zustand(推荐) +```tsx +import { create } from 'zustand'; + +/** + * 用户状态 Store + */ +interface UserStore { + user: User | null; + isAuthenticated: boolean; + login: (user: User) => void; + logout: () => void; + updateUser: (updates: Partial) => void; +} + +const useUserStore = create((set) => ({ + user: null, + isAuthenticated: false, + + login: (user) => set({ user, isAuthenticated: true }), + + logout: () => set({ user: null, isAuthenticated: false }), + + updateUser: (updates) => set((state) => ({ + user: state.user ? { ...state.user, ...updates } : null + })) +})); + +// 使用 +function UserProfile() { + const { user, logout } = useUserStore(); + // ... +} +``` + +### 6.4 性能优化 + +```tsx +// ✅ 使用 React.memo 避免不必要的重渲染 +const UserCard = React.memo(function UserCard({ user }: UserCardProps) { + return
{user.name}
; +}); + +// ✅ 使用 useCallback 缓存回调函数 +const handleClick = useCallback((id: string) => { + // 处理点击 +}, [/* 依赖项 */]); + +// ✅ 使用 useMemo 缓存计算结果 +const sortedItems = useMemo(() => { + return [...items].sort((a, b) => a.name.localeCompare(b.name)); +}, [items]); + +// ✅ 列表渲染使用稳定的 key +{items.map((item) => ( + // ✅ 使用唯一 ID + // // ❌ 避免使用索引 +))} + +// ✅ 懒加载组件 +const HeavyComponent = React.lazy(() => import('./HeavyComponent')); + +function App() { + return ( + }> + + + ); +} +``` + +--- + +## 7. Vue 规范 + +### 7.1 组件设计(Vue 3 Composition API) + +#### 单文件组件结构 +```vue + + + + + +``` + +### 7.2 组合式函数(Composables) + +```typescript +// composables/useUser.ts +import { ref, computed, watch } from 'vue'; +import type { Ref } from 'vue'; +import type { User } from '@/types'; +import { userApi } from '@/api'; + +/** + * 用户数据组合式函数 + * + * @param userId 用户ID(响应式引用) + * @returns 用户数据、加载状态、错误信息、刷新方法 + */ +export function useUser(userId: Ref | string) { + const user = ref(null); + const loading = ref(false); + const error = ref(null); + + // 获取用户数据 + async function fetchUser(id: string) { + loading.value = true; + error.value = null; + + try { + user.value = await userApi.getUser(id); + } catch (err) { + error.value = err instanceof Error ? err : new Error('获取用户失败'); + user.value = null; + } finally { + loading.value = false; + } + } + + // 刷新数据 + function refresh() { + const id = typeof userId === 'string' ? userId : userId.value; + return fetchUser(id); + } + + // 计算属性 + const displayName = computed(() => { + if (!user.value) return ''; + return `${user.value.firstName} ${user.value.lastName}`; + }); + + // 侦听 userId 变化 + if (typeof userId !== 'string') { + watch(userId, (newId) => { + if (newId) { + fetchUser(newId); + } + }, { immediate: true }); + } else { + // 立即获取数据 + fetchUser(userId); + } + + return { + user, + loading, + error, + displayName, + refresh + }; +} +``` + +### 7.3 Pinia 状态管理 + +```typescript +// stores/user.ts +import { defineStore } from 'pinia'; +import type { User } from '@/types'; +import { userApi } from '@/api'; + +/** + * 用户状态 Store + */ +export const useUserStore = defineStore('user', { + // 状态 + state: () => ({ + currentUser: null as User | null, + isAuthenticated: false, + loading: false + }), + + // 计算属性 + getters: { + /** 用户显示名称 */ + displayName(): string { + if (!this.currentUser) return '游客'; + return `${this.currentUser.firstName} ${this.currentUser.lastName}`; + }, + + /** 是否是管理员 */ + isAdmin(): boolean { + return this.currentUser?.role === 'admin'; + } + }, + + // 方法 + actions: { + /** + * 用户登录 + */ + async login(credentials: { username: string; password: string }) { + this.loading = true; + try { + const user = await userApi.login(credentials); + this.currentUser = user; + this.isAuthenticated = true; + } finally { + this.loading = false; + } + }, + + /** + * 用户登出 + */ + logout() { + this.currentUser = null; + this.isAuthenticated = false; + }, + + /** + * 更新用户信息 + */ + updateUser(updates: Partial) { + if (this.currentUser) { + this.currentUser = { ...this.currentUser, ...updates }; + } + } + } +}); + +// 使用 Setup Store 语法(更灵活) +export const useUserStore2 = defineStore('user', () => { + // 状态 + const currentUser = ref(null); + const isAuthenticated = ref(false); + + // 计算属性 + const displayName = computed(() => { + return currentUser.value?.name ?? '游客'; + }); + + // 方法 + async function login(credentials: LoginCredentials) { + const user = await userApi.login(credentials); + currentUser.value = user; + isAuthenticated.value = true; + } + + function logout() { + currentUser.value = null; + isAuthenticated.value = false; + } + + return { + currentUser, + isAuthenticated, + displayName, + login, + logout + }; +}); +``` + +--- + +# 第四部分:工程化规范 + +## 8. Git 提交规范 + +### 8.1 提交信息格式 + +``` +(): + + + +