- Replace Go (Kratos/Wire/Watermill) implementation with .NET 10 solution - Add FileSystem.slnx, Directory.Build.props and ABP module structure - Keep Docker/Jenkins/docker-compose deployment artifacts - This drops all Go sources (cmd/, internal/, api/proto) in favor of src/
162 lines
6.4 KiB
Markdown
162 lines
6.4 KiB
Markdown
# CLAUDE.md
|
||
|
||
> **Cross-repo rules** — see `/Users/wen/project/rag/CLAUDE.md` for full workspace conventions.
|
||
> - .NET backends share JWT key: `RagJwtSecretKey2026MustBeAtLeast32CharsLong!`
|
||
> - 鉴权:本服务用 OIDC Authority(信 SSO 签发的 RS256 JWT,框架自动发现 JWKS),不调 rag-backend gRPC
|
||
> - 其他仓库:`rag-backend`(5211)、`im-system`(5212)、`work-flow`、`order-service`、`rag-frontend`(5666 Vue)
|
||
|
||
## Build & Run
|
||
|
||
```bash
|
||
dotnet build # 全量构建(TreatWarningsAsErrors)
|
||
cd src/FileSystem.Api && dotnet run # HTTP :8080, gRPC :9090
|
||
cd src/FileSystem.Infrastructure && dotnet ef migrations add <Name> --startup-project ../FileSystem.Api
|
||
cd src/FileSystem.Infrastructure && dotnet ef database update --startup-project ../FileSystem.Api
|
||
docker compose up -d # PostgreSQL + 服务
|
||
```
|
||
|
||
## Architecture
|
||
|
||
.NET 10, ABP modular. Four projects: `Api → Application → Infrastructure → Domain`。
|
||
|
||
### ABP Module Chain
|
||
|
||
```
|
||
FileSystemApiModule [DependsOn(FileSystemApplicationModule, FileSystemInfrastructureModule)]
|
||
→ FileSystemApplicationModule [DependsOn(FileSystemInfrastructureModule)]
|
||
→ FileSystemInfrastructureModule [DependsOn(FileSystemDomainModule)]
|
||
```
|
||
|
||
DI 在 `ConfigureServices()`,中间件在 `OnApplicationInitialization()`。`Program.cs` 经 `builder.AddApplicationAsync<FileSystemApiModule>()` 引导。
|
||
|
||
### Middleware Order(不可变)
|
||
|
||
```
|
||
Cors → GlobalExceptionMiddleware → MapGrpcService<FileGrpcService> → ApiResponseMiddleware
|
||
→ Authentication → Authorization → FastEndpoints(RoutePrefix="api") → SwaggerGen
|
||
```
|
||
|
||
**`MapGrpcService` 必须在 `ApiResponseMiddleware` 之前**(否则 MemoryStream 缓冲破坏 gRPC HTTP/2 帧)。
|
||
|
||
### 鉴权
|
||
|
||
OIDC Authority 模式:`AddAuthentication("Bearer").AddJwtBearer(Authority = Jwt:Authority, MapInboundClaims=false, ValidateAudience=false, ValidateIssuer=false)`。框架自动从 SSO 发现 JWKS 验签 RS256。
|
||
|
||
全局 fallback policy(`RequireAuthenticatedUser`):所有未声明 `[AllowAnonymous]` 的 endpoint 默认要求认证。公开接口(`GetShareInfoEndpoint` / `DownloadShareEndpoint`)在 endpoint 类标注 `[AllowAnonymous]`。
|
||
|
||
gRPC 鉴权:`FileGrpcService` 类级 `[Authorize]`,`GetShareInfo`/`DownloadShare` 方法级 `[AllowAnonymous]`。
|
||
|
||
## Tech Stack
|
||
|
||
- ABP 10.3(模块化)、FastEndpoints 8.1(HTTP REPR)、MediatR 14(CQRS)
|
||
- EF Core 10 + Npgsql(PostgreSQL)、FluentValidation 12
|
||
- AWSSDK.S3(ForcePathStyle,RustFS/MinIO 兼容)
|
||
- Grpc.AspNetCore(原生 proto code-gen)
|
||
|
||
## Code Patterns
|
||
|
||
### Entity(硬删除,不软删)
|
||
|
||
file-system 的三实体(`Folder`/`FileMeta`/`ShareLink`)实现 `IAuditable + IHasOperatorIP`,**不**实现 `ISoftDelete`——忠实 Go 版硬删除语义,`Remove()` 走真实物理删除。
|
||
|
||
```csharp
|
||
public class Folder : BaseEntity, IAuditable, IHasOperatorIP
|
||
{
|
||
public Guid? ParentId { get; set; }
|
||
public string Name { get; set; } = default!;
|
||
public string OwnerId { get; set; } = default!;
|
||
// IAuditable + IHasOperatorIP 字段(= default!)
|
||
}
|
||
```
|
||
|
||
### EF Configuration(snake_case + ValueGeneratedNever)
|
||
|
||
```csharp
|
||
public class FolderConfiguration : IEntityTypeConfiguration<Folder>
|
||
{
|
||
public void Configure(EntityTypeBuilder<Folder> builder)
|
||
{
|
||
builder.ToTable("folders");
|
||
builder.HasKey(e => e.Id);
|
||
builder.Property(e => e.Id).ValueGeneratedNever();
|
||
builder.HasIndex(e => e.OwnerId);
|
||
}
|
||
}
|
||
```
|
||
|
||
自动发现(`ApplyConfigurationsFromAssembly`)。
|
||
|
||
### Command + Handler(同文件,主构造注入)
|
||
|
||
```csharp
|
||
public record CreateFolderCommand(Guid? ParentId, string Name) : IRequest<FolderDto>;
|
||
|
||
public class CreateFolderCommandHandler(FileSystemDbContext db, ICurrentUserContext currentUser, IMediator mediator)
|
||
: IRequestHandler<CreateFolderCommand, FolderDto> { ... }
|
||
```
|
||
|
||
`ValidationBehavior` 管线自动跑 FluentValidation。
|
||
|
||
### Endpoint(FastEndpoints REPR)
|
||
|
||
```csharp
|
||
public class CreateFolderEndpoint(IMediator mediator) : Endpoint<CreateFolderRequest, FolderDto>
|
||
{
|
||
public override void Configure() => Post("/folders");
|
||
public override async Task HandleAsync(CreateFolderRequest req, CancellationToken ct)
|
||
{
|
||
var result = await mediator.Send(new CreateFolderCommand(...), ct);
|
||
await Send.OkAsync(result, ct);
|
||
}
|
||
}
|
||
```
|
||
|
||
- 受保护 endpoint:全局 fallback policy 兜底认证
|
||
- 公开 endpoint:endpoint 类 `[AllowAnonymous]`
|
||
- 路由前缀 `/api/`(Module 配置)
|
||
- 文件上传:`AllowFileUploads()` + Request 的 `IFormFile File`
|
||
|
||
### gRPC(双协议共享 MediatR)
|
||
|
||
`FileGrpcService : FileService.FileServiceBase`(proto 生成),24 RPC 全部经 `IMediator.Send` 复用 Application 层 Command/Query。HTTP endpoint 与 gRPC 共享业务逻辑。proto 见 `src/FileSystem.Api/Protos/file.proto`。
|
||
|
||
### 递归 CTE 删文件夹(DeleteFolderCommand)
|
||
|
||
PostgreSQL 递归 CTE(EF Core 不原生支持,用原生 SQL):
|
||
1. 事务外 `SqlQuery<S3Ref>` 查后代 file 的 S3 引用;
|
||
2. 单事务内 `ExecuteSqlInterpolated` 递归删 files + 删 folder 子树;
|
||
3. 事务提交后 best-effort 循环删 S3(单失败只 log);
|
||
4. 发布 `FolderDeletedEvent`。
|
||
|
||
### S3 存储(S3StorageService)
|
||
|
||
AWSSDK.S3,`ForcePathStyle=true`。`CompleteMultipartUpload` 内部按 PartNumber 升序排序。NoSuchKey/NotFound → `NotFoundException`(404)。
|
||
|
||
## Conventions
|
||
|
||
- file-scoped namespaces、primary constructor DI、`record` for DTO/Command/Query
|
||
- `class` for Entity/Handler/Endpoint/Middleware
|
||
- NRT 开启 + `= default!`、`TreatWarningsAsErrors` ON
|
||
- 响应:`{ code: 0, data, message: "ok" }`(ApiResponseMiddleware 自动包裹)
|
||
- 路由前缀 `/api/`
|
||
- 错误码:401/403/400/404/409/410/413(GlobalExceptionMiddleware 映射)
|
||
|
||
## Feature Folder
|
||
|
||
```
|
||
Application/{Feature}/Commands/ Query+Handler 同文件
|
||
Application/{Feature}/Queries/
|
||
Application/{Feature}/DTOs/
|
||
Application/{Feature}/Validators/
|
||
Api/Endpoints/{Feature}/ FastEndpoint<TReq, TRes>
|
||
```
|
||
|
||
Features:`Files`、`Multipart`、`Buckets`、`Folders`、`Shares`。
|
||
|
||
## Port & Config
|
||
|
||
- HTTP `:8080`(Kestrel Http1,前端 `/file-api` 代理)
|
||
- gRPC `:9090`(Kestrel Http2)
|
||
- PostgreSQL 库 `file_system`
|
||
- `Kestrel:Limits:MaxRequestBodySize` = 100 MiB;gRPC `MaxReceiveMessageSize` = 100 MiB
|