# 外卖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 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 /// /// 订单服务接口 /// public interface IOrderService { /// /// 创建订单 /// /// 订单创建请求 /// 订单信息 /// 业务异常 Task CreateOrderAsync(CreateOrderRequest request); } ``` ### 4.2 业务逻辑注释 ```csharp // ✅ 复杂业务逻辑必须添加注释 public async Task 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 Errors { get; } public ValidationException(IDictionary errors) : base("一个或多个验证错误") { Errors = errors; } } ``` ### 5.2 全局异常处理中间件(必须实现) ```csharp public class ExceptionHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _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 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 _logger; private readonly IMapper _mapper; private readonly IOptions _settings; public OrderService( IOrderRepository orderRepository, IUnitOfWork unitOfWork, ILogger logger, IMapper mapper, IOptions settings) { _orderRepository = orderRepository; _unitOfWork = unitOfWork; _logger = logger; _mapper = mapper; _settings = settings; } public async Task 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(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(); } } ``` ## 7. 数据访问规范 ### 7.1 仓储模式(必须使用) ```csharp // ✅ 仓储接口 public interface IOrderRepository { Task GetByIdAsync(Guid id); Task> GetAllAsync(); Task AddAsync(Order order); Task UpdateAsync(Order order); Task DeleteAsync(Guid id); Task ExistsAsync(Guid id); } // ✅ EF Core仓储实现 public class OrderRepository : IOrderRepository { private readonly AppDbContext _context; public OrderRepository(AppDbContext context) { _context = context; } public async Task GetByIdAsync(Guid id) { return await _context.Orders .Include(o => o.OrderItems) .Include(o => o.Customer) .FirstOrDefaultAsync(o => o.Id == id); } public async Task AddAsync(Order order) { await _context.Orders.AddAsync(order); return order; } } ``` ### 7.2 EF Core vs Dapper 使用场景 ```csharp // ✅ EF Core - 复杂查询和实体管理 public async Task 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 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(sql, new { StartDate = startDate, EndDate = endDate, Status = OrderStatus.Completed }); } // ✅ Dapper - 批量插入 public async Task BulkInsertOrdersAsync(IEnumerable 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 SaveChangesAsync(CancellationToken cancellationToken = default); Task BeginTransactionAsync(); Task CommitTransactionAsync(); Task RollbackTransactionAsync(); } // ✅ 使用工作单元 public async Task 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(order); } catch { await _unitOfWork.RollbackTransactionAsync(); throw; } } ``` ## 8. CQRS模式规范 ### 8.1 命令(Command)- 写操作 ```csharp // ✅ 命令定义 public class CreateOrderCommand : IRequest { public Guid MerchantId { get; set; } public Guid CustomerId { get; set; } public List Items { get; set; } public decimal TotalAmount { get; set; } } // ✅ 命令处理器 public class CreateOrderCommandHandler : IRequestHandler { private readonly IOrderRepository _orderRepository; private readonly IUnitOfWork _unitOfWork; private readonly ILogger _logger; public async Task 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(order); } } ``` ### 8.2 查询(Query)- 读操作 ```csharp // ✅ 查询定义 public class GetOrderByIdQuery : IRequest { public Guid OrderId { get; set; } } // ✅ 查询处理器 public class GetOrderByIdQueryHandler : IRequestHandler { private readonly IOrderRepository _orderRepository; private readonly IMapper _mapper; public async Task Handle(GetOrderByIdQuery request, CancellationToken cancellationToken) { var order = await _orderRepository.GetByIdAsync(request.OrderId); if (order == null) throw new BusinessException(404, "订单不存在"); return _mapper.Map(order); } } ``` ## 9. 验证规范(FluentValidation) ### 9.1 验证器定义 ```csharp // ✅ 使用FluentValidation public class CreateOrderRequestValidator : AbstractValidator { 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(); 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 _logger; public async Task 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(cachedData); } // 2. 缓存未命中,从数据库查询 var merchant = await _merchantRepository.GetByIdAsync(merchantId); if (merchant == null) throw new BusinessException(404, "商家不存在"); var dto = _mapper.Map(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 _logger; public async Task 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 /// /// 订单管理API /// [ApiController] [Route("api/[controller]")] [Authorize] public class OrdersController : ControllerBase { private readonly IMediator _mediator; private readonly ILogger _logger; public OrdersController(IMediator mediator, ILogger logger) { _mediator = mediator; _logger = logger; } /// /// 创建订单 /// /// 订单创建请求 /// 订单信息 /// 创建成功 /// 请求参数错误 /// 业务验证失败 [HttpPost] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] public async Task 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.SuccessResult(result)); } /// /// 获取订单详情 /// /// 订单ID /// 订单详情 [HttpGet("{id}")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task GetOrder(Guid id) { var query = new GetOrderByIdQuery { OrderId = id }; var result = await _mediator.Send(query); return Ok(ApiResponse.SuccessResult(result)); } /// /// 获取订单列表 /// /// 查询参数 /// 订单列表 [HttpGet] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task 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>.SuccessResult(result)); } } ``` ### 12.2 统一返回结果(ApiResponse) ```csharp // ✅ 统一返回结果(泛型) public class ApiResponse { 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 SuccessResult(T data, string? message = "操作成功") => new() { Success = true, Code = 200, Message = message, Data = data }; // 工厂方法:失败 public static ApiResponse Failure(int code, string message) => new() { Success = false, Code = code, Message = message }; } // ✅ 分页结果 public class PagedResult { public List 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 Success(string? message = "操作成功") => new ApiResponse { Success = true, Code = 200, Message = message, Data = null }; // ✅ 错误(配合统一错误码) public static ApiResponse Failure(int code, string message) => new ApiResponse { 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 Errors { get; } public ValidationException(IDictionary errors) : base("一个或多个验证错误") => Errors = errors; } public class ExceptionHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public ExceptionHandlingMiddleware(RequestDelegate next, ILogger 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(); ``` ## 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 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 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 Items { get; set; } public DateTime CreatedAt { get; set; } } ``` ### 13.3 AutoMapper配置 ```csharp // ✅ AutoMapper Profile public class OrderMappingProfile : Profile { public OrderMappingProfile() { CreateMap() .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() .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类型(必须使用以下之一): # 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 { 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( async () => await _orderService.CreateOrderAsync(request)); } ``` ### 15.2 Mock使用 ```csharp // ✅ 使用Moq进行Mock public class OrderServiceTests { private readonly Mock _orderRepositoryMock; private readonly Mock _unitOfWorkMock; private readonly Mock> _loggerMock; private readonly Mock _mapperMock; private readonly OrderService _orderService; public OrderServiceTests() { _orderRepositoryMock = new Mock(); _unitOfWorkMock = new Mock(); _loggerMock = new Mock>(); _mapperMock = new Mock(); _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(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> 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>(orders); } // ✅ 正确:使用Include预加载 public async Task> 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>(orders); } // ✅ 更好:大数据量使用Dapper public async Task> 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(sql, new { Status = 1, Limit = 100, Offset = 0 }); } ``` ### 16.2 异步编程规范 ```csharp // ✅ 正确:使用async/await public async Task CreateOrderAsync(CreateOrderRequest request) { var order = new Order { /* ... */ }; await _orderRepository.AddAsync(order); await _unitOfWork.SaveChangesAsync(); return _mapper.Map(order); } // ❌ 错误:不要使用.Result或.Wait() public OrderDto CreateOrder(CreateOrderRequest request) { var order = new Order { /* ... */ }; _orderRepository.AddAsync(order).Wait(); // 可能导致死锁! _unitOfWork.SaveChangesAsync().Result; // 可能导致死锁! return _mapper.Map(order); } // ❌ 错误:不要混用同步和异步 public async Task CreateOrderAsync(CreateOrderRequest request) { var order = new Order { /* ... */ }; _orderRepository.AddAsync(order).Wait(); // 错误! await _unitOfWork.SaveChangesAsync(); return _mapper.Map(order); } ``` ### 16.3 批量操作优化 ```csharp // ❌ 错误:逐条插入 public async Task ImportOrdersAsync(List orders) { foreach (var order in orders) { await _context.Orders.AddAsync(order); await _context.SaveChangesAsync(); // 每次都保存,性能差! } } // ✅ 正确:批量插入 public async Task ImportOrdersAsync(List orders) { await _context.Orders.AddRangeAsync(orders); await _context.SaveChangesAsync(); // 一次性保存 } // ✅ 更好:使用Dapper批量插入(大数据量) public async Task ImportOrdersAsync(List 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 // ❌ 错误:字符串拼接SQL(SQL注入风险) public async Task GetOrderByNoAsync(string orderNo) { var sql = $"SELECT * FROM orders WHERE order_no = '{orderNo}'"; // 危险! return await _connection.QueryFirstOrDefaultAsync(sql); } // ✅ 正确:使用参数化查询 public async Task GetOrderByNoAsync(string orderNo) { var sql = "SELECT * FROM orders WHERE order_no = @OrderNo"; return await _connection.QueryFirstOrDefaultAsync(sql, new { OrderNo = orderNo }); } ``` ### 17.2 敏感数据加密 ```csharp // ✅ 密码加密存储 public class UserService { private readonly IPasswordHasher _passwordHasher; public async Task 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 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 GetUsers() { // 只有Admin角色可以访问 } } // ✅ 基于策略的授权 [Authorize(Policy = "MerchantOwner")] public class MerchantController : ControllerBase { [HttpPut("{id}")] public async Task 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编程助手、开发人员