SOMS/rules.txt
2025-09-03 10:16:04 +08:00

396 lines
11 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# .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# 编程指南