file-system/CLAUDE.md
向宁 df8f1e4cb7 refactor: rewrite file-system from Go to .NET 10 / ABP modular
- 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/
2026-06-14 15:02:59 +08:00

162 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.1HTTP REPR、MediatR 14CQRS
- EF Core 10 + NpgsqlPostgreSQL、FluentValidation 12
- AWSSDK.S3ForcePathStyleRustFS/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 Configurationsnake_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。
### EndpointFastEndpoints 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 兜底认证
- 公开 endpointendpoint 类 `[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 递归 CTEEF 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/413GlobalExceptionMiddleware 映射)
## 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 MiBgRPC `MaxReceiveMessageSize` = 100 MiB