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

6.4 KiB
Raw Blame History

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-backend5211im-system5212work-floworder-servicerag-frontend5666 Vue

Build & Run

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.csbuilder.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 policyRequireAuthenticatedUser):所有未声明 [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() 走真实物理删除。

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

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同文件主构造注入

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

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.FileServiceBaseproto 生成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.S3ForcePathStyle=trueCompleteMultipartUpload 内部按 PartNumber 升序排序。NoSuchKey/NotFound → NotFoundException404

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>

FeaturesFilesMultipartBucketsFoldersShares

Port & Config

  • HTTP :8080Kestrel Http1前端 /file-api 代理)
  • gRPC :9090Kestrel Http2
  • PostgreSQL 库 file_system
  • Kestrel:Limits:MaxRequestBodySize = 100 MiBgRPC MaxReceiveMessageSize = 100 MiB