Files
TakeoutSaaS.AdminApi/Document/08_AI编程规范.md
2025-11-23 09:55:11 +08:00

1590 lines
46 KiB
Markdown
Raw 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.
# 外卖SaaS系统 - AI编程规范汇总
> 本文档专门为AI编程助手准备汇总了所有编码规范和规则确保代码质量和一致性。
## 1. 技术栈要求
### 1.1 核心技术
- **.NET 10** + **ASP.NET Core Web API**
- **Entity Framework Core 10**(复杂查询和实体管理)
- **Dapper 2.1+**(高性能查询和批量操作)
- **PostgreSQL 16+**(主数据库)
- **Redis 7.0+**(缓存和会话)
- **RabbitMQ 3.12+**(消息队列)
### 1.2 必用框架和库
- **AutoMapper**:对象映射
- **FluentValidation**:数据验证
- **Serilog**:结构化日志
- **MediatR**CQRS和中介者模式
- **Hangfire**:后台任务调度
- **Polly**:弹性和瞬态故障处理
- **Swagger/Swashbuckle**API文档
### 1.3 测试框架
- **xUnit**:单元测试
- **Moq**Mock框架
- **FluentAssertions**:断言库
## 2. 命名规范(严格遵守)
### 2.1 C#命名规范
```csharp
// ✅ 类名PascalCase
public class OrderService { }
// ✅ 接口I + PascalCase
public interface IOrderRepository { }
// ✅ 方法PascalCase异步方法以Async结尾
public async Task<Order> CreateOrderAsync() { }
// ✅ 私有字段_camelCase下划线前缀
private readonly IOrderRepository _orderRepository;
// ✅ 公共属性PascalCase
public string OrderNo { get; set; }
// ✅ 局部变量camelCase
var orderTotal = 100.00m;
// ✅ 常量PascalCase
public const int MaxOrderItems = 50;
// ✅ 枚举PascalCase枚举值也是PascalCase
public enum OrderStatus
{
Pending = 1,
Confirmed = 2,
Completed = 3
}
```
### 2.2 数据库命名规范
```sql
-- ✅ 表名:小写,下划线分隔,复数形式
orders
order_items
merchant_stores
-- ✅ 字段名:小写,下划线分隔
order_no
created_at
total_amount
-- ✅ 索引idx_表名_字段名
idx_orders_merchant_id
idx_orders_created_at
-- ✅ 外键fk_表名_引用表名
fk_orders_merchants
```
## 3. 项目结构规范
### 3.1 分层架构DDD + Clean Architecture
```
TakeoutSaaS/
├── src/
│ ├── Api/ # API层表现层
│ │ ├── Controllers/ # 控制器
│ │ ├── Filters/ # 过滤器
│ │ ├── Middleware/ # 中间件
│ │ └── Models/ # DTO模型
│ ├── Application/ # 应用层
│ │ ├── Services/ # 应用服务
│ │ ├── DTOs/ # 数据传输对象
│ │ ├── Interfaces/ # 服务接口
│ │ ├── Validators/ # FluentValidation验证器
│ │ ├── Mappings/ # AutoMapper配置
│ │ └── Commands/Queries/ # CQRS命令和查询
│ ├── Domain/ # 领域层(核心业务)
│ │ ├── Entities/ # 实体
│ │ ├── ValueObjects/ # 值对象
│ │ ├── Enums/ # 枚举
│ │ ├── Events/ # 领域事件
│ │ └── Interfaces/ # 仓储接口
│ ├── Infrastructure/ # 基础设施层
│ │ ├── Data/ # 数据访问
│ │ │ ├── EFCore/ # EF Core实现
│ │ │ ├── Dapper/ # Dapper实现
│ │ │ └── Repositories/ # 仓储实现
│ │ ├── Cache/ # 缓存实现
│ │ ├── MessageQueue/ # 消息队列
│ │ └── ExternalServices/ # 外部服务
│ ├── Core/ # 核心共享层
│ │ ├── Constants/ # 常量
│ │ ├── Exceptions/ # 异常
│ │ ├── Extensions/ # 扩展方法
│ │ └── Results/ # 统一返回结果
│ └── Modules/ # 模块化(可选)
└── tests/
├── UnitTests/ # 单元测试
├── IntegrationTests/ # 集成测试
└── PerformanceTests/ # 性能测试
```
### 3.2 文件组织规则
- 每个文件只包含一个公共类/接口
- 文件名与类名保持一致
- 相关的类放在同一个文件夹
- 使用命名空间反映文件夹结构
## 4. 代码注释规范
### 4.1 XML文档注释必须
```csharp
/// <summary>
/// 订单服务接口
/// </summary>
public interface IOrderService
{
/// <summary>
/// 创建订单
/// </summary>
/// <param name="request">订单创建请求</param>
/// <returns>订单信息</returns>
/// <exception cref="BusinessException">业务异常</exception>
Task<OrderDto> CreateOrderAsync(CreateOrderRequest request);
}
```
### 4.2 业务逻辑注释
```csharp
// ✅ 复杂业务逻辑必须添加注释
public async Task<decimal> CalculateOrderAmount(Order order)
{
// 1. 计算菜品总金额
var dishAmount = order.Items.Sum(x => x.Price * x.Quantity);
// 2. 计算配送费(距离 > 3km每公里加收2元
var deliveryFee = CalculateDeliveryFee(order.Distance);
// 3. 应用优惠券折扣
var discount = await ApplyCouponDiscountAsync(order.CouponId, dishAmount);
// 4. 计算最终金额
return dishAmount + deliveryFee - discount;
}
```
## 5. 异常处理规范
### 5.1 自定义异常
```csharp
// ✅ 业务异常
public class BusinessException : Exception
{
public int ErrorCode { get; }
public BusinessException(int errorCode, string message)
: base(message)
{
ErrorCode = errorCode;
}
}
// ✅ 验证异常
public class ValidationException : Exception
{
public IDictionary<string, string[]> Errors { get; }
public ValidationException(IDictionary<string, string[]> errors)
: base("一个或多个验证错误")
{
Errors = errors;
}
}
```
### 5.2 全局异常处理中间件(必须实现)
```csharp
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "业务异常:{Message}", ex.Message);
await HandleBusinessExceptionAsync(context, ex);
}
catch (ValidationException ex)
{
_logger.LogWarning(ex, "验证异常:{Errors}", ex.Errors);
await HandleValidationExceptionAsync(context, ex);
}
catch (Exception ex)
{
_logger.LogError(ex, "系统异常:{Message}", ex.Message);
await HandleSystemExceptionAsync(context, ex);
}
}
private static Task HandleBusinessExceptionAsync(HttpContext context, BusinessException ex)
{
context.Response.StatusCode = StatusCodes.Status422UnprocessableEntity;
return context.Response.WriteAsJsonAsync(new
{
success = false,
code = ex.ErrorCode,
message = ex.Message
});
}
}
```
### 5.3 异常使用示例
```csharp
// ✅ 正确的异常抛出
public async Task<Order> GetOrderAsync(Guid orderId)
{
var order = await _orderRepository.GetByIdAsync(orderId);
if (order == null)
{
throw new BusinessException(404, "订单不存在");
}
return order;
}
// ❌ 错误:不要吞掉异常
try
{
// ...
}
catch (Exception)
{
// 什么都不做 - 这是错误的!
}
```
## 6. 服务层编码规范
### 6.1 服务类结构(标准模板)
```csharp
// ✅ 好的服务实现
public class OrderService : IOrderService
{
private readonly IOrderRepository _orderRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<OrderService> _logger;
private readonly IMapper _mapper;
private readonly IOptions<OrderSettings> _settings;
public OrderService(
IOrderRepository orderRepository,
IUnitOfWork unitOfWork,
ILogger<OrderService> logger,
IMapper mapper,
IOptions<OrderSettings> settings)
{
_orderRepository = orderRepository;
_unitOfWork = unitOfWork;
_logger = logger;
_mapper = mapper;
_settings = settings;
}
public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest request)
{
try
{
// 1. 参数验证FluentValidation会自动验证这里是额外检查
if (request == null)
throw new ArgumentNullException(nameof(request));
// 2. 记录日志
_logger.LogInformation("创建订单:{@Request}", request);
// 3. 业务逻辑
var order = new Order
{
OrderNo = GenerateOrderNo(),
TotalAmount = request.TotalAmount,
DeliveryFee = _settings.Value.DefaultDeliveryFee,
CreatedAt = DateTime.UtcNow
};
// 4. 数据持久化
await _orderRepository.AddAsync(order);
await _unitOfWork.SaveChangesAsync();
// 5. 记录成功日志
_logger.LogInformation("订单创建成功:{OrderId}", order.Id);
// 6. 返回DTO
return _mapper.Map<OrderDto>(order);
}
catch (Exception ex)
{
_logger.LogError(ex, "创建订单失败:{@Request}", request);
throw;
}
}
}
// ❌ 错误的服务实现
public class BadOrderService
{
// ❌ 直接注入DbContext而不是仓储
private readonly AppDbContext _dbContext;
// ❌ 没有日志
// ❌ 硬编码配置
public Order CreateOrder(CreateOrderRequest request)
{
var order = new Order();
order.DeliveryFee = 5.0m; // ❌ 硬编码
_dbContext.Orders.Add(order);
_dbContext.SaveChanges(); // ❌ 同步方法
return order; // ❌ 返回实体而不是DTO
}
}
```
### 6.2 依赖注入规则
```csharp
// ✅ 使用构造函数注入
public class OrderService
{
private readonly IOrderRepository _orderRepository;
public OrderService(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
}
// ❌ 不要使用属性注入
public class BadOrderService
{
[Inject]
public IOrderRepository OrderRepository { get; set; }
}
// ❌ 不要使用服务定位器模式
public class BadOrderService
{
public void DoSomething()
{
var repository = ServiceLocator.GetService<IOrderRepository>();
}
}
```
## 7. 数据访问规范
### 7.1 仓储模式(必须使用)
```csharp
// ✅ 仓储接口
public interface IOrderRepository
{
Task<Order> GetByIdAsync(Guid id);
Task<IEnumerable<Order>> GetAllAsync();
Task<Order> AddAsync(Order order);
Task UpdateAsync(Order order);
Task DeleteAsync(Guid id);
Task<bool> ExistsAsync(Guid id);
}
// ✅ EF Core仓储实现
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context)
{
_context = context;
}
public async Task<Order> GetByIdAsync(Guid id)
{
return await _context.Orders
.Include(o => o.OrderItems)
.Include(o => o.Customer)
.FirstOrDefaultAsync(o => o.Id == id);
}
public async Task<Order> AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
return order;
}
}
```
### 7.2 EF Core vs Dapper 使用场景
```csharp
// ✅ EF Core - 复杂查询和实体管理
public async Task<Order> GetOrderWithDetailsAsync(Guid orderId)
{
return await _dbContext.Orders
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Dish)
.Include(o => o.Customer)
.Include(o => o.Merchant)
.FirstOrDefaultAsync(o => o.Id == orderId);
}
// ✅ Dapper - 高性能统计查询
public async Task<OrderStatistics> GetOrderStatisticsAsync(DateTime startDate, DateTime endDate)
{
var sql = @"
SELECT
COUNT(*) as TotalOrders,
SUM(total_amount) as TotalAmount,
AVG(total_amount) as AvgAmount,
MAX(total_amount) as MaxAmount,
MIN(total_amount) as MinAmount
FROM orders
WHERE created_at BETWEEN @StartDate AND @EndDate
AND status = @Status";
return await _connection.QueryFirstOrDefaultAsync<OrderStatistics>(sql,
new { StartDate = startDate, EndDate = endDate, Status = OrderStatus.Completed });
}
// ✅ Dapper - 批量插入
public async Task<int> BulkInsertOrdersAsync(IEnumerable<Order> orders)
{
var sql = @"
INSERT INTO orders (id, order_no, merchant_id, total_amount, created_at)
VALUES (@Id, @OrderNo, @MerchantId, @TotalAmount, @CreatedAt)";
return await _connection.ExecuteAsync(sql, orders);
}
```
### 7.3 工作单元模式(必须使用)
```csharp
// ✅ 工作单元接口
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Task BeginTransactionAsync();
Task CommitTransactionAsync();
Task RollbackTransactionAsync();
}
// ✅ 使用工作单元
public async Task<OrderDto> CreateOrderWithItemsAsync(CreateOrderRequest request)
{
await _unitOfWork.BeginTransactionAsync();
try
{
// 1. 创建订单
var order = new Order { /* ... */ };
await _orderRepository.AddAsync(order);
// 2. 创建订单项
foreach (var item in request.Items)
{
var orderItem = new OrderItem { /* ... */ };
await _orderItemRepository.AddAsync(orderItem);
}
// 3. 提交事务
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitTransactionAsync();
return _mapper.Map<OrderDto>(order);
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}
```
## 8. CQRS模式规范
### 8.1 命令Command- 写操作
```csharp
// ✅ 命令定义
public class CreateOrderCommand : IRequest<OrderDto>
{
public Guid MerchantId { get; set; }
public Guid CustomerId { get; set; }
public List<OrderItemDto> Items { get; set; }
public decimal TotalAmount { get; set; }
}
// ✅ 命令处理器
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, OrderDto>
{
private readonly IOrderRepository _orderRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<CreateOrderCommandHandler> _logger;
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
_logger.LogInformation("处理创建订单命令:{@Command}", request);
var order = new Order
{
MerchantId = request.MerchantId,
CustomerId = request.CustomerId,
TotalAmount = request.TotalAmount
};
await _orderRepository.AddAsync(order);
await _unitOfWork.SaveChangesAsync(cancellationToken);
return _mapper.Map<OrderDto>(order);
}
}
```
### 8.2 查询Query- 读操作
```csharp
// ✅ 查询定义
public class GetOrderByIdQuery : IRequest<OrderDto>
{
public Guid OrderId { get; set; }
}
// ✅ 查询处理器
public class GetOrderByIdQueryHandler : IRequestHandler<GetOrderByIdQuery, OrderDto>
{
private readonly IOrderRepository _orderRepository;
private readonly IMapper _mapper;
public async Task<OrderDto> Handle(GetOrderByIdQuery request, CancellationToken cancellationToken)
{
var order = await _orderRepository.GetByIdAsync(request.OrderId);
if (order == null)
throw new BusinessException(404, "订单不存在");
return _mapper.Map<OrderDto>(order);
}
}
```
## 9. 验证规范FluentValidation
### 9.1 验证器定义
```csharp
// ✅ 使用FluentValidation
public class CreateOrderRequestValidator : AbstractValidator<CreateOrderRequest>
{
public CreateOrderRequestValidator()
{
RuleFor(x => x.MerchantId)
.NotEmpty().WithMessage("商家ID不能为空");
RuleFor(x => x.CustomerId)
.NotEmpty().WithMessage("客户ID不能为空");
RuleFor(x => x.Items)
.NotEmpty().WithMessage("订单项不能为空")
.Must(items => items.Count <= 50).WithMessage("订单项不能超过50个");
RuleFor(x => x.TotalAmount)
.GreaterThan(0).WithMessage("订单金额必须大于0");
RuleFor(x => x.DeliveryAddress)
.NotEmpty().WithMessage("配送地址不能为空")
.MaximumLength(200).WithMessage("配送地址不能超过200个字符");
}
}
```
### 9.2 验证器注册
```csharp
// ✅ 在Program.cs中注册
builder.Services.AddValidatorsFromAssemblyContaining<CreateOrderRequestValidator>();
builder.Services.AddFluentValidationAutoValidation();
```
## 10. 缓存策略规范
### 10.1 缓存时间策略
```csharp
// ✅ 缓存时间常量
public static class CacheTimeouts
{
public static readonly TimeSpan MerchantInfo = TimeSpan.FromMinutes(30);
public static readonly TimeSpan DishInfo = TimeSpan.FromMinutes(15);
public static readonly TimeSpan UserSession = TimeSpan.FromHours(2);
public static readonly TimeSpan ConfigInfo = TimeSpan.FromHours(1);
public static readonly TimeSpan HotData = TimeSpan.FromMinutes(5);
}
```
### 10.2 缓存使用示例
```csharp
// ✅ 使用分布式缓存Redis
public class MerchantService : IMerchantService
{
private readonly IMerchantRepository _merchantRepository;
private readonly IDistributedCache _cache;
private readonly ILogger<MerchantService> _logger;
public async Task<MerchantDto> GetMerchantAsync(Guid merchantId)
{
var cacheKey = $"merchant:{merchantId}";
// 1. 尝试从缓存获取
var cachedData = await _cache.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedData))
{
_logger.LogDebug("从缓存获取商家信息:{MerchantId}", merchantId);
return JsonSerializer.Deserialize<MerchantDto>(cachedData);
}
// 2. 缓存未命中,从数据库查询
var merchant = await _merchantRepository.GetByIdAsync(merchantId);
if (merchant == null)
throw new BusinessException(404, "商家不存在");
var dto = _mapper.Map<MerchantDto>(merchant);
// 3. 写入缓存
var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = CacheTimeouts.MerchantInfo
};
await _cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(dto), cacheOptions);
_logger.LogDebug("商家信息已缓存:{MerchantId}", merchantId);
return dto;
}
public async Task UpdateMerchantAsync(Guid merchantId, UpdateMerchantRequest request)
{
// 更新数据库
var merchant = await _merchantRepository.GetByIdAsync(merchantId);
// ... 更新逻辑
await _unitOfWork.SaveChangesAsync();
// ✅ 更新后清除缓存
var cacheKey = $"merchant:{merchantId}";
await _cache.RemoveAsync(cacheKey);
_logger.LogDebug("已清除商家缓存:{MerchantId}", merchantId);
}
}
```
## 11. 日志规范Serilog
### 11.1 日志级别使用
```csharp
// ✅ 正确的日志级别使用
public class OrderService
{
private readonly ILogger<OrderService> _logger;
public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest request)
{
// Trace: 非常详细的调试信息(生产环境不记录)
_logger.LogTrace("进入CreateOrderAsync方法");
// Debug: 调试信息(生产环境不记录)
_logger.LogDebug("订单请求参数:{@Request}", request);
// Information: 一般信息(重要业务流程)
_logger.LogInformation("开始创建订单商家ID{MerchantId}客户ID{CustomerId}",
request.MerchantId, request.CustomerId);
try
{
// ... 业务逻辑
// Information: 成功完成
_logger.LogInformation("订单创建成功:{OrderId},订单号:{OrderNo}",
order.Id, order.OrderNo);
return dto;
}
catch (BusinessException ex)
{
// Warning: 业务异常(预期内的错误)
_logger.LogWarning(ex, "创建订单失败(业务异常):{Message}", ex.Message);
throw;
}
catch (Exception ex)
{
// Error: 系统异常(非预期错误)
_logger.LogError(ex, "创建订单失败(系统异常):{@Request}", request);
throw;
}
}
}
```
### 11.2 结构化日志
```csharp
// ✅ 使用结构化日志(推荐)
_logger.LogInformation("用户 {UserId} 创建了订单 {OrderId},金额 {Amount}",
userId, orderId, amount);
// ✅ 记录对象(使用@符号)
_logger.LogInformation("订单详情:{@Order}", order);
// ❌ 不要使用字符串拼接
_logger.LogInformation("用户 " + userId + " 创建了订单 " + orderId);
```
### 11.3 敏感信息处理
```csharp
// ✅ 不要记录敏感信息
public class PaymentService
{
public async Task ProcessPaymentAsync(PaymentRequest request)
{
// ❌ 错误:记录了密码、支付密码等敏感信息
_logger.LogInformation("支付请求:{@Request}", request);
// ✅ 正确:只记录非敏感信息
_logger.LogInformation("处理支付订单ID{OrderId},金额:{Amount}",
request.OrderId, request.Amount);
}
}
```
## 12. API控制器规范
### 12.1 控制器结构(标准模板)
```csharp
/// <summary>
/// 订单管理API
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<OrdersController> _logger;
public OrdersController(IMediator mediator, ILogger<OrdersController> logger)
{
_mediator = mediator;
_logger = logger;
}
/// <summary>
/// 创建订单
/// </summary>
/// <param name="request">订单创建请求</param>
/// <returns>订单信息</returns>
/// <response code="200">创建成功</response>
/// <response code="400">请求参数错误</response>
/// <response code="422">业务验证失败</response>
[HttpPost]
[ProducesResponseType(typeof(ApiResponse<OrderDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status422UnprocessableEntity)]
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderRequest request)
{
_logger.LogInformation("API调用创建订单");
var command = new CreateOrderCommand
{
MerchantId = request.MerchantId,
CustomerId = request.CustomerId,
Items = request.Items,
TotalAmount = request.TotalAmount
};
var result = await _mediator.Send(command);
return Ok(ApiResponse<OrderDto>.SuccessResult(result));
}
/// <summary>
/// 获取订单详情
/// </summary>
/// <param name="id">订单ID</param>
/// <returns>订单详情</returns>
[HttpGet("{id}")]
[ProducesResponseType(typeof(ApiResponse<OrderDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetOrder(Guid id)
{
var query = new GetOrderByIdQuery { OrderId = id };
var result = await _mediator.Send(query);
return Ok(ApiResponse<OrderDto>.SuccessResult(result));
}
/// <summary>
/// 获取订单列表
/// </summary>
/// <param name="request">查询参数</param>
/// <returns>订单列表</returns>
[HttpGet]
[ProducesResponseType(typeof(ApiResponse<PagedResult<OrderDto>>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetOrders([FromQuery] GetOrdersRequest request)
{
var query = new GetOrdersQuery
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Status = request.Status
};
var result = await _mediator.Send(query);
return Ok(ApiResponse<PagedResult<OrderDto>>.SuccessResult(result));
}
}
```
### 12.2 统一返回结果ApiResponse
```csharp
// ✅ 统一返回结果(泛型)
public class ApiResponse<T>
{
public bool Success { get; set; }
public int Code { get; set; } = 200;
public string? Message { get; set; }
public T? Data { get; set; }
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
// 工厂方法:成功
public static ApiResponse<T> SuccessResult(T data, string? message = "操作成功") => new()
{
Success = true,
Code = 200,
Message = message,
Data = data
};
// 工厂方法:失败
public static ApiResponse<T> Failure(int code, string message) => new()
{
Success = false,
Code = code,
Message = message
};
}
// ✅ 分页结果
public class PagedResult<T>
{
public List<T> Items { get; set; }
public int TotalCount { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
}
```
### 12.3 非泛型便捷封装(推荐在仅返回消息时使用)
```csharp
public static class ApiResponse
{
// ✅ 仅返回成功/消息(无数据载荷)
public static ApiResponse<object> Success(string? message = "操作成功")
=> new ApiResponse<object> { Success = true, Code = 200, Message = message, Data = null };
// ✅ 错误(配合统一错误码)
public static ApiResponse<object> Failure(int code, string message)
=> new ApiResponse<object> { Success = false, Code = code, Message = message, Data = null };
}
```
### 12.4 统一错误码ErrorCodes
```csharp
public static class ErrorCodes
{
public const int BadRequest = 400;
public const int Unauthorized = 401;
public const int Forbidden = 403;
public const int NotFound = 404;
public const int Conflict = 409;
public const int ValidationFailed = 422; // 业务/验证不通过
public const int InternalServerError = 500;
// 业务自定义区间10000+
public const int BusinessError = 10001;
}
```
### 12.5 错误响应的ProblemDetails映射全局异常处理中间件
```csharp
public class BusinessException : Exception
{
public int ErrorCode { get; }
public BusinessException(int errorCode, string message) : base(message) => ErrorCode = errorCode;
}
public class ValidationException : Exception
{
public IDictionary<string, string[]> Errors { get; }
public ValidationException(IDictionary<string, string[]> errors) : base("一个或多个验证错误") => Errors = errors;
}
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{ _next = next; _logger = logger; }
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (BusinessException ex)
{
await WriteProblemDetailsAsync(context, StatusCodes.Status422UnprocessableEntity, "业务异常", ex.Message, ex.ErrorCode);
}
catch (ValidationException ex)
{
await WriteProblemDetailsAsync(context, StatusCodes.Status422UnprocessableEntity, "验证异常", ex.Message, ErrorCodes.ValidationFailed, ex.Errors);
}
catch (Exception ex)
{
_logger.LogError(ex, "系统异常");
await WriteProblemDetailsAsync(context, StatusCodes.Status500InternalServerError, "系统异常", "服务器发生错误,请稍后重试", ErrorCodes.InternalServerError);
}
}
private static Task WriteProblemDetailsAsync(HttpContext context, int status, string title, string detail, int code, object? errors = null)
{
var problem = new ProblemDetails
{
Status = status,
Title = title,
Detail = detail,
Instance = context.Request.Path,
Type = $"https://httpstatuses.com/{status}"
};
problem.Extensions["code"] = code;
if (errors != null) problem.Extensions["errors"] = errors;
context.Response.StatusCode = status;
context.Response.ContentType = "application/problem+json";
return context.Response.WriteAsJsonAsync(problem);
}
}
// Program.cs 中注册
app.UseMiddleware<ExceptionHandlingMiddleware>();
```
## 13. 实体和DTO规范
### 13.1 实体类Domain Entity
```csharp
// ✅ 领域实体
public class Order : BaseEntity
{
public Guid Id { get; private set; }
public string OrderNo { get; private set; }
public Guid MerchantId { get; private set; }
public Guid CustomerId { get; private set; }
public decimal TotalAmount { get; private set; }
public OrderStatus Status { get; private set; }
public DateTime CreatedAt { get; private set; }
public DateTime? UpdatedAt { get; private set; }
// 导航属性
public virtual Merchant Merchant { get; private set; }
public virtual Customer Customer { get; private set; }
public virtual ICollection<OrderItem> OrderItems { get; private set; }
// 私有构造函数用于EF Core
private Order() { }
// 工厂方法
public static Order Create(Guid merchantId, Guid customerId, decimal totalAmount)
{
return new Order
{
Id = Guid.NewGuid(),
OrderNo = GenerateOrderNo(),
MerchantId = merchantId,
CustomerId = customerId,
TotalAmount = totalAmount,
Status = OrderStatus.Pending,
CreatedAt = DateTime.UtcNow
};
}
// 业务方法
public void Confirm()
{
if (Status != OrderStatus.Pending)
throw new BusinessException(400, "只有待确认的订单才能确认");
Status = OrderStatus.Confirmed;
UpdatedAt = DateTime.UtcNow;
}
public void Cancel(string reason)
{
if (Status == OrderStatus.Completed)
throw new BusinessException(400, "已完成的订单不能取消");
Status = OrderStatus.Cancelled;
UpdatedAt = DateTime.UtcNow;
}
private static string GenerateOrderNo()
{
return $"ORD{DateTime.UtcNow:yyyyMMddHHmmss}{Random.Shared.Next(1000, 9999)}";
}
}
```
### 13.2 DTO数据传输对象
```csharp
// ✅ 请求DTO
public class CreateOrderRequest
{
public Guid MerchantId { get; set; }
public Guid CustomerId { get; set; }
public List<OrderItemDto> Items { get; set; }
public decimal TotalAmount { get; set; }
public string DeliveryAddress { get; set; }
public string ContactPhone { get; set; }
public string Remark { get; set; }
}
// ✅ 响应DTO
public class OrderDto
{
public Guid Id { get; set; }
public string OrderNo { get; set; }
public Guid MerchantId { get; set; }
public string MerchantName { get; set; }
public Guid CustomerId { get; set; }
public string CustomerName { get; set; }
public decimal TotalAmount { get; set; }
public OrderStatus Status { get; set; }
public string StatusText { get; set; }
public List<OrderItemDto> Items { get; set; }
public DateTime CreatedAt { get; set; }
}
```
### 13.3 AutoMapper配置
```csharp
// ✅ AutoMapper Profile
public class OrderMappingProfile : Profile
{
public OrderMappingProfile()
{
CreateMap<Order, OrderDto>()
.ForMember(dest => dest.MerchantName, opt => opt.MapFrom(src => src.Merchant.Name))
.ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ForMember(dest => dest.StatusText, opt => opt.MapFrom(src => src.Status.ToString()));
CreateMap<CreateOrderRequest, Order>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.OrderNo, opt => opt.Ignore())
.ForMember(dest => dest.CreatedAt, opt => opt.Ignore());
}
}
```
## 14. Git工作流规范
### 14.1 分支管理
```
main # 主分支,生产环境代码
├── develop # 开发分支
│ ├── feature/order-management # 功能分支
│ ├── feature/payment-integration # 功能分支
│ └── bugfix/order-calculation # 修复分支
└── hotfix/critical-bug # 紧急修复分支
```
### 14.2 分支命名规范
- **功能分支**`feature/功能名称`(如:`feature/order-management`
- **修复分支**`bugfix/问题描述`(如:`bugfix/order-calculation`
- **紧急修复**`hotfix/问题描述`(如:`hotfix/payment-error`
- **发布分支**`release/版本号`(如:`release/v1.0.0`
### 14.3 提交信息规范(严格遵守)
```bash
# 格式:<type>(<scope>): <subject>
# type类型必须使用以下之一
# feat: 新功能
# fix: 修复bug
# docs: 文档更新
# style: 代码格式调整(不影响代码运行)
# refactor: 重构既不是新功能也不是修复bug
# perf: 性能优化
# test: 测试相关
# chore: 构建/工具相关
# 示例(必须遵循):
git commit -m "feat(order): 添加订单创建功能"
git commit -m "fix(payment): 修复支付回调处理错误"
git commit -m "docs(api): 更新API文档"
git commit -m "refactor(service): 重构订单服务"
git commit -m "perf(query): 优化订单查询性能"
```
## 15. 单元测试规范
### 15.1 测试命名规范
```csharp
// ✅ 测试命名格式MethodName_Scenario_ExpectedResult
[Fact]
public async Task CreateOrder_ValidRequest_ReturnsOrderDto()
{
// Arrange准备
var request = new CreateOrderRequest
{
MerchantId = Guid.NewGuid(),
CustomerId = Guid.NewGuid(),
Items = new List<OrderItemDto>
{
new OrderItemDto { DishId = Guid.NewGuid(), Quantity = 2, Price = 50.00m }
},
TotalAmount = 100.00m
};
// Act执行
var result = await _orderService.CreateOrderAsync(request);
// Assert断言
result.Should().NotBeNull();
result.OrderNo.Should().NotBeNullOrEmpty();
result.TotalAmount.Should().Be(100.00m);
}
[Fact]
public async Task CreateOrder_InvalidMerchantId_ThrowsBusinessException()
{
// Arrange
var request = new CreateOrderRequest
{
MerchantId = Guid.Empty, // 无效的商家ID
CustomerId = Guid.NewGuid(),
TotalAmount = 100.00m
};
// Act & Assert
await Assert.ThrowsAsync<BusinessException>(
async () => await _orderService.CreateOrderAsync(request));
}
```
### 15.2 Mock使用
```csharp
// ✅ 使用Moq进行Mock
public class OrderServiceTests
{
private readonly Mock<IOrderRepository> _orderRepositoryMock;
private readonly Mock<IUnitOfWork> _unitOfWorkMock;
private readonly Mock<ILogger<OrderService>> _loggerMock;
private readonly Mock<IMapper> _mapperMock;
private readonly OrderService _orderService;
public OrderServiceTests()
{
_orderRepositoryMock = new Mock<IOrderRepository>();
_unitOfWorkMock = new Mock<IUnitOfWork>();
_loggerMock = new Mock<ILogger<OrderService>>();
_mapperMock = new Mock<IMapper>();
_orderService = new OrderService(
_orderRepositoryMock.Object,
_unitOfWorkMock.Object,
_loggerMock.Object,
_mapperMock.Object,
Options.Create(new OrderSettings())
);
}
[Fact]
public async Task GetOrder_ExistingId_ReturnsOrder()
{
// Arrange
var orderId = Guid.NewGuid();
var order = new Order { Id = orderId, OrderNo = "ORD001" };
var orderDto = new OrderDto { Id = orderId, OrderNo = "ORD001" };
_orderRepositoryMock
.Setup(x => x.GetByIdAsync(orderId))
.ReturnsAsync(order);
_mapperMock
.Setup(x => x.Map<OrderDto>(order))
.Returns(orderDto);
// Act
var result = await _orderService.GetOrderAsync(orderId);
// Assert
result.Should().NotBeNull();
result.Id.Should().Be(orderId);
_orderRepositoryMock.Verify(x => x.GetByIdAsync(orderId), Times.Once);
}
}
```
### 15.3 测试覆盖率要求
- **核心业务逻辑**>= 80%
- **服务层**>= 70%
- **仓储层**>= 60%
- **控制器层**>= 50%
## 16. 性能优化规范
### 16.1 数据库查询优化
```csharp
// ❌ 错误N+1查询问题
public async Task<List<OrderDto>> GetOrdersAsync()
{
var orders = await _context.Orders.ToListAsync();
foreach (var order in orders)
{
// 每次循环都会查询数据库!
order.Customer = await _context.Customers.FindAsync(order.CustomerId);
order.Merchant = await _context.Merchants.FindAsync(order.MerchantId);
}
return _mapper.Map<List<OrderDto>>(orders);
}
// ✅ 正确使用Include预加载
public async Task<List<OrderDto>> GetOrdersAsync()
{
var orders = await _context.Orders
.Include(o => o.Customer)
.Include(o => o.Merchant)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Dish)
.ToListAsync();
return _mapper.Map<List<OrderDto>>(orders);
}
// ✅ 更好大数据量使用Dapper
public async Task<List<OrderDto>> GetOrdersAsync()
{
var sql = @"
SELECT
o.id, o.order_no, o.total_amount,
c.id as customer_id, c.name as customer_name,
m.id as merchant_id, m.name as merchant_name
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id
INNER JOIN merchants m ON o.merchant_id = m.id
WHERE o.status = @Status
ORDER BY o.created_at DESC
LIMIT @Limit OFFSET @Offset";
return await _connection.QueryAsync<OrderDto>(sql, new { Status = 1, Limit = 100, Offset = 0 });
}
```
### 16.2 异步编程规范
```csharp
// ✅ 正确使用async/await
public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest request)
{
var order = new Order { /* ... */ };
await _orderRepository.AddAsync(order);
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<OrderDto>(order);
}
// ❌ 错误:不要使用.Result或.Wait()
public OrderDto CreateOrder(CreateOrderRequest request)
{
var order = new Order { /* ... */ };
_orderRepository.AddAsync(order).Wait(); // 可能导致死锁!
_unitOfWork.SaveChangesAsync().Result; // 可能导致死锁!
return _mapper.Map<OrderDto>(order);
}
// ❌ 错误:不要混用同步和异步
public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest request)
{
var order = new Order { /* ... */ };
_orderRepository.AddAsync(order).Wait(); // 错误!
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<OrderDto>(order);
}
```
### 16.3 批量操作优化
```csharp
// ❌ 错误:逐条插入
public async Task ImportOrdersAsync(List<Order> orders)
{
foreach (var order in orders)
{
await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync(); // 每次都保存,性能差!
}
}
// ✅ 正确:批量插入
public async Task ImportOrdersAsync(List<Order> orders)
{
await _context.Orders.AddRangeAsync(orders);
await _context.SaveChangesAsync(); // 一次性保存
}
// ✅ 更好使用Dapper批量插入大数据量
public async Task ImportOrdersAsync(List<Order> orders)
{
var sql = @"
INSERT INTO orders (id, order_no, merchant_id, total_amount, created_at)
VALUES (@Id, @OrderNo, @MerchantId, @TotalAmount, @CreatedAt)";
await _connection.ExecuteAsync(sql, orders);
}
```
## 17. 安全规范
### 17.1 SQL注入防护
```csharp
// ❌ 错误字符串拼接SQLSQL注入风险
public async Task<Order> GetOrderByNoAsync(string orderNo)
{
var sql = $"SELECT * FROM orders WHERE order_no = '{orderNo}'"; // 危险!
return await _connection.QueryFirstOrDefaultAsync<Order>(sql);
}
// ✅ 正确:使用参数化查询
public async Task<Order> GetOrderByNoAsync(string orderNo)
{
var sql = "SELECT * FROM orders WHERE order_no = @OrderNo";
return await _connection.QueryFirstOrDefaultAsync<Order>(sql, new { OrderNo = orderNo });
}
```
### 17.2 敏感数据加密
```csharp
// ✅ 密码加密存储
public class UserService
{
private readonly IPasswordHasher<User> _passwordHasher;
public async Task<User> CreateUserAsync(string username, string password)
{
var user = new User { Username = username };
// ✅ 使用密码哈希
user.PasswordHash = _passwordHasher.HashPassword(user, password);
await _userRepository.AddAsync(user);
await _unitOfWork.SaveChangesAsync();
return user;
}
public async Task<bool> ValidatePasswordAsync(User user, string password)
{
var result = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
return result == PasswordVerificationResult.Success;
}
}
```
### 17.3 授权验证
```csharp
// ✅ 使用授权特性
[Authorize(Roles = "Admin")]
public class AdminController : ControllerBase
{
[HttpGet("users")]
public async Task<IActionResult> GetUsers()
{
// 只有Admin角色可以访问
}
}
// ✅ 基于策略的授权
[Authorize(Policy = "MerchantOwner")]
public class MerchantController : ControllerBase
{
[HttpPut("{id}")]
public async Task<IActionResult> UpdateMerchant(Guid id, UpdateMerchantRequest request)
{
// 只有商家所有者可以更新
}
}
// ✅ 在服务层也要验证权限
public async Task UpdateMerchantAsync(Guid merchantId, Guid userId, UpdateMerchantRequest request)
{
var merchant = await _merchantRepository.GetByIdAsync(merchantId);
// 验证用户是否有权限
if (merchant.OwnerId != userId)
{
throw new BusinessException(403, "无权限操作");
}
// ... 更新逻辑
}
```
## 18. 代码审查清单
### 18.1 必查项目
- [ ] 代码符合命名规范PascalCase、camelCase、_camelCase
- [ ] 所有公共API有XML文档注释
- [ ] 复杂业务逻辑有注释说明
- [ ] 异常处理完善try-catch、自定义异常
- [ ] 使用异步方法async/await
- [ ] 使用依赖注入(构造函数注入)
- [ ] 使用仓储模式不直接操作DbContext
- [ ] 使用工作单元模式(事务管理)
- [ ] 日志记录完善Information、Warning、Error
- [ ] 参数验证FluentValidation
- [ ] 返回DTO而不是实体
- [ ] 无硬编码配置使用IOptions
- [ ] 无SQL注入风险参数化查询
- [ ] 敏感数据加密
- [ ] 权限验证完善
### 18.2 性能检查
- [ ] 避免N+1查询使用Include
- [ ] 大数据量使用Dapper
- [ ] 合理使用缓存
- [ ] 批量操作使用批量方法
- [ ] 异步方法不使用.Result或.Wait()
### 18.3 安全检查
- [ ] 无SQL注入风险
- [ ] 密码已加密
- [ ] 授权验证完善
- [ ] 敏感信息不记录日志
- [ ] HTTPS传输
## 19. 禁止事项(严格禁止)
### 19.1 绝对禁止
```csharp
// ❌ 禁止直接在控制器或服务中使用DbContext
public class OrderController
{
private readonly AppDbContext _context; // 禁止!
}
// ❌ 禁止:硬编码配置
var deliveryFee = 5.0m; // 禁止!应该从配置读取
// ❌ 禁止:返回实体类
public Order GetOrder(Guid id) // 禁止应该返回DTO
{
return _context.Orders.Find(id);
}
// ❌ 禁止字符串拼接SQL
var sql = $"SELECT * FROM orders WHERE id = '{id}'"; // 禁止SQL注入风险
// ❌ 禁止:吞掉异常
try
{
// ...
}
catch (Exception)
{
// 什么都不做 - 禁止!
}
// ❌ 禁止:使用.Result或.Wait()
var result = _service.GetOrderAsync(id).Result; // 禁止!可能死锁
// ❌ 禁止:记录敏感信息
_logger.LogInformation("用户密码:{Password}", password); // 禁止!
// ❌ 禁止:不使用异步方法
public Order CreateOrder(CreateOrderRequest request) // 禁止应该使用async
{
_context.Orders.Add(order);
_context.SaveChanges(); // 应该使用SaveChangesAsync
}
```
## 20. 最佳实践总结
### 20.1 核心原则
1. **SOLID原则**:单一职责、开闭原则、里氏替换、接口隔离、依赖倒置
2. **DRY原则**不要重复自己Don't Repeat Yourself
3. **KISS原则**保持简单Keep It Simple, Stupid
4. **YAGNI原则**你不会需要它You Aren't Gonna Need It
### 20.2 编码习惯
- ✅ 使用有意义的变量名
- ✅ 方法保持简短不超过50行
- ✅ 类保持单一职责
- ✅ 优先使用组合而不是继承
- ✅ 编写可测试的代码
- ✅ 先写测试再写代码TDD
- ✅ 持续重构,保持代码整洁
### 20.3 团队协作
- ✅ 遵循统一的代码规范
- ✅ 代码审查必须通过
- ✅ 提交前运行测试
- ✅ 提交信息清晰明确
- ✅ 及时更新文档
- ✅ 主动分享知识
---
## 附录:快速参考
### A. 常用命名模式
- 类:`OrderService``MerchantRepository`
- 接口:`IOrderService``IMerchantRepository`
- 方法:`CreateOrderAsync``GetOrderByIdAsync`
- 字段:`_orderRepository``_logger`
- 属性:`OrderNo``TotalAmount`
- 变量:`orderTotal``merchantId`
### B. 常用文件夹结构
```
Controllers/
Services/
Repositories/
DTOs/
Entities/
Validators/
Mappings/
Exceptions/
Constants/
Extensions/
```
### C. 必须使用的NuGet包
- Microsoft.EntityFrameworkCore
- Dapper
- AutoMapper.Extensions.Microsoft.DependencyInjection
- FluentValidation.AspNetCore
- Serilog.AspNetCore
- MediatR
- Swashbuckle.AspNetCore
- xUnit
- Moq
- FluentAssertions
---
**文档版本**v1.0
**最后更新**2025-11-22
**适用项目**外卖SaaS系统
**目标读者**AI编程助手、开发人员