# 编码规范
统一的编码规范是保证代码质量和可维护性的基础。本文档适用于所有 DarkM 项目参与者。
# 📚 参考标准
# 🔖 后端编码规范(C#)
# 1. 命名规范
# 1.1 命名风格
| 元素 | 风格 | 示例 |
|---|---|---|
| 命名空间 | Pascal | DarkM.Lib.Data |
| 类/结构 | Pascal | UserEntity, UserService |
| 接口 | Pascal + I 前缀 | IUserService, IRepository |
| 方法 | Pascal | GetUserById, CreateAsync |
| 属性 | Pascal | UserName, CreateTime |
| 字段 | Camel + _ 前缀 | _userRepository, _logger |
| 参数 | Camel | userId, userName |
| 局部变量 | Camel | userList, totalCount |
| 常量 | Pascal | MaxRetryCount, DefaultPageSize |
| 枚举类型 | Pascal | UserStatus, OrderType |
| 枚举值 | Pascal | Active, Inactive, Pending |
# 1.2 命名最佳实践
// ✅ 推荐
public class UserEntity : EntityBase
{
private readonly IUserRepository _userRepository;
public string UserName { get; set; }
public UserStatus Status { get; set; }
public const int MaxUserNameLength = 50;
}
// ❌ 不推荐
public class user_entity
{
private IUserRepository UserRepository; // 字段应该用_前缀
public string userName { get; set; } // 属性应该 Pascal 命名
public const int maxUserNameLength = 50; // 常量应该 Pascal 命名
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.3 命名原则
- 可读性优先:
ScreenX比ScreenHorizontally更易读 - 避免缩写:除非广泛接受(如 HTML、URL、ID)
- 使用复数:命名空间使用复数
System.Collections - 避免关键字冲突:不使用 C# 关键字作为标识符
- 语义清晰:
userList比list更明确
# 2. 代码组织
# 2.1 文件结构
// 1. using 语句(按命名空间分组)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DarkM.Lib.Data.Abstractions;
using DarkM.Lib.Utils.Core.Result;
// 2. 命名空间声明
namespace DarkM.Module.User.Application
{
// 3. 类定义
/// <summary>
/// 用户应用服务
/// </summary>
public class UserService : IUserService
{
// 4. 私有字段
private readonly IUserRepository _userRepository;
private readonly ILogger _logger;
// 5. 构造函数
public UserService(IUserRepository userRepository, ILogger logger)
{
_userRepository = userRepository;
_logger = logger;
}
// 6. 公共方法
public async Task<IResultModel> GetUserById(Guid id)
{
// 方法实现
}
// 7. 私有方法
private void ValidateUser(UserEntity user)
{
// 方法实现
}
// 8. 内部类
private class UserCache
{
// 类实现
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 2.2 空行使用
// ✅ 推荐:适当使用空行增强可读性
public class UserService
{
private readonly IUserRepository _userRepository;
private readonly ILogger _logger;
public UserService(IUserRepository userRepository, ILogger logger)
{
_userRepository = userRepository;
_logger = logger;
}
public async Task<IResultModel> GetUserById(Guid id)
{
var user = await _userRepository.GetAsync(id);
if (user == null)
return ResultModel.Failed("用户不存在");
return ResultModel.Success(user);
}
private void ValidateUser(UserEntity user)
{
Check.NotNull(user.Name, nameof(user.Name));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 3. 注释规范
# 3.1 XML 文档注释
/// <summary>
/// 用户应用服务
/// </summary>
public class UserService : IUserService
{
/// <summary>
/// 根据 ID 获取用户
/// </summary>
/// <param name="id">用户 ID</param>
/// <returns>用户信息</returns>
public async Task<IResultModel> GetUserById(Guid id)
{
// 实现
}
/// <summary>
/// 创建用户
/// </summary>
/// <param name="model">用户信息</param>
/// <returns>创建结果</returns>
/// <exception cref="ArgumentNullException">当用户名为空时抛出</exception>
public async Task<IResultModel> CreateUser(UserAddModel model)
{
// 实现
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 3.2 代码注释
// ✅ 推荐:解释为什么这样做
// 使用事务确保数据一致性
using var uow = _unitOfWorkManager.Begin();
try
{
await _userDbSet.InsertAsync(user);
await _logDbSet.InsertAsync(log);
uow.Commit();
}
catch
{
uow.Rollback();
throw;
}
// ❌ 不推荐:解释做了什么(代码已经很清楚了)
// 设置用户名称
user.Name = "张三";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4. 异常处理
# 4.1 使用 Check 进行参数验证
public async Task<IResultModel> CreateUser(UserAddModel model)
{
// ✅ 推荐
Check.NotNull(model, nameof(model), "用户信息不能为空");
Check.NotNull(model.UserName, nameof(model.UserName), "用户名不能为空");
// ❌ 不推荐
if (model == null)
throw new ArgumentNullException(nameof(model));
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 4.2 异常捕获
// ✅ 推荐:捕获特定异常
try
{
await _userRepository.AddAsync(user);
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "数据库更新失败");
return ResultModel.Failed("数据库操作失败");
}
// ❌ 不推荐:捕获所有异常
try
{
await _userRepository.AddAsync(user);
}
catch (Exception)
{
// 空的 catch 块
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5. 异步编程
# 5.1 异步方法命名
// ✅ 推荐:异步方法以 Async 结尾
public Task<UserEntity> GetUserByIdAsync(Guid id);
public Task<List<UserEntity>> GetAllUsersAsync();
// ❌ 不推荐
public Task<UserEntity> GetUserById(Guid id);
1
2
3
4
5
6
2
3
4
5
6
# 5.2 异步最佳实践
// ✅ 推荐
public async Task<IResultModel> GetUserAsync(Guid id)
{
var user = await _userRepository.GetAsync(id);
return ResultModel.Success(user);
}
// ❌ 不推荐:async void(除了事件处理程序)
public async void GetUserAsync(Guid id) { }
// ❌ 不推荐:Task.Result(可能导致死锁)
var user = _userRepository.GetAsync(id).Result;
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 6. LINQ 使用
// ✅ 推荐:链式调用
var activeUsers = _userRepository.Find()
.Where(u => u.Status == UserStatus.Active)
.OrderBy(u => u.CreateTime)
.Take(10)
.ToList();
// ✅ 推荐:查询表达式
var query = from u in _userRepository.Find()
where u.Status == UserStatus.Active
orderby u.CreateTime descending
select u;
// ❌ 不推荐:多次遍历
var users = _userRepository.Find().ToList();
var activeUsers = users.Where(u => u.Status == UserStatus.Active);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 🎨 前端编码规范(Vue.js)
# 1. 目录结构
src/
├── api/ # API 接口
│ ├── modules/ # 模块接口
│ │ ├── user.js
│ │ └── order.js
│ └── index.js
├── views/ # 页面组件
│ ├── user/
│ │ ├── list.vue
│ │ └── edit.vue
│ └── order/
├── components/ # 公共组件
│ ├── NmTable/
│ └── NmForm/
├── utils/ # 工具函数
│ ├── request.js
│ └── validate.js
├── store/ # Vuex 状态管理
│ ├── modules/
│ └── index.js
├── router/ # 路由配置
│ └── index.js
├── styles/ # 全局样式
│ ├── variables.scss
│ └── mixins.scss
└── App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 2. 命名规范
# 2.1 文件和目录
| 类型 | 风格 | 示例 |
|---|---|---|
| 组件文件 | Pascal | UserList.vue, NmTable.vue |
| 页面目录 | camel | user/, orderManage/ |
| API 文件 | camel | user.js, orderApi.js |
| 工具函数 | camel | validate.js, utils.js |
| 样式文件 | camel | variables.scss |
# 2.2 组件命名
// ✅ 推荐:多单词组件名
export default {
name: 'UserList',
// ...
}
// ❌ 不推荐:单单词组件名
export default {
name: 'List', // 可能与 HTML 标签冲突
// ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2.3 DarkM.UI 组件别名规则
使用 DarkM.UI 组件时,支持驼峰格式和连字符格式两种写法,系统推荐使用连字符格式:
<template>
<!-- 两种写法都支持 -->
<NmButton type="primary">按钮</NmButton>
<nm-button type="primary">按钮</nm-button> <!-- ✅ 推荐:连字符格式 -->
<NmDatePicker />
<nm-date-picker /> <!-- ✅ 推荐 -->
<NmFormDialog />
<nm-form-dialog /> <!-- ✅ 推荐 -->
</template>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
转换规则:驼峰命名的大写字母前添加 - 并转小写
NmButton→nm-buttonNmDatePicker→nm-date-pickerNmFormDialog→nm-form-dialog
# 3. 代码规范
# 3.1 组件结构
<template>
<div class="user-list">
<nm-table :data="list" :columns="columns">
<!-- 自定义列 -->
</nm-table>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'UserList',
// 组件选项顺序
components: { NmTable },
props: { },
data() {
return {
list: [],
columns: []
}
},
computed: {
...mapGetters(['userId'])
},
watch: { },
created() { },
mounted() { },
methods: {
async fetchList() {
const res = await this.$api.user.getList()
this.list = res.data
}
}
}
</script>
<style lang="scss" scoped>
.user-list {
padding: 20px;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 3.2 Props 定义
// ✅ 推荐:完整定义
export default {
props: {
userId: {
type: String,
required: true,
validator: value => value.length > 0
},
status: {
type: Number,
default: 0,
validator: value => [0, 1, 2].includes(value)
}
}
}
// ❌ 不推荐:简写
export default {
props: ['userId', 'status']
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4. 异步操作
// ✅ 推荐:async/await
export default {
methods: {
async fetchUser(id) {
try {
const res = await this.$api.user.getById(id)
this.user = res.data
} catch (error) {
this.$message.error('获取用户失败')
}
}
}
}
// ❌ 不推荐:Promise 链
export default {
methods: {
fetchUser(id) {
this.$api.user.getById(id)
.then(res => {
this.user = res.data
})
.catch(error => {
this.$message.error('获取用户失败')
})
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 5. 状态管理
// store/modules/user.js
export default {
namespaced: true,
state: {
userInfo: null,
token: ''
},
mutations: {
SET_USER_INFO(state, info) {
state.userInfo = info
},
SET_TOKEN(state, token) {
state.token = token
}
},
actions: {
async login({ commit }, credentials) {
const res = await loginApi(credentials)
commit('SET_TOKEN', res.data.token)
commit('SET_USER_INFO', res.data.userInfo)
}
},
getters: {
userId: state => state.userInfo?.id,
userName: state => state.userInfo?.name
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 📝 代码审查清单
# 后端检查项
- [ ] 命名符合 Pascal/Camel 规范
- [ ] 所有公共方法有 XML 注释
- [ ] 使用异步方法处理 I/O 操作
- [ ] 异常处理适当,不吞掉异常
- [ ] 使用 Check 进行参数验证
- [ ] 数据库操作使用事务
- [ ] 避免 N+1 查询问题
- [ ] 日志记录完整
# 前端检查项
- [ ] 组件使用多单词命名
- [ ] Props 有完整定义和验证
- [ ] 使用 async/await 处理异步
- [ ] 组件职责单一
- [ ] 避免直接修改 props
- [ ] 使用 Vuex 管理全局状态
- [ ] 样式使用 scoped
- [ ] 响应式设计考虑移动端
# 🔗 相关文档
最后更新: 2022-08-07