项目名称: 次世代多用户内容社区平台 / Corasta
文档版本: V1.0
最后更新: 2024
本文档旨在统一项目代码风格,提高代码可读性、可维护性和团队协作效率。所有团队成员在编写代码时都应遵循本规范。
- 后端 Java/Spring Boot 代码
- 前端 Vue 3/JavaScript 代码
- 数据库 SQL 脚本
- Git 提交信息
- 代码注释和文档
- 可读性优先: 代码应该易于理解,优先考虑可读性而非过度优化
- 一致性: 保持代码风格的一致性,遵循项目约定
- 简洁性: 避免不必要的复杂性,保持代码简洁
- 可维护性: 代码应该易于修改和扩展
- 使用有意义的名称,避免缩写(除非是广泛认知的缩写)
- 使用英文命名,避免拼音
- 名称应该清晰表达其用途
- 类名: 使用 PascalCase(大驼峰)
- 示例:
UserController、ArticleService
- 示例:
- 方法名、变量名: 使用 camelCase(小驼峰)
- 示例:
getUserById、articleList
- 示例:
- 常量名: 使用 UPPER_SNAKE_CASE(大写下划线)
- 示例:
MAX_FILE_SIZE、DEFAULT_PAGE_SIZE
- 示例:
- 包名、目录名: 使用小写字母,多个单词用点或连字符分隔
- 示例:
com.example.back.controller、user-center
- 示例:
项目根目录/
├── back/ # 后端项目
│ └── src/main/java/
│ └── com/example/back/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务层
│ ├── repository/ # 数据访问层
│ ├── entity/ # 实体类
│ ├── dto/ # 数据传输对象
│ ├── vo/ # 视图对象
│ ├── config/ # 配置类
│ ├── exception/ # 异常类
│ └── util/ # 工具类
│
└── front/ # 前端项目
└── src/
├── api/ # API 接口
├── components/ # 组件
├── views/ # 页面视图
├── router/ # 路由配置
├── stores/ # 状态管理
├── utils/ # 工具函数
└── assets/ # 静态资源
- Java 类文件: 与类名一致,使用 PascalCase
- 示例:
UserController.java、ArticleService.java
- 示例:
- Vue 组件文件: 使用 PascalCase
- 示例:
UserProfile.vue、ArticleList.vue
- 示例:
- 工具文件: 使用 camelCase
- 示例:
dateUtils.js、apiClient.js
- 示例:
- 使用 4 个空格进行缩进(不使用 Tab)
- 每行代码长度不超过 120 个字符
- 运算符前后添加空格
- 方法参数之间使用逗号和空格分隔
// ✅ 正确
public User getUserById(Long id, boolean includeDeleted) {
return userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("用户不存在"));
}
// ❌ 错误
public User getUserById(Long id,boolean includeDeleted){
return userRepository.findById(id).orElseThrow(()->new NotFoundException("用户不存在"));
}- 使用 K&R 风格(左大括号不换行)
- 单行语句也使用大括号
// ✅ 正确
if (condition) {
doSomething();
} else {
doOtherThing();
}
// ❌ 错误
if (condition)
doSomething();
else
doOtherThing();- 类成员之间使用一个空行分隔
- 方法之间使用一个空行分隔
- 逻辑块之间使用空行分隔以提高可读性
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUserById(Long id) {
// 实现
}
public User createUser(UserCreateDTO dto) {
// 实现
}
}- 使用名词或名词短语
- 避免使用缩写
- Controller 类以
Controller结尾 - Service 类以
Service结尾 - Repository 接口以
Repository结尾 - Entity 类使用名词,不使用后缀
// ✅ 正确
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
// ...
}
@Service
public class UserService {
// ...
}
public interface UserRepository extends JpaRepository<User, Long> {
// ...
}
@Entity
@Table(name = "users")
public class User {
// ...
}- Controller: 处理 HTTP 请求,参数验证,调用 Service
- Service: 业务逻辑处理
- Repository: 数据访问
- Entity: 数据库实体映射
- DTO: 数据传输对象(请求/响应)
- VO: 视图对象(返回给前端的数据)
// Controller - 只负责请求处理
@RestController
@RequestMapping("/api/v1/articles")
public class ArticleController {
private final ArticleService articleService;
@GetMapping("/{id}")
public ResponseEntity<ArticleVO> getArticle(@PathVariable Long id) {
ArticleVO article = articleService.getArticleById(id);
return ResponseEntity.ok(article);
}
}
// Service - 业务逻辑
@Service
@Transactional
public class ArticleService {
private final ArticleRepository articleRepository;
public ArticleVO getArticleById(Long id) {
Article article = articleRepository.findById(id)
.orElseThrow(() -> new NotFoundException("文章不存在"));
// 增加阅读量
article.setViewCount(article.getViewCount() + 1);
articleRepository.save(article);
return convertToVO(article);
}
}- 使用动词或动词短语
- 布尔返回值的方法使用
is、has、can等前缀 - 获取方法使用
get前缀 - 设置方法使用
set前缀 - 查询方法使用
find、query前缀
// ✅ 正确
public User getUserById(Long id) { }
public List<User> findUsersByStatus(Integer status) { }
public boolean isUserExists(String username) { }
public boolean hasPermission(Long userId, String permission) { }
public void updateUser(UserUpdateDTO dto) { }
public void deleteUser(Long id) { }
// ❌ 错误
public User user(Long id) { }
public List<User> users(Integer status) { }
public boolean check(Long userId) { }- 方法参数不超过 5 个,超过时使用对象封装
- 参数顺序:必填参数在前,可选参数在后
- 使用有意义的参数名
// ✅ 正确 - 使用 DTO 封装多个参数
public PageResult<ArticleVO> getArticleList(ArticleQueryDTO queryDTO) {
// ...
}
// ❌ 错误 - 参数过多
public PageResult<ArticleVO> getArticleList(
Integer page, Integer pageSize, Long categoryId,
Long tagId, String keyword, String sort) {
// ...
}- 单个方法代码行数不超过 50 行
- 超过时拆分为多个小方法
// ✅ 正确 - 方法简洁
public ArticleVO createArticle(ArticleCreateDTO dto) {
validateArticleDTO(dto);
Article article = convertToEntity(dto);
article = saveArticle(article);
return convertToVO(article);
}
private void validateArticleDTO(ArticleCreateDTO dto) {
// 验证逻辑
}
private Article convertToEntity(ArticleCreateDTO dto) {
// 转换逻辑
}- 使用 camelCase
- 使用有意义的名称
- 避免单字母变量(循环变量除外)
// ✅ 正确
List<Article> articleList = articleRepository.findAll();
Article article = articleList.get(0);
for (Article item : articleList) {
// ...
}
// ❌ 错误
List<Article> list = articleRepository.findAll();
Article a = list.get(0);
for (Article x : list) {
// ...
}- 使用
static final修饰 - 使用 UPPER_SNAKE_CASE 命名
- 在类顶部或常量类中定义
// ✅ 正确
public class Constants {
public static final int DEFAULT_PAGE_SIZE = 20;
public static final int MAX_PAGE_SIZE = 100;
public static final String DEFAULT_SORT = "latest";
}
// 或在类中定义
public class ArticleService {
private static final int MAX_TITLE_LENGTH = 100;
private static final int MAX_CONTENT_LENGTH = 50000;
}- 创建业务异常类,继承
RuntimeException - 异常类以
Exception结尾 - 提供有意义的错误消息
// ✅ 正确
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
}- 使用全局异常处理器
- 不要捕获异常后忽略(至少记录日志)
- 不要使用空的 catch 块
// ✅ 正确
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFoundException(NotFoundException e) {
ErrorResponse error = new ErrorResponse(404, e.getMessage());
return ResponseEntity.status(404).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("服务器错误", e);
ErrorResponse error = new ErrorResponse(500, "服务器内部错误");
return ResponseEntity.status(500).body(error);
}
}
// ❌ 错误
try {
doSomething();
} catch (Exception e) {
// 空 catch 块
}- Controller 使用
@RestController或@Controller - Service 使用
@Service - Repository 使用
@Repository或继承JpaRepository - 配置类使用
@Configuration
// ✅ 正确
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserVO> getUser(@PathVariable Long id) {
// ...
}
}
@Service
@Transactional
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
}- 使用
@Valid和 Bean Validation 注解 - DTO 类中使用验证注解
// ✅ 正确
@PostMapping("/register")
public ResponseEntity<UserVO> register(@Valid @RequestBody UserRegisterDTO dto) {
// ...
}
public class UserRegisterDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度必须在4-20字符之间")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 8, max = 32, message = "密码长度必须在8-32字符之间")
private String password;
}- 使用 JavaDoc 格式
- 说明类的用途和主要功能
/**
* 用户服务类
*
* <p>提供用户相关的业务逻辑处理,包括:
* <ul>
* <li>用户注册和登录</li>
* <li>用户信息管理</li>
* <li>用户权限验证</li>
* </ul>
*
* @author YourName
* @since 1.0
*/
@Service
public class UserService {
// ...
}- 公共方法必须添加 JavaDoc 注释
- 说明方法用途、参数、返回值、异常
/**
* 根据ID获取用户信息
*
* @param id 用户ID
* @return 用户信息,如果不存在则返回null
* @throws NotFoundException 当用户不存在时抛出
*/
public UserVO getUserById(Long id) {
// ...
}- 解释"为什么"而不是"做什么"
- 避免显而易见的注释
// ✅ 正确 - 解释为什么
// 使用软删除,保留历史数据用于数据分析
user.setDeletedAt(LocalDateTime.now());
// ❌ 错误 - 显而易见的注释
// 设置用户ID
user.setId(id);- 继承
JpaRepository<Entity, ID> - 自定义查询方法使用有意义的命名
- 复杂查询使用
@Query注解
// ✅ 正确
@Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
// 使用方法命名查询
List<Article> findByUserIdAndStatus(Long userId, Integer status);
// 使用 @Query 注解
@Query("SELECT a FROM Article a WHERE a.title LIKE %:keyword% OR a.content LIKE %:keyword%")
Page<Article> searchArticles(@Param("keyword") String keyword, Pageable pageable);
// 使用原生SQL(谨慎使用)
@Query(value = "SELECT * FROM articles WHERE status = :status", nativeQuery = true)
List<Article> findByStatusNative(@Param("status") Integer status);
}- Service 层方法使用
@Transactional - 只读操作使用
@Transactional(readOnly = true)
// ✅ 正确
@Service
@Transactional
public class ArticleService {
@Transactional(readOnly = true)
public ArticleVO getArticleById(Long id) {
// 只读操作
}
public ArticleVO createArticle(ArticleCreateDTO dto) {
// 写操作,自动开启事务
}
}- 使用 2 个空格进行缩进
- 每行代码长度不超过 100 个字符
- 运算符前后添加空格
- 对象属性之间使用逗号和空格
// ✅ 正确
const user = {
id: 1,
username: 'testuser',
nickname: '测试用户'
}
const result = a + b * c
// ❌ 错误
const user={id:1,username:'testuser',nickname:'测试用户'}
const result=a+b*c- 使用 单引号(
')作为字符串引号 - 模板字符串使用反引号(
`)
// ✅ 正确
const message = 'Hello World'
const template = `Hello ${name}`
// ❌ 错误
const message = "Hello World"- 不使用分号(遵循 Vue 3 官方风格)
- 仅在必要时使用(如以
[、(开头的行)
// ✅ 正确
const name = 'Vue'
const list = [1, 2, 3]
;[1, 2, 3].forEach(item => console.log(item))
// ❌ 错误
const name = 'Vue';
const list = [1, 2, 3];- 组件文件名使用 PascalCase
- 组件名与文件名保持一致
<!-- ✅ 正确 - UserProfile.vue -->
<template>
<div class="user-profile">
<!-- ... -->
</div>
</template>
<script setup>
// 组件逻辑
</script>- 按照
<template>、<script>、<style>的顺序 - 使用
<script setup>语法
<!-- ✅ 正确 -->
<template>
<div class="article-list">
<!-- 模板内容 -->
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import ArticleCard from '@/components/ArticleCard.vue'
// 组件逻辑
const articles = ref([])
const isLoading = ref(false)
const articleCount = computed(() => articles.value.length)
</script>
<style scoped>
.article-list {
/* 样式 */
}
</style>- 使用
defineProps定义 props - 提供类型和默认值
- 使用
withDefaults设置默认值
<script setup>
// ✅ 正确
interface Props {
userId: number
showAvatar?: boolean
maxLength?: number
}
const props = withDefaults(defineProps<Props>(), {
showAvatar: true,
maxLength: 100
})
</script>- 使用
defineEmits定义事件 - 提供事件类型定义
<script setup>
// ✅ 正确
interface Emits {
(e: 'update', value: string): void
(e: 'delete', id: number): void
}
const emit = defineEmits<Emits>()
const handleUpdate = (value: string) => {
emit('update', value)
}
</script>- 使用 camelCase
- 布尔值使用
is、has、can前缀 - 事件处理函数使用
handle前缀
// ✅ 正确
const userName = 'testuser'
const articleList = []
const isLoading = false
const hasPermission = true
const canEdit = false
function handleClick() {}
function handleSubmit() {}
function getUserById(id) {}- 使用 UPPER_SNAKE_CASE
- 在文件顶部或常量文件中定义
// ✅ 正确
const API_BASE_URL = 'https://api.example.com'
const DEFAULT_PAGE_SIZE = 20
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB- 组件引用使用 PascalCase
<script setup>
import UserProfile from '@/components/UserProfile.vue'
import ArticleCard from '@/components/ArticleCard.vue'
</script>
<template>
<UserProfile :user="user" />
<ArticleCard v-for="article in articles" :key="article.id" :article="article" />
</template>- 按模块组织 API 文件
- 使用统一的 API 客户端
// ✅ 正确 - api/user.js
import request from '@/utils/request'
export function getUserById(id) {
return request.get(`/api/v1/users/${id}`)
}
export function updateUser(id, data) {
return request.put(`/api/v1/users/${id}`, data)
}
export function deleteUser(id) {
return request.delete(`/api/v1/users/${id}`)
}- 使用统一的错误处理
- 在组件中处理错误并提示用户
// ✅ 正确
import { ElMessage } from 'element-plus'
import { getUserById } from '@/api/user'
const fetchUser = async (id) => {
try {
isLoading.value = true
const response = await getUserById(id)
user.value = response.data
} catch (error) {
ElMessage.error(error.message || '获取用户信息失败')
} finally {
isLoading.value = false
}
}- 使用
defineStore定义 store - Store 名称使用 camelCase
// ✅ 正确 - stores/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { getUserInfo } from '@/api/user'
export const useUserStore = defineStore('user', () => {
const user = ref(null)
const token = ref(localStorage.getItem('token'))
const isLoggedIn = computed(() => !!token.value)
async function fetchUserInfo() {
const response = await getUserInfo()
user.value = response.data
}
function setToken(newToken) {
token.value = newToken
localStorage.setItem('token', newToken)
}
function logout() {
user.value = null
token.value = null
localStorage.removeItem('token')
}
return {
user,
token,
isLoggedIn,
fetchUserInfo,
setToken,
logout
}
})- 使用有意义的路径
- 使用 kebab-case 命名路由
- 提供路由元信息
// ✅ 正确 - router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'home',
component: () => import('@/views/Home.vue'),
meta: {
title: '首页',
requiresAuth: false
}
},
{
path: '/articles/:id',
name: 'article-detail',
component: () => import('@/views/ArticleDetail.vue'),
meta: {
title: '文章详情',
requiresAuth: false
}
},
{
path: '/dashboard',
name: 'dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: {
title: '管理后台',
requiresAuth: true
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router- 使用 BEM 命名法或 kebab-case
- 使用 scoped 样式避免污染
<template>
<div class="article-list">
<div class="article-list__header">
<h2 class="article-list__title">文章列表</h2>
</div>
<div class="article-list__content">
<ArticleCard
v-for="article in articles"
:key="article.id"
:article="article"
class="article-list__item"
/>
</div>
</div>
</template>
<style scoped>
.article-list {
padding: 20px;
}
.article-list__header {
margin-bottom: 20px;
}
.article-list__title {
font-size: 24px;
font-weight: bold;
}
.article-list__item {
margin-bottom: 16px;
}
</style>- 按功能模块组织样式
- 使用 CSS 变量定义主题色
/* ✅ 正确 */
:root {
--primary-color: #409eff;
--success-color: #67c23a;
--warning-color: #e6a23c;
--danger-color: #f56c6c;
--text-color: #303133;
--border-color: #dcdfe6;
}
.article-card {
background: #fff;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 16px;
}- 复杂函数添加 JSDoc 注释
- 说明参数、返回值和用途
/**
* 格式化日期
*
* @param {Date|string|number} date - 日期对象、字符串或时间戳
* @param {string} format - 格式字符串,默认 'YYYY-MM-DD'
* @returns {string} 格式化后的日期字符串
*
* @example
* formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
* // => '2024-01-01 12:00:00'
*/
export function formatDate(date, format = 'YYYY-MM-DD') {
// 实现
}- 复杂组件添加注释说明
- 说明 props 和 emits
<script setup>
/**
* 文章卡片组件
*
* 用于展示文章的基本信息,包括标题、摘要、作者等
*
* @component ArticleCard
*/
interface Props {
/** 文章ID */
id: number
/** 文章标题 */
title: string
/** 文章摘要 */
summary?: string
/** 是否显示作者信息 */
showAuthor?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showAuthor: true
})
</script>- 关键字使用大写
- 表名、字段名使用小写
- 使用 2 个空格缩进
- 每个字段定义占一行
-- ✅ 正确
CREATE TABLE users (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(20) NOT NULL COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_users_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- ❌ 错误
create table users(id bigint not null auto_increment,username varchar(20) not null,primary key(id));- 表必须添加 COMMENT
- 字段必须添加 COMMENT
- 复杂查询添加说明注释
-- ✅ 正确
CREATE TABLE articles (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT NOT NULL COMMENT '作者ID',
title VARCHAR(100) NOT NULL COMMENT '文章标题',
content LONGTEXT NOT NULL COMMENT '文章内容(Markdown)',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
INDEX idx_articles_user_id (user_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表';使用 Conventional Commits 规范:
<type>(<scope>): <subject>
<body>
<footer>
feat: 新功能fix: 修复 Bugdocs: 文档更新style: 代码格式调整(不影响功能)refactor: 代码重构perf: 性能优化test: 测试相关chore: 构建过程或辅助工具的变动
# ✅ 正确
feat(user): 添加用户注册功能
fix(article): 修复文章列表分页问题
docs(api): 更新API接口文档
style: 统一代码格式
refactor(auth): 重构认证逻辑
perf(search): 优化搜索性能
# ❌ 错误
更新代码
fix bug
添加功能main/master: 主分支develop: 开发分支feature/功能名称: 功能分支fix/问题描述: Bug 修复分支hotfix/问题描述: 紧急修复分支
# ✅ 正确
feature/user-register
feature/article-management
fix/article-pagination
hotfix/login-error
# ❌ 错误
feature1
fix1
new-feature- 解释"为什么",而不是"做什么"
- 避免显而易见的注释
- 复杂逻辑必须添加注释
- 公共 API 必须添加文档注释
// ✅ 正确 - 解释为什么
// 使用软删除而非物理删除,保留历史数据用于数据分析
user.setDeletedAt(LocalDateTime.now());
// ✅ 正确 - 解释复杂逻辑
// 使用二分查找优化性能,时间复杂度从 O(n) 降低到 O(log n)
int index = binarySearch(sortedList, target);
// ❌ 错误 - 显而易见的注释
// 设置用户ID
user.setId(id);
// ❌ 错误 - 注释与代码不一致
// 获取文章列表
List<User> users = userRepository.findAll();- 代码变更时同步更新相关文档
- API 变更时更新 API 文档
- 数据库变更时更新数据库设计文档
- Checkstyle: 代码风格检查
- SpotBugs: Bug 检测
- PMD: 代码质量分析
- ESLint: JavaScript 代码检查
- Prettier: 代码格式化
- Stylelint: CSS 代码检查
- 启用代码格式化(保存时自动格式化)
- 配置代码检查规则
- 使用代码模板
- 安装 ESLint、Prettier 插件
- 配置保存时自动格式化
- 使用代码片段
文档版本: V1.0
最后更新: 2024