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

46 KiB
Raw Blame History

外卖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:结构化日志
  • MediatRCQRS和中介者模式
  • Hangfire:后台任务调度
  • Polly:弹性和瞬态故障处理
  • Swagger/SwashbuckleAPI文档

1.3 测试框架

  • xUnit:单元测试
  • MoqMock框架
  • 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注入防护

// ❌ 错误字符串拼接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 敏感数据加密

// ✅ 密码加密存储
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 核心原则

  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. 常用命名模式

  • 类:OrderServiceMerchantRepository
  • 接口:IOrderServiceIMerchantRepository
  • 方法:CreateOrderAsyncGetOrderByIdAsync
  • 字段:_orderRepository_logger
  • 属性:OrderNoTotalAmount
  • 变量:orderTotalmerchantId

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编程助手、开发人员