# 代码约定

**分析日期:** 2026-02-28

## 命名模式

### 文件
- **类/记录:** PascalCase（如 `ApplicationUser.cs`、`TenantManager.cs`、`TenantInfo.cs`）
- **枚举:** PascalCase（如 `TenantStatus`，定义在 `Tenant.cs` 中）
- **接口:** PascalCase + "I" 前缀（如 `ITenantStore.cs`、`ITenantManager.cs`）
- **配置:** PascalCase + "Configuration" 后缀（如 `TenantConfiguration.cs`）

### 目录
- **聚合文件夹:** PascalCase（如 `UserAggregate/`、`TenantAggregate/`、`RoleAggregate/`）
- **用途文件夹:** PascalCase（如 `Configurations/`、`Migrations/`）

### 命名空间
- **模式:** `Fengling.Platform.{层级}.{聚合}.{组件}`
- **示例:**
  - `Fengling.Platform.Domain.AggregatesModel.TenantAggregate`
  - `Fengling.Platform.Domain.AggregatesModel.UserAggregate`
  - `Fengling.Platform.Infrastructure`

### 类型

| 类型 | 模式 | 示例 |
|------|------|------|
| 类 | PascalCase | `TenantManager`, `PlatformDbContext` |
| 接口 | I + PascalCase | `ITenantManager`, `ITenantStore` |
| 记录 | PascalCase | `TenantInfo` |
| 枚举 | PascalCase | `TenantStatus` |
| 枚举值 | PascalCase | `Active`, `Inactive`, `Frozen` |
| 属性 | PascalCase | `TenantCode`, `CreatedAt`, `IsDeleted` |
| 方法 | PascalCase | `FindByIdAsync`, `GetAllAsync` |
| 参数 | camelCase | `tenantId`, `tenantCode`, `cancellationToken` |
| 私有字段 | camelCase | `_context`, `_tenants` |

## 代码风格

### 项目配置
- **目标框架:** .NET 10.0
- **隐式 using:** 启用
- **可空:** 启用
- **文档生成:** 启用（`<GenerateDocumentationFile>true</GenerateDocumentationFile>`）
- **集中包管理:** 启用（`<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>`）

### 格式化
- **大括号:** K&R 风格（开括号在同一行）
- **缩进:** 标准 Visual Studio 默认（4 空格）
- **行尾:** 平台默认（Unix LF，Windows CRLF）

### 全局 Using

Domain 项目 (`Fengling.Platform.Domain/GlobalUsings.cs`):
```csharp
global using NetCorePal.Extensions.Domain;
global using NetCorePal.Extensions.Primitives;
global using System.ComponentModel.DataAnnotations;
```

Infrastructure 项目 (`Fengling.Platform.Infrastructure/GlobalUsings.cs`):
```csharp
global using NetCorePal.Extensions.Domain;
global using NetCorePal.Extensions.Primitives;
global using NetCorePal.Extensions.Repository;
global using NetCorePal.Extensions.Repository.EntityFrameworkCore;
global using MediatR;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.EntityFrameworkCore.Metadata.Builders;
```

## 导入组织

### 顺序
1. 系统命名空间（通过 ImplicitUsings 隐式）
2. 外部包（MediatR、EF Core）
3. 领域命名空间（本地项目）
4. 基础设施命名空间（本地项目）

### 完全限定
- 需要时使用 `Microsoft.AspNetCore.Identity` 的完全限定（如 `ITenantStore.cs` 中的 `Microsoft.AspNetCore.Identity.IdentityResult`）

## 错误处理

### 空检查
**模式:** 显式参数空检查并抛出异常
```csharp
if (modelBuilder is null)
{
    throw new ArgumentNullException(nameof(modelBuilder));
}
```
- 位置: `PlatformDbContext.cs` 第 20-23 行

### 空返回
**模式:** 对可空结果返回 `Task.FromResult<T?>(null)`
```csharp
if (tenantId == null) return Task.FromResult<Tenant?>(null);
```
- 位置: `TenantStore.cs` 第 23 行

### 验证
- 查询中使用 `string.IsNullOrEmpty()` 进行字符串验证
- 对引用类型使用可空注解 `?` 后缀

## 记录类型

### 值对象
**模式:** 带参数的主构造函数记录
```csharp
public record TenantInfo(long? TenantId, string? TenantCode, string? TenantName)
{
    public TenantInfo(Tenant? tenant)
        : this(tenant?.Id, tenant?.TenantCode, tenant?.Name)
    {
    }
    
    public static TenantInfo Admin => new TenantInfo(null, null, null);
}
```
- 位置: `TenantInfo.cs`

## 实体设计

### 贫血领域模型
- **Tenant:** 贫血模型（带公共 setter 的数据容器）
- **ApplicationUser:** 富模型，带到 `TenantInfo` 的导航属性
- **ID 类型:** 所有实体标识使用 `long`

### 属性模式
```csharp
public long Id { get; set; }
public string TenantCode { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
public bool IsDeleted { get; set; }
```

## 类设计

### 主构造函数
**模式:** 用于依赖注入的主构造函数
```csharp
public sealed class TenantManager(ITenantStore store) : ITenantManager
{
    // 构造函数参数自动成为私有 readonly 字段
}
```
- 位置: `TenantManager.cs` 第 21 行

### 泛型约束
```csharp
public class TenantStore<TContext> : ITenantStore
where TContext : PlatformDbContext
```

### 方法返回类型
- 异步方法: 返回 `Task<T>` 或 `Task`
- 集合查询: 返回 `IList<T>` 或 `IQueryable<T>`
- 单个实体: 返回 `T?`

## 注释

### 何时注释
- **中文注释:** 用于领域概念和解释
  ```csharp
  /// <summary>
  /// 租户信息
  /// </summary>
  public record TenantInfo(...)
  ```
- **XML 文档:** 用于公共 API
- **最少行内注释:** 仅用于复杂业务逻辑

### JSDoc/TSDoc
- 对公共 API 使用 `/// <summary>` 和 `/// <param name="...">`
- 位置: `TenantInfo.cs` 第 3-8 行、11-13 行

## 函数设计

### 异步方法
- 始终接受 `CancellationToken cancellationToken = default` 作为最后一个参数
- 一致使用 `async`/`await`
- 对有返回值操作返回 `Task<T>`

### 查询方法
- **分页:** 接受 `page` 和 `pageSize` 参数
- **过滤:** 接受可选过滤参数（`name`、`tenantCode`、`status`）
- **排序:** 按创建日期应用 `OrderByDescending`

### IdentityResult 模式
业务操作返回 `IdentityResult`:
```csharp
Task<IdentityResult> CreateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<IdentityResult> UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<IdentityResult> DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default);
```

## DbContext 设计

### 配置
- **模式:** `OnModelCreating` 中的流式 API
- **实体配置:** 使用 `modelBuilder.Entity<T>(entity => {...})`
- **索引配置:** 使用 `entity.HasIndex(e => e.Property)`
- **拥有类型:** 使用 `entity.OwnsOne(e => e.TenantInfo, navigationBuilder => {...})`

### 配置发现
```csharp
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly);
```

---

*约定分析: 2026-02-28*
