46 KiB
46 KiB
外卖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#命名规范
// ✅ 类名: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 数据库命名规范
-- ✅ 表名:小写,下划线分隔,复数形式
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文档注释(必须)
/// <summary>
/// 订单服务接口
/// </summary>
public interface IOrderService
{
/// <summary>
/// 创建订单
/// </summary>
/// <param name="request">订单创建请求</param>
/// <returns>订单信息</returns>
/// <exception cref="BusinessException">业务异常</exception>
Task<OrderDto> CreateOrderAsync(CreateOrderRequest request);
}
4.2 业务逻辑注释
// ✅ 复杂业务逻辑必须添加注释
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 自定义异常
// ✅ 业务异常
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 全局异常处理中间件(必须实现)
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 异常使用示例
// ✅ 正确的异常抛出
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 服务类结构(标准模板)
// ✅ 好的服务实现
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 依赖注入规则
// ✅ 使用构造函数注入
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 仓储模式(必须使用)
// ✅ 仓储接口
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 使用场景
// ✅ 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 工作单元模式(必须使用)
// ✅ 工作单元接口
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)- 写操作
// ✅ 命令定义
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)- 读操作
// ✅ 查询定义
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 验证器定义
// ✅ 使用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 验证器注册
// ✅ 在Program.cs中注册
builder.Services.AddValidatorsFromAssemblyContaining<CreateOrderRequestValidator>();
builder.Services.AddFluentValidationAutoValidation();
10. 缓存策略规范
10.1 缓存时间策略
// ✅ 缓存时间常量
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 缓存使用示例
// ✅ 使用分布式缓存(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 日志级别使用
// ✅ 正确的日志级别使用
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 结构化日志
// ✅ 使用结构化日志(推荐)
_logger.LogInformation("用户 {UserId} 创建了订单 {OrderId},金额 {Amount}",
userId, orderId, amount);
// ✅ 记录对象(使用@符号)
_logger.LogInformation("订单详情:{@Order}", order);
// ❌ 不要使用字符串拼接
_logger.LogInformation("用户 " + userId + " 创建了订单 " + orderId);
11.3 敏感信息处理
// ✅ 不要记录敏感信息
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 控制器结构(标准模板)
/// <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)
// ✅ 统一返回结果(泛型)
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 非泛型便捷封装(推荐在仅返回消息时使用)
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)
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映射(全局异常处理中间件)
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)
// ✅ 领域实体
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(数据传输对象)
// ✅ 请求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配置
// ✅ 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 提交信息规范(严格遵守)
# 格式:<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 测试命名规范
// ✅ 测试命名格式: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使用
// ✅ 使用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 数据库查询优化
// ❌ 错误: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 异步编程规范
// ✅ 正确:使用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 批量操作优化
// ❌ 错误:逐条插入
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注入防护
// ❌ 错误:字符串拼接SQL(SQL注入风险)
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 敏感数据加密
// ✅ 密码加密存储
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 授权验证
// ✅ 使用授权特性
[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 绝对禁止
// ❌ 禁止:直接在控制器或服务中使用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 核心原则
- SOLID原则:单一职责、开闭原则、里氏替换、接口隔离、依赖倒置
- DRY原则:不要重复自己(Don't Repeat Yourself)
- KISS原则:保持简单(Keep It Simple, Stupid)
- 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编程助手、开发人员