SOMS/rules.txt

396 lines
11 KiB
Plaintext
Raw Normal View History

# .NET 6.0 开发规范 (基于ASP.NET Boilerplate)
## 📁 项目结构
```
YunDa.SOMS/
├── YunDa.Application/
│ ├── YunDa.SOMS.Application/ # API服务层
│ └── YunDa.SOMS.DataTransferObject/ # DTO中间层
├── YunDa.Domain/ # 实体与业务逻辑
├── YunDa.Web/
│ └── YunDa.SOMS.Web.MVC/ # MVC Web项目
└── YunDa.Infrastructure/ # 基础设施层
```
## 🏗️ 架构规范
### 分层职责
| 层级 | 职责 | 依赖规则 |
|------|------|----------|
| **Application** | 继承`SOMSAppServiceBase`处理DTO转换、业务协调、API接口 | 可依赖Domain |
| **Domain** | 纯业务逻辑、实体定义、领域服务 | 不依赖任何外部设施 |
| **Infrastructure** | 数据访问、外部服务调用、技术实现 | 实现Domain接口 |
| **Web.MVC** | 前端页面、控制器、视图 | 依赖Application |
### 数据库配置
- **MySQL**: 配置数据存储
- **MongoDB**: 记录数据存储
- **Redis**: 数据共享与缓存
### 服务注册
```csharp
// AppService/DomainService: Scoped生命周期
// 使用ABP约定自动注册
[Dependency(ReplaceServices = true)]
public class UserManagementAppService : SOMSAppServiceBase, IUserManagementAppService
{
// 自动注册为Scoped
}
```
## 📝 代码规范
### 命名约定
```csharp
// ✅ 类名: PascalCase
public class UserManagementAppService : SOMSAppServiceBase
// ✅ 接口: I + PascalCase
public interface IUserManagementAppService : IApplicationService
// ✅ 方法: 动词开头 + Async (异步方法)
public async Task<RequestResult<UserOutput>> CreateUserAsync(CreateUserInput input)
// ✅ 变量/字段: camelCase私有字段下划线前缀
private readonly IUserRepository _userRepository;
private readonly ILogger _logger;
public string userName { get; set; }
// ✅ 常量: UPPER_SNAKE_CASE
private const string DEFAULT_USER_ROLE = "User";
// ✅ 枚举: PascalCase
public enum UserStatus
{
Active = 0,
Inactive = 1,
Suspended = 2
}
```
### 前后端命名差异
```csharp
// 后端 (PascalCase)
public class UserCreateInput
{
public string UserName { get; set; }
public string EmailAddress { get; set; }
public DateTime CreatedTime { get; set; }
}
// 前端请求 (camelCase)
{
"userName": "john_doe",
"emailAddress": "john@example.com",
"createdTime": "2024-01-01T00:00:00Z"
}
```
### 异步编程规范
```csharp
// ✅ 正确的异步方法
public async Task<RequestResult<UserOutput>> GetUserAsync(
Guid userId,
CancellationToken cancellationToken = default)
{
var user = await _userRepository
.GetAsync(userId, cancellationToken)
.ConfigureAwait(false);
return new RequestResult<UserOutput>
{
Flag = true,
ResultData = ObjectMapper.Map<UserOutput>(user)
};
}
// ❌ 避免的写法
public RequestResult<UserOutput> GetUser(Guid userId) // 缺少async
{
var user = _userRepository.Get(userId); // 同步调用
// ...
}
```
### C# 8.0+ 特性使用
```csharp
// ✅ Nullable引用类型
public async Task<RequestResult<string?>> GetOptionalDataAsync(Guid? id)
{
if (id is null) return RequestResult<string?>.Failed("ID不能为空");
// ...
}
// ✅ Switch表达式
public string GetStatusDescription(UserStatus status) => status switch
{
UserStatus.Active => "正常",
UserStatus.Inactive => "未激活",
UserStatus.Suspended => "已暂停",
_ => "未知状态"
};
// ✅ Pattern matching
public bool IsValidUser(object user) => user switch
{
User { IsActive: true, EmailConfirmed: true } => true,
_ => false
};
```
## 🛡️ AppService标准模板
### 基础模板
```csharp
[AbpAuthorize("Permission.User.Management")]
public class UserManagementAppService : SOMSAppServiceBase, IUserManagementAppService
{
private readonly IUserRepository _userRepository;
private readonly IUserDomainService _userDomainService;
public UserManagementAppService(
IUserRepository userRepository,
IUserDomainService userDomainService)
{
_userRepository = userRepository;
_userDomainService = userDomainService;
}
public async Task<RequestResult<UserOutput>> CreateUserAsync(
CreateUserInput input,
CancellationToken cancellationToken = default)
{
var result = new RequestResult<UserOutput>();
try
{
// 1. 输入验证
await ValidateInputAsync(input);
// 2. 业务逻辑处理
var user = await _userDomainService
.CreateUserAsync(input.UserName, input.EmailAddress, cancellationToken)
.ConfigureAwait(false);
// 3. 持久化
await _userRepository.InsertAsync(user, cancellationToken).ConfigureAwait(false);
await CurrentUnitOfWork.SaveChangesAsync(cancellationToken);
// 4. 结果转换
result.Flag = true;
result.ResultData = ObjectMapper.Map<UserOutput>(user);
result.Message = "用户创建成功";
Log4Helper.Info(GetType(), $"用户创建成功: {user.UserName}");
}
catch (ValidationException ex)
{
result.Flag = false;
result.Message = $"数据验证失败:{ex.Message}";
Log4Helper.Info(GetType(), "用户创建-验证异常", ex);
}
catch (BusinessException ex)
{
result.Flag = false;
result.Message = ex.Message;
Log4Helper.Warning(GetType(), "用户创建-业务异常", ex);
}
catch (Exception ex)
{
result.Flag = false;
result.Message = "系统异常,请联系管理员";
Log4Helper.Error(GetType(), "用户创建-系统异常", ex);
}
return result;
}
private async Task ValidateInputAsync(CreateUserInput input)
{
if (string.IsNullOrWhiteSpace(input.UserName))
throw new ValidationException("用户名不能为空");
if (await _userRepository.AnyAsync(u => u.UserName == input.UserName))
throw new ValidationException("用户名已存在");
}
}
```
### CRUD标准模板
```csharp
public class StandardCrudAppService<TEntity, TDto, TKey, TCreateInput, TUpdateInput>
: SOMSAppServiceBase
where TEntity : class, IEntity<TKey>
where TDto : IEntityDto<TKey>
{
protected readonly IRepository<TEntity, TKey> Repository;
protected StandardCrudAppService(IRepository<TEntity, TKey> repository)
{
Repository = repository;
}
public virtual async Task<RequestResult<TDto>> GetAsync(
TKey id,
CancellationToken cancellationToken = default)
{
var result = new RequestResult<TDto>();
try
{
var entity = await Repository
.GetAsync(id, cancellationToken)
.ConfigureAwait(false);
result.Flag = true;
result.ResultData = ObjectMapper.Map<TDto>(entity);
}
catch (EntityNotFoundException ex)
{
result.Flag = false;
result.Message = $"未找到ID为{id}的记录";
Log4Helper.Warning(GetType(), "记录不存在", ex);
}
catch (Exception ex)
{
result.Flag = false;
result.Message = "获取数据失败";
Log4Helper.Error(GetType(), "获取数据异常", ex);
}
return result;
}
public virtual async Task<RequestResult<PagedResultDto<TDto>>> GetPagedAsync(
PagedAndSortedRequestDto input,
CancellationToken cancellationToken = default)
{
var result = new RequestResult<PagedResultDto<TDto>>();
try
{
var query = Repository.GetAll();
var totalCount = await query.CountAsync(cancellationToken).ConfigureAwait(false);
var entities = await query
.OrderBy(input.Sorting ?? "Id")
.Skip(input.SkipCount)
.Take(input.MaxResultCount)
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
result.Flag = true;
result.ResultData = new PagedResultDto<TDto>
{
TotalCount = totalCount,
Items = ObjectMapper.Map<List<TDto>>(entities)
};
}
catch (Exception ex)
{
result.Flag = false;
result.Message = "获取分页数据失败";
Log4Helper.Error(GetType(), "分页查询异常", ex);
}
return result;
}
}
```
## 🔧 最佳实践
### 1. 异常处理层次
```csharp
// 业务异常 - 用户可理解的错误
throw new BusinessException("用户名已存在,请选择其他用户名");
// 验证异常 - 输入格式错误
throw new ValidationException("邮箱格式不正确");
// 系统异常 - 记录详细日志,返回通用错误消息
Log4Helper.Error(GetType(), "数据库连接失败", ex);
return RequestResult.Failed("系统异常,请联系管理员");
```
### 2. 日志记录规范
```csharp
// Info: 正常业务流程
Log4Helper.Info(GetType(), $"用户 {userName} 登录成功");
// Warning: 业务异常,需要关注但不影响系统运行
Log4Helper.Warning(GetType(), "用户尝试访问无权限资源", ex);
// Error: 系统异常,需要立即处理
Log4Helper.Error(GetType(), "数据库操作失败", ex);
```
### 3. 性能优化
```csharp
// ✅ 使用异步操作
await repository.GetListAsync(cancellationToken).ConfigureAwait(false);
// ✅ 合理使用缓存
var cacheKey = $"user_{userId}";
var user = await _cache.GetOrAddAsync(cacheKey,
() => _userRepository.GetAsync(userId),
TimeSpan.FromMinutes(30));
// ✅ 批量操作
await repository.InsertRangeAsync(users, cancellationToken);
```
### 4. 依赖注入
```csharp
// ✅ 构造函数注入
public class UserAppService : SOMSAppServiceBase
{
private readonly IUserRepository _userRepository;
private readonly IUserDomainService _userDomainService;
public UserAppService(
IUserRepository userRepository,
IUserDomainService userDomainService)
{
_userRepository = userRepository;
_userDomainService = userDomainService;
}
}
```
## ✅ 编译检查
- 每次编写代码后立即编译
- 解决所有编译错误和警告
- 使用 `dotnet build` 验证整个解决方案
- 启用 Nullable 引用类型检查
- 配置 EditorConfig 统一代码格式
## 🚫 开发约束
- ❌ 不需要创建测试项目
- ❌ 不需要生成 Summary.md 文件
- ❌ 避免在Domain层引用基础设施
- ❌ 不要在AppService中直接操作数据库连接
- ❌ 避免在业务逻辑中硬编码字符串
## 📚 参考文档
- [ASP.NET Boilerplate官方文档](https://aspnetboilerplate.com/Pages/Documents)
- .NET 6.0 官方文档
- C# 编程指南