开发规范
本文档定义 youlai-aspnet 后端项目的开发约定,参考微软官方编码规范及业界最佳实践。
命名规范
命名空间命名
| 规则 | 示例 | 说明 |
|---|---|---|
| PascalCase | Youlai.Application.Services | ✅ 正确 |
| 单词间无分隔 | Youlai.ApplicationService | ❌ 错误 |
命名空间结构:
Youlai
├── Api/ # Web API 层
│ ├── Controllers/ # 控制器
│ ├── Middleware/ # 中间件
│ └── Security/ # 安全配置
├── Application/ # 应用层
│ ├── Common/ # 公共组件
│ ├── System/ # 系统模块
│ └── Auth/ # 认证模块
├── Domain/ # 领域层
│ └── Entities/ # 实体类
└── Infrastructure/ # 基础设施层
├── Persistence/ # 持久化
└── Services/ # 基础服务类命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 控制器 | XxxController | UsersController |
| 服务接口 | IXxxService | IUserService |
| 服务实现 | XxxService | UserService |
| 实体类 | Xxx | SysUser |
| DTO | XxxDto | UserPageDto |
| 表单对象 | XxxForm | UserForm |
| 查询对象 | XxxQuery | UserQuery |
| 枚举类 | Xxx | DataScope |
| 常量类 | XxxConstants | SecurityConstants |
| 配置类 | XxxOptions | JwtOptions |
| 异常类 | XxxException | BusinessException |
方法命名
查询方法
| 方法前缀 | 说明 | 示例 |
|---|---|---|
Get | 单个对象查询 | GetUserByIdAsync()、GetUserFormAsync() |
List | 列表查询 | ListUsersAsync()、ListUserOptionsAsync() |
GetPage | 分页查询 | GetUserPageAsync() |
Exists | 存在性判断 | ExistsByUsernameAsync() |
csharp
// ✅ 推荐
Task<User?> GetUserByIdAsync(long id);
Task<List<User>> ListUsersAsync(UserQuery query);
Task<PageResult<UserPageDto>> GetUserPageAsync(UserQuery query);
Task<bool> ExistsByUsernameAsync(string username);
// ❌ 不推荐
Task<User> SelectUser(long id); // 应使用 Get
Task<List<User>> QueryUsers(); // 应使用 List写操作方法
| 方法前缀 | 说明 | 示例 |
|---|---|---|
Save / Create | 新增 | SaveUserAsync() |
Update | 修改 | UpdateUserAsync() |
Delete | 删除 | DeleteUserAsync() |
csharp
// ✅ 推荐
Task<bool> SaveUserAsync(UserForm form);
Task<bool> UpdateUserAsync(long id, UserForm form);
Task<bool> DeleteUsersAsync(string ids);
// ❌ 不推荐
Task<bool> AddUser(); // 应使用 Save/Create
Task<bool> ModifyUser(); // 应使用 Update
Task<bool> RemoveUser(); // 应使用 Delete变量命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 成员变量 | _camelCase | private readonly IUserService _userService; |
| 局部变量 | camelCase | var user = await _userService.GetByIdAsync(id); |
| 常量 | PascalCase | public const string DefaultPassword = "123456"; |
| 布尔变量 | Is/Has/Can 前缀 | public bool IsRoot { get; set; } |
分层架构规约
调用链路
各层职责
| 层级 | 职责 | 禁止事项 |
|---|---|---|
| Api | 参数校验、调用 Service、组装返回结果 | ❌ 禁止直接访问 DbContext |
| Application | 业务逻辑、事务控制、数据组装 | ❌ 禁止跨业务调用 Repository |
| Infrastructure | 数据库访问、外部服务集成 | ❌ 禁止包含业务逻辑 |
Service 调用规范
核心原则:Service 只能调用自己业务域的 Repository,禁止跨业务调用。
csharp
// ✅ 正确:UserService 调用 IUserRepository
public class UserService : IUserService
{
private readonly IUserRepository _userRepository; // 自己业务域
private readonly IUserRoleService _userRoleService; // 通过 Service 交互
public async Task<User?> GetUserByIdAsync(long id)
{
return await _userRepository.FindByIdAsync(id);
}
}
// ❌ 错误:UserService 直接调用 IRoleRepository
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
private readonly IRoleRepository _roleRepository; // ❌ 禁止跨业务调用
public async Task<User?> GetUserWithRolesAsync(long id)
{
var user = await _userRepository.FindByIdAsync(id);
var roles = await _roleRepository.ListByUserIdAsync(id); // ❌ 错误
return user;
}
}
// ✅ 正确:通过 IRoleService 获取角色
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
private readonly IRoleService _roleService; // ✅ 通过 Service 交互
public async Task<User?> GetUserWithRolesAsync(long id)
{
var user = await _userRepository.FindByIdAsync(id);
var roles = await _roleService.ListRolesByUserIdAsync(id); // ✅ 正确
return user;
}
}RESTful API 规范
URL 设计
| 规则 | 示例 | 说明 |
|---|---|---|
| 使用名词复数 | /api/v1/users | ✅ 正确 |
| 避免动词 | /api/v1/getUsers | ❌ 错误 |
| 层级不超过 3 层 | /api/v1/users/{id}/roles | ✅ 正确 |
HTTP 方法映射
| HTTP 方法 | 操作 | URL 示例 |
|---|---|---|
GET | 列表查询 | GET /api/v1/users |
GET | 单个查询 | GET /api/v1/users/{id} |
POST | 新增 | POST /api/v1/users |
PUT | 全量修改 | PUT /api/v1/users/{id} |
PATCH | 部分修改 | PATCH /api/v1/users/{id}/status |
DELETE | 删除 | DELETE /api/v1/users/{id} |
错误码规范
常用错误码
| 错误码 | 说明 | 场景 |
|---|---|---|
00000 | 成功 | 正常执行 |
A0001 | 用户端错误 | 一级宏观 |
A0200 | 用户登录异常 | 登录失败 |
A0230 | Token 无效或过期 | 认证失败 |
A0301 | 访问未授权 | 权限不足 |
A0400 | 请求参数错误 | 参数校验失败 |
B0001 | 系统执行出错 | 一级宏观 |
日志规范
日志级别
| 级别 | 场景 |
|---|---|
Error | 错误信息,需要立即处理 |
Warning | 警告信息,可能存在问题 |
Information | 关键流程信息 |
Debug | 调试信息 |
日志格式
csharp
// ✅ 推荐:使用结构化日志
_logger.LogInformation("用户登录成功:UserId={UserId}, Username={Username}", userId, username);
_logger.LogError(e, "用户登录失败:Username={Username}", username);
// ❌ 不推荐:字符串拼接
_logger.LogInformation($"用户登录成功:UserId={userId}");最佳实践
避免魔法值
csharp
// ❌ 不推荐
if (user.Status == 1) { }
// ✅ 推荐:使用枚举
if (user.Status == UserStatus.Enabled) { }
// ✅ 推荐:使用常量
public static class UserStatus
{
public const int Enabled = 1;
public const int Disabled = 0;
}集合判空
csharp
// ❌ 不推荐
if (list.Count > 0) { }
// ✅ 推荐
if (list.Count > 0) { }
// 或使用扩展方法
if (list.Any()) { }字符串判空
csharp
// ❌ 不推荐
if (str != null && str.Length > 0) { }
// ✅ 推荐
if (!string.IsNullOrEmpty(str)) { }
// 或更严格(排除空白字符)
if (!string.IsNullOrWhiteSpace(str)) { }避免 NullReferenceException
csharp
// ❌ 不推荐
return user.Username.Equals("admin");
// ✅ 推荐
return user.Username?.Equals("admin") == true;
// ✅ 推荐:使用模式匹配
return user.Username is "admin";相关文件
| 文件 | 说明 |
|---|---|
Youlai.Application/Common/Results/ResultCode.cs | 错误码枚举定义 |
Youlai.Api/Middleware/ExceptionMiddleware.cs | 全局异常处理 |
Youlai.Application/Common/Models/PageResult.cs | 分页结果基类 |
