refactor: 拆分 platform 模块(Auth/Users/Roles/Permissions/Menus/Notifications 从 rag 独立)

- 新建 modules/platform 三层模块(Domain/Application/Infrastructure)
- 搬入 8 个实体(User/Role/Permission/UserRole/RolePermission/RefreshToken/Menu/Notification)
- 新建 PlatformDbContext(连 platform 库)+ 8 个 EF 配置 + SeedData
- 搬入 92 个 Application CQRS 文件 + 57 个 Host Endpoints
- RagDbContext 瘦身:只留 RAG 专属表(KnowledgeBase/Document/ChatMessage/Conversation)
- Handler 注入改为 PlatformDbContext
- appsettings 新增 DatabaseNames:Platform
- BuildFlowHostModule 注册 PlatformDbContext + DependsOn PlatformApplicationModule
- Conversation 实体去掉 User 导航(跨模块不直接引用实体)
- rag SeedData 清空(平台种子归 platform)
- 暂移除测 platform 功能的 Rag.Tests(后续建 platform 测试项目)
- build 0/0
This commit is contained in:
xiangning 2026-07-02 18:22:17 +08:00
parent 386280a4c4
commit ff895628e5
288 changed files with 2156 additions and 1486 deletions

View File

@ -18,6 +18,11 @@
<Project Path="src/modules/im/Infrastructure/IM.Infrastructure.csproj" /> <Project Path="src/modules/im/Infrastructure/IM.Infrastructure.csproj" />
<Project Path="src/modules/im/Application/IM.Application.csproj" /> <Project Path="src/modules/im/Application/IM.Application.csproj" />
</Folder> </Folder>
<Folder Name="/src/modules/platform/">
<Project Path="src/modules/platform/Domain/Platform.Domain.csproj" />
<Project Path="src/modules/platform/Infrastructure/Platform.Infrastructure.csproj" />
<Project Path="src/modules/platform/Application/Platform.Application.csproj" />
</Folder>
<Folder Name="/src/modules/workflow/"> <Folder Name="/src/modules/workflow/">
<Project Path="src/modules/workflow/Domain/Workflow.Domain.csproj" /> <Project Path="src/modules/workflow/Domain/Workflow.Domain.csproj" />
<Project Path="src/modules/workflow/Infrastructure/Workflow.Infrastructure.csproj" /> <Project Path="src/modules/workflow/Infrastructure/Workflow.Infrastructure.csproj" />

View File

@ -17,6 +17,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\modules\rag\Application\BuildFlow.Application.csproj" /> <ProjectReference Include="..\modules\rag\Application\BuildFlow.Application.csproj" />
<ProjectReference Include="..\modules\rag\Infrastructure\BuildFlow.Infrastructure.csproj" /> <ProjectReference Include="..\modules\rag\Infrastructure\BuildFlow.Infrastructure.csproj" />
<ProjectReference Include="..\modules\platform\Application\Platform.Application.csproj" />
<ProjectReference Include="..\modules\platform\Infrastructure\Platform.Infrastructure.csproj" />
<ProjectReference Include="..\modules\file\Application\File.Application.csproj" /> <ProjectReference Include="..\modules\file\Application\File.Application.csproj" />
<ProjectReference Include="..\modules\file\Infrastructure\File.Infrastructure.csproj" /> <ProjectReference Include="..\modules\file\Infrastructure\File.Infrastructure.csproj" />
<ProjectReference Include="..\modules\im\Application\IM.Application.csproj" /> <ProjectReference Include="..\modules\im\Application\IM.Application.csproj" />

View File

@ -1,6 +1,9 @@
using BuildFlow.Host.Configuration; using BuildFlow.Host.Configuration;
using BuildFlow.Host.Hubs; using BuildFlow.Host.Hubs;
using BuildFlow.Host.Services; using BuildFlow.Host.Services.BackgroundServices;
using BuildFlow.Host.Services.Integration;
using BuildFlow.Host.Services.Notifications;
using BuildFlow.Host.Services.Users;
using FastEndpoints.Swagger; using FastEndpoints.Swagger;
using OpenTelemetry.Metrics; using OpenTelemetry.Metrics;
using OpenTelemetry.Resources; using OpenTelemetry.Resources;
@ -12,6 +15,8 @@ using File.Infrastructure;
using IM.Application; using IM.Application;
using IM.Domain.Common; using IM.Domain.Common;
using IM.Infrastructure; using IM.Infrastructure;
using BuildFlow.Platform.Application;
using BuildFlow.Platform.Infrastructure.Persistence;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Workflow.Application; using Workflow.Application;
@ -26,6 +31,7 @@ namespace BuildFlow.Host;
[DependsOn( [DependsOn(
typeof(BuildFlow.Application.RAGApplicationModule), typeof(BuildFlow.Application.RAGApplicationModule),
typeof(BuildFlow.Infrastructure.RAGInfrastructureModule), typeof(BuildFlow.Infrastructure.RAGInfrastructureModule),
typeof(PlatformApplicationModule),
typeof(FileApplicationModule), typeof(FileApplicationModule),
typeof(FileInfrastructureModule), typeof(FileInfrastructureModule),
typeof(IMApplicationModule), typeof(IMApplicationModule),
@ -88,7 +94,7 @@ public class BuildFlowHostModule : AbpModule
services.AddHostedService<WebhookDispatcherService>(); services.AddHostedService<WebhookDispatcherService>();
// 消息中心通知分发器(仅 rag 用) // 消息中心通知分发器(仅 rag 用)
services.AddScoped<BuildFlow.Application.Notifications.INotificationDispatcher, SignalRNotificationDispatcher>(); services.AddScoped<BuildFlow.Platform.Application.Notifications.INotificationDispatcher, SignalRNotificationDispatcher>();
// 进程内文件存储:覆盖 rag 原有的 HTTP 版 FileStorageClient(后注册者胜出) // 进程内文件存储:覆盖 rag 原有的 HTTP 版 FileStorageClient(后注册者胜出)
services.AddScoped<BuildFlow.Domain.Interfaces.IFileStorageClient, InProcessFileStorageClient>(); services.AddScoped<BuildFlow.Domain.Interfaces.IFileStorageClient, InProcessFileStorageClient>();
@ -96,6 +102,12 @@ public class BuildFlowHostModule : AbpModule
// JWT 认证 + CORS:使用共享内核扩展方法(消除重复的认证/跨域配置) // JWT 认证 + CORS:使用共享内核扩展方法(消除重复的认证/跨域配置)
services.AddSharedJwtAuthentication(config); services.AddSharedJwtAuthentication(config);
services.AddSharedCors(); services.AddSharedCors();
// 响应包裹中间件配置:文件下载为二进制流式响应,必须绕过 JSON 包裹与 MemoryStream 缓冲
services.Configure<BuildFlow.Shared.Api.ApiResponseMiddlewareOptions>(opts =>
{
opts.PassthroughPrefixes.Add("/api/files/download");
});
} }
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)

View File

@ -1,4 +1,4 @@
using BuildFlow.Host.Services; using BuildFlow.Host.Services.Users;
using Workflow.Application.Notifications; using Workflow.Application.Notifications;
using Workflow.Application.Scheduler; using Workflow.Application.Scheduler;

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Buckets;
/// <summary>创建存储桶端点。POST /api/buckets,按名称创建存储桶。</summary> /// <summary>创建存储桶端点。POST /api/buckets,按名称创建存储桶。</summary>
[Authorize]
public class CreateBucketEndpoint(IMediator mediator) : Endpoint<CreateBucketRequest, object> public class CreateBucketEndpoint(IMediator mediator) : Endpoint<CreateBucketRequest, object>
{ {
public override void Configure() => Post("/buckets"); public override void Configure() => Post("/buckets");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Buckets;
/// <summary>删除存储桶端点。DELETE /api/buckets,按名称删除存储桶。</summary> /// <summary>删除存储桶端点。DELETE /api/buckets,按名称删除存储桶。</summary>
[Authorize]
public class DeleteBucketEndpoint(IMediator mediator) : Endpoint<DeleteBucketRequest, object> public class DeleteBucketEndpoint(IMediator mediator) : Endpoint<DeleteBucketRequest, object>
{ {
public override void Configure() => Delete("/buckets"); public override void Configure() => Delete("/buckets");

View File

@ -5,6 +5,7 @@ using MediatR;
namespace BuildFlow.Host.Endpoints.file.Files; namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>秒传预检端点。POST /api/files/check,按哈希查同用户已存在文件。</summary> /// <summary>秒传预检端点。POST /api/files/check,按哈希查同用户已存在文件。</summary>
[Authorize]
public class CheckFileEndpoint(IMediator mediator) : Endpoint<CheckFileRequest, CheckFileResultDto> public class CheckFileEndpoint(IMediator mediator) : Endpoint<CheckFileRequest, CheckFileResultDto>
{ {
public override void Configure() => Post("/files/check"); public override void Configure() => Post("/files/check");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>删除文件端点。DELETE /api/files/delete,按对象键删除文件。</summary> /// <summary>删除文件端点。DELETE /api/files/delete,按对象键删除文件。</summary>
[Authorize]
public class DeleteFileEndpoint(IMediator mediator) : Endpoint<DeleteFileRequest, object> public class DeleteFileEndpoint(IMediator mediator) : Endpoint<DeleteFileRequest, object>
{ {
public override void Configure() => Delete("/files/delete"); public override void Configure() => Delete("/files/delete");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>文件内容端点。GET /api/files/content,返回文件文本内容。</summary> /// <summary>文件内容端点。GET /api/files/content,返回文件文本内容。</summary>
[Authorize]
public class GetFileContentEndpoint(IMediator mediator) : Endpoint<GetFileContentRequest, FileContentResult> public class GetFileContentEndpoint(IMediator mediator) : Endpoint<GetFileContentRequest, FileContentResult>
{ {
public override void Configure() => Get("/files/content"); public override void Configure() => Get("/files/content");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>文件预览端点。GET /api/files/preview,返回预签名预览 URL。</summary> /// <summary>文件预览端点。GET /api/files/preview,返回预签名预览 URL。</summary>
[Authorize]
public class GetFilePreviewEndpoint(IMediator mediator) : Endpoint<GetFilePreviewRequest, FilePreviewResult> public class GetFilePreviewEndpoint(IMediator mediator) : Endpoint<GetFilePreviewRequest, FilePreviewResult>
{ {
public override void Configure() => Get("/files/preview"); public override void Configure() => Get("/files/preview");

View File

@ -0,0 +1,21 @@
using File.Application.Features.Files.DTOs;
using File.Application.Features.Files.Queries;
using MediatR;
namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>
/// Office/PDF 文档在线预览端点。GET /api/files/office-preview,
/// 返回 KKFileView 预览地址,前端 window.open 打开即可渲染 Office 文档。
/// </summary>
[Authorize]
public class GetOfficePreviewEndpoint(IMediator mediator) : Endpoint<GetOfficePreviewRequest, OfficePreviewResult>
{
public override void Configure() => Get("/files/office-preview");
public override async Task HandleAsync(GetOfficePreviewRequest req, CancellationToken ct)
{
var result = await mediator.Send(new GetOfficePreviewQuery(req.BucketName, req.ObjectKey), ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -0,0 +1,8 @@
namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>Office/PDF 文档预览请求。以查询参数指定存储桶与对象键。</summary>
public class GetOfficePreviewRequest
{
[QueryParam] public string BucketName { get; init; } = default!;
[QueryParam] public string ObjectKey { get; init; } = default!;
}

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>列举文件端点。GET /api/files/list,支持前缀与分页查询。</summary> /// <summary>列举文件端点。GET /api/files/list,支持前缀与分页查询。</summary>
[Authorize]
public class ListFilesEndpoint(IMediator mediator) : Endpoint<ListFilesRequest, ListFilesResponse> public class ListFilesEndpoint(IMediator mediator) : Endpoint<ListFilesRequest, ListFilesResponse>
{ {
public override void Configure() => Get("/files/list"); public override void Configure() => Get("/files/list");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files;
/// <summary>上传文件端点。POST /api/files/upload,支持 multipart 文件上传。</summary> /// <summary>上传文件端点。POST /api/files/upload,支持 multipart 文件上传。</summary>
[Authorize]
public class UploadFileEndpoint(IMediator mediator) : Endpoint<UploadFileRequest, UploadFileResponse> public class UploadFileEndpoint(IMediator mediator) : Endpoint<UploadFileRequest, UploadFileResponse>
{ {
public override void Configure() public override void Configure()

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>创建文件夹端点。POST /api/folders。</summary> /// <summary>创建文件夹端点。POST /api/folders。</summary>
[Authorize]
public class CreateFolderEndpoint(IMediator mediator) : Endpoint<CreateFolderRequest, FolderDto> public class CreateFolderEndpoint(IMediator mediator) : Endpoint<CreateFolderRequest, FolderDto>
{ {
public override void Configure() => Post("/folders"); public override void Configure() => Post("/folders");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>删除文件夹端点。DELETE /api/folders/{Id}。</summary> /// <summary>删除文件夹端点。DELETE /api/folders/{Id}。</summary>
[Authorize]
public class DeleteFolderEndpoint(IMediator mediator) : Endpoint<DeleteFolderRequest, object> public class DeleteFolderEndpoint(IMediator mediator) : Endpoint<DeleteFolderRequest, object>
{ {
public override void Configure() => Delete("/folders/{Id}"); public override void Configure() => Delete("/folders/{Id}");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>获取文件夹详情端点。GET /api/folders/{Id}。</summary> /// <summary>获取文件夹详情端点。GET /api/folders/{Id}。</summary>
[Authorize]
public class GetFolderEndpoint(IMediator mediator) : Endpoint<GetFolderRequest, object> public class GetFolderEndpoint(IMediator mediator) : Endpoint<GetFolderRequest, object>
{ {
public override void Configure() => Get("/folders/{Id}"); public override void Configure() => Get("/folders/{Id}");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>移动文件到目标文件夹端点。POST /api/files/{Id}/move。</summary> /// <summary>移动文件到目标文件夹端点。POST /api/files/{Id}/move。</summary>
[Authorize]
public class MoveFileEndpoint(IMediator mediator) : Endpoint<MoveFileRequest, object> public class MoveFileEndpoint(IMediator mediator) : Endpoint<MoveFileRequest, object>
{ {
public override void Configure() => Post("/files/{Id}/move"); public override void Configure() => Post("/files/{Id}/move");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>重命名文件夹端点。PUT /api/folders/{Id}。</summary> /// <summary>重命名文件夹端点。PUT /api/folders/{Id}。</summary>
[Authorize]
public class RenameFolderEndpoint(IMediator mediator) : Endpoint<RenameFolderRequest, FolderDto> public class RenameFolderEndpoint(IMediator mediator) : Endpoint<RenameFolderRequest, FolderDto>
{ {
public override void Configure() => Put("/folders/{Id}"); public override void Configure() => Put("/folders/{Id}");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders;
/// <summary>上传文件到指定文件夹端点。POST /api/folders/{FolderId}/files。</summary> /// <summary>上传文件到指定文件夹端点。POST /api/folders/{FolderId}/files。</summary>
[Authorize]
public class UploadToFolderEndpoint(IMediator mediator) : Endpoint<UploadToFolderRequest, FileMetaDto> public class UploadToFolderEndpoint(IMediator mediator) : Endpoint<UploadToFolderRequest, FileMetaDto>
{ {
public override void Configure() public override void Configure()

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart;
/// <summary>中止分片上传端点。POST /api/files/multipart/abort。</summary> /// <summary>中止分片上传端点。POST /api/files/multipart/abort。</summary>
[Authorize]
public class AbortMultipartEndpoint(IMediator mediator) : Endpoint<AbortMultipartRequest, object> public class AbortMultipartEndpoint(IMediator mediator) : Endpoint<AbortMultipartRequest, object>
{ {
public override void Configure() => Post("/files/multipart/abort"); public override void Configure() => Post("/files/multipart/abort");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart;
/// <summary>完成分片上传端点。POST /api/files/multipart/complete。</summary> /// <summary>完成分片上传端点。POST /api/files/multipart/complete。</summary>
[Authorize]
public class CompleteMultipartEndpoint(IMediator mediator) : Endpoint<CompleteMultipartRequest, CompleteMultipartResult> public class CompleteMultipartEndpoint(IMediator mediator) : Endpoint<CompleteMultipartRequest, CompleteMultipartResult>
{ {
public override void Configure() => Post("/files/multipart/complete"); public override void Configure() => Post("/files/multipart/complete");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart;
/// <summary>初始化分片上传端点。POST /api/files/multipart/init。</summary> /// <summary>初始化分片上传端点。POST /api/files/multipart/init。</summary>
[Authorize]
public class InitMultipartEndpoint(IMediator mediator) : Endpoint<InitMultipartRequest, InitMultipartResult> public class InitMultipartEndpoint(IMediator mediator) : Endpoint<InitMultipartRequest, InitMultipartResult>
{ {
public override void Configure() => Post("/files/multipart/init"); public override void Configure() => Post("/files/multipart/init");

View File

@ -8,6 +8,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart;
/// 查询分片上传已传分片清单端点。GET /api/files/multipart/parts?upload_id=xxx。 /// 查询分片上传已传分片清单端点。GET /api/files/multipart/parts?upload_id=xxx。
/// 断点续传恢复点:前端续传前调用,据此跳过已传 partNumber。 /// 断点续传恢复点:前端续传前调用,据此跳过已传 partNumber。
/// </summary> /// </summary>
[Authorize]
public class ListPartsEndpoint(IMediator mediator) : Endpoint<ListPartsRequest, ListPartsResultDto> public class ListPartsEndpoint(IMediator mediator) : Endpoint<ListPartsRequest, ListPartsResultDto>
{ {
public override void Configure() => Get("/files/multipart/parts"); public override void Configure() => Get("/files/multipart/parts");

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart;
/// <summary>上传单个分片端点。PUT /api/files/multipart/part。</summary> /// <summary>上传单个分片端点。PUT /api/files/multipart/part。</summary>
[Authorize]
public class UploadPartEndpoint(IMediator mediator) : Endpoint<UploadPartRequest, UploadPartResult> public class UploadPartEndpoint(IMediator mediator) : Endpoint<UploadPartRequest, UploadPartResult>
{ {
public override void Configure() public override void Configure()

View File

@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Shares;
/// <summary>创建分享链接端点。POST /api/share。</summary> /// <summary>创建分享链接端点。POST /api/share。</summary>
[Authorize]
public class CreateShareEndpoint(IMediator mediator) : Endpoint<CreateShareRequest, ShareLinkDto> public class CreateShareEndpoint(IMediator mediator) : Endpoint<CreateShareRequest, ShareLinkDto>
{ {
public override void Configure() => Post("/share"); public override void Configure() => Post("/share");

View File

@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Shares;
/// <summary>删除分享端点。DELETE /api/share/{Id}。</summary> /// <summary>删除分享端点。DELETE /api/share/{Id}。</summary>
[Authorize]
public class DeleteShareEndpoint(IMediator mediator) : Endpoint<DeleteShareRequest, object> public class DeleteShareEndpoint(IMediator mediator) : Endpoint<DeleteShareRequest, object>
{ {
public override void Configure() => Delete("/share/{Id}"); public override void Configure() => Delete("/share/{Id}");

View File

@ -1,4 +1,4 @@
using BuildFlow.Infrastructure.Persistence; using BuildFlow.Platform.Infrastructure.Persistence;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;
@ -7,7 +7,7 @@ namespace BuildFlow.Host.Endpoints.rag.Auth;
/// 修改当前用户密码。验证旧密码后更新为新密码。 /// 修改当前用户密码。验证旧密码后更新为新密码。
/// POST /api/user/change-password /// POST /api/user/change-password
/// </summary> /// </summary>
public class ChangePasswordEndpoint(RagDbContext db, ICurrentUserContext userContext) public class ChangePasswordEndpoint(PlatformDbContext db, ICurrentUserContext userContext)
: Endpoint<ChangePasswordRequest> : Endpoint<ChangePasswordRequest>
{ {
public override void Configure() public override void Configure()

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.Queries; using BuildFlow.Platform.Application.Auth.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.Queries; using BuildFlow.Platform.Application.Auth.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Auth.DTOs; using BuildFlow.Platform.Application.Auth.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Auth; namespace BuildFlow.Host.Endpoints.rag.Auth;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Auth.Commands; using BuildFlow.Platform.Application.Auth.Commands;
using BuildFlow.Application.Auth.Queries; using BuildFlow.Platform.Application.Auth.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -0,0 +1,41 @@
using BuildFlow.Platform.Application.Menus.Commands;
using BuildFlow.Platform.Application.Menus.DTOs;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 创建菜单端点。
/// </summary>
public class CreateMenuEndpoint(IMediator mediator) : Endpoint<CreateMenuRequest, MenuDto>
{
public override void Configure()
{
Post("/menus");
Permissions("menu:create");
}
public override async Task HandleAsync(CreateMenuRequest req, CancellationToken ct)
{
var result = await mediator.Send(new CreateMenuCommand
{
ParentId = req.ParentId,
Name = req.Name,
Path = req.Path,
Component = req.Component,
Redirect = req.Redirect,
Title = req.Title,
Icon = req.Icon,
ActiveIcon = req.ActiveIcon,
Order = req.Order,
HideInMenu = req.HideInMenu,
KeepAlive = req.KeepAlive,
AffixTab = req.AffixTab,
Link = req.Link,
NoBasicLayout = req.NoBasicLayout,
MenuVisibleWithForbidden = req.MenuVisibleWithForbidden,
Authority = req.Authority,
}, ct);
await Send.ResponseAsync(result, 201, ct);
}
}

View File

@ -0,0 +1,27 @@
using BuildFlow.Platform.Application.Menus.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 创建菜单请求校验。
/// </summary>
public class CreateMenuRequestValidator : Validator<CreateMenuRequest>
{
public CreateMenuRequestValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("菜单路由名称不能为空")
.MaximumLength(100).WithMessage("菜单路由名称长度不能超过 100 个字符");
RuleFor(x => x.Path).NotEmpty().WithMessage("路由路径不能为空")
.MaximumLength(200).WithMessage("路由路径长度不能超过 200 个字符");
RuleFor(x => x.Title).NotEmpty().WithMessage("菜单标题不能为空")
.MaximumLength(100).WithMessage("菜单标题长度不能超过 100 个字符");
When(x => x.Component is not null, () =>
{
RuleFor(x => x.Component!).MaximumLength(200).WithMessage("组件路径长度不能超过 200 个字符");
});
When(x => x.Authority is not null, () =>
{
RuleFor(x => x.Authority!).MaximumLength(200).WithMessage("Authority 长度不能超过 200 个字符");
});
}
}

View File

@ -0,0 +1,22 @@
using BuildFlow.Platform.Application.Menus.Commands;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 删除菜单端点。
/// </summary>
public class DeleteMenuEndpoint(IMediator mediator) : Endpoint<DeleteMenuEndpointRequest>
{
public override void Configure()
{
Delete("/menus/{Id}");
Permissions("menu:delete");
}
public override async Task HandleAsync(DeleteMenuEndpointRequest req, CancellationToken ct)
{
await mediator.Send(new DeleteMenuCommand(req.Id), ct);
await Send.OkAsync(true, ct);
}
}

View File

@ -0,0 +1,6 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 删除菜单请求体。携带路由 Id。
/// </summary>
public record DeleteMenuEndpointRequest(Guid Id);

View File

@ -0,0 +1,12 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 删除菜单请求校验。
/// </summary>
public class DeleteMenuEndpointRequestValidator : Validator<DeleteMenuEndpointRequest>
{
public DeleteMenuEndpointRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("菜单 ID 不能为空");
}
}

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Menus.DTOs; using BuildFlow.Platform.Application.Menus.DTOs;
using BuildFlow.Application.Menus.Queries; using BuildFlow.Platform.Application.Menus.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -0,0 +1,23 @@
using BuildFlow.Platform.Application.Menus.DTOs;
using BuildFlow.Platform.Application.Menus.Queries;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 按主键查询单个菜单端点。
/// </summary>
public class GetMenuByIdEndpoint(IMediator mediator) : Endpoint<GetMenuByIdRequest, MenuDto>
{
public override void Configure()
{
Get("/menus/{Id}");
Permissions("menu:read");
}
public override async Task HandleAsync(GetMenuByIdRequest req, CancellationToken ct)
{
var result = await mediator.Send(new GetMenuByIdQuery(req.Id), ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -0,0 +1,6 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 按主键查询菜单请求体。携带路由 Id。
/// </summary>
public record GetMenuByIdRequest(Guid Id);

View File

@ -0,0 +1,12 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 按主键查询菜单请求校验。
/// </summary>
public class GetMenuByIdRequestValidator : Validator<GetMenuByIdRequest>
{
public GetMenuByIdRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("菜单 ID 不能为空");
}
}

View File

@ -0,0 +1,23 @@
using BuildFlow.Platform.Application.Menus.DTOs;
using BuildFlow.Platform.Application.Menus.Queries;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 菜单管理树端点。返回全部菜单,不做角色过滤,供管理端展示。
/// </summary>
public class GetMenuTreeEndpoint(IMediator mediator) : EndpointWithoutRequest<List<MenuTreeNodeDto>>
{
public override void Configure()
{
Get("/menu/tree");
Permissions("menu:read");
}
public override async Task HandleAsync(CancellationToken ct)
{
var tree = await mediator.Send(new GetMenuTreeQuery(), ct);
await Send.OkAsync(tree, ct);
}
}

View File

@ -0,0 +1,42 @@
using BuildFlow.Platform.Application.Menus.Commands;
using BuildFlow.Platform.Application.Menus.DTOs;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 更新菜单端点。
/// </summary>
public class UpdateMenuEndpoint(IMediator mediator) : Endpoint<UpdateMenuEndpointRequest, MenuDto>
{
public override void Configure()
{
Put("/menus/{Id}");
Permissions("menu:update");
}
public override async Task HandleAsync(UpdateMenuEndpointRequest req, CancellationToken ct)
{
var result = await mediator.Send(new UpdateMenuCommand
{
Id = req.Id,
ParentId = req.ParentId,
Name = req.Name,
Path = req.Path,
Component = req.Component,
Redirect = req.Redirect,
Title = req.Title,
Icon = req.Icon,
ActiveIcon = req.ActiveIcon,
Order = req.Order,
HideInMenu = req.HideInMenu,
KeepAlive = req.KeepAlive,
AffixTab = req.AffixTab,
Link = req.Link,
NoBasicLayout = req.NoBasicLayout,
MenuVisibleWithForbidden = req.MenuVisibleWithForbidden,
Authority = req.Authority,
}, ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -0,0 +1,23 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 更新菜单请求体。携带路由 Id。
/// </summary>
public record UpdateMenuEndpointRequest(
Guid Id,
Guid? ParentId,
string? Name,
string? Path,
string? Component,
string? Redirect,
string? Title,
string? Icon,
string? ActiveIcon,
int? Order,
bool? HideInMenu,
bool? KeepAlive,
bool? AffixTab,
string? Link,
bool? NoBasicLayout,
bool? MenuVisibleWithForbidden,
string? Authority);

View File

@ -0,0 +1,20 @@
namespace BuildFlow.Host.Endpoints.rag.Menus;
/// <summary>
/// 更新菜单请求校验。
/// </summary>
public class UpdateMenuEndpointRequestValidator : Validator<UpdateMenuEndpointRequest>
{
public UpdateMenuEndpointRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("菜单 ID 不能为空");
When(x => x.Name is not null, () =>
{
RuleFor(x => x.Name!).MaximumLength(100).WithMessage("菜单路由名称长度不能超过 100 个字符");
});
When(x => x.Path is not null, () =>
{
RuleFor(x => x.Path!).MaximumLength(200).WithMessage("路由路径长度不能超过 200 个字符");
});
}
}

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Notifications.DTOs; using BuildFlow.Platform.Application.Notifications.DTOs;
using BuildFlow.Application.Notifications.Queries; using BuildFlow.Platform.Application.Notifications.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Notifications.Queries; using BuildFlow.Platform.Application.Notifications.Queries;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Notifications.Commands; using BuildFlow.Platform.Application.Notifications.Commands;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Notifications.Commands; using BuildFlow.Platform.Application.Notifications.Commands;
using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Domain.Common;
using MediatR; using MediatR;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Notifications.Commands; using BuildFlow.Platform.Application.Notifications.Commands;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Notifications; namespace BuildFlow.Host.Endpoints.rag.Notifications;

View File

@ -0,0 +1,24 @@
using BuildFlow.Platform.Application.Permissions.Commands;
using BuildFlow.Platform.Application.Permissions.DTOs;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 创建权限端点。
/// </summary>
public class CreatePermissionEndpoint(IMediator mediator) : Endpoint<CreatePermissionRequest, PermissionDto>
{
public override void Configure()
{
Post("/permissions");
Permissions("permission:create");
}
public override async Task HandleAsync(CreatePermissionRequest req, CancellationToken ct)
{
var result = await mediator.Send(
new CreatePermissionCommand(req.Name, req.Code, req.Description, req.Group), ct);
await Send.ResponseAsync(result, 201, ct);
}
}

View File

@ -0,0 +1,28 @@
using BuildFlow.Platform.Application.Permissions.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 创建权限请求校验。
/// </summary>
public class CreatePermissionRequestValidator : Validator<CreatePermissionRequest>
{
public CreatePermissionRequestValidator()
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("权限名称不能为空")
.Length(2, 50).WithMessage("权限名称长度 2-50 个字符");
RuleFor(x => x.Code)
.NotEmpty().WithMessage("权限编码不能为空")
.Matches(@"^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)+$")
.WithMessage("权限编码格式应为 resource:action,且仅小写字母、数字、连字符");
When(x => x.Description is not null, () =>
{
RuleFor(x => x.Description!).MaximumLength(500).WithMessage("权限描述长度不能超过 500 个字符");
});
When(x => x.Group is not null, () =>
{
RuleFor(x => x.Group!).MaximumLength(50).WithMessage("权限分组长度不能超过 50 个字符");
});
}
}

View File

@ -0,0 +1,22 @@
using BuildFlow.Platform.Application.Permissions.Commands;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 删除权限端点。
/// </summary>
public class DeletePermissionEndpoint(IMediator mediator) : Endpoint<DeletePermissionEndpointRequest>
{
public override void Configure()
{
Delete("/permissions/{Id}");
Permissions("permission:delete");
}
public override async Task HandleAsync(DeletePermissionEndpointRequest req, CancellationToken ct)
{
await mediator.Send(new DeletePermissionCommand(req.Id), ct);
await Send.OkAsync(true, ct);
}
}

View File

@ -0,0 +1,6 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 删除权限请求体。携带路由 Id。
/// </summary>
public record DeletePermissionEndpointRequest(Guid Id);

View File

@ -0,0 +1,12 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 删除权限请求校验。
/// </summary>
public class DeletePermissionEndpointRequestValidator : Validator<DeletePermissionEndpointRequest>
{
public DeletePermissionEndpointRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("权限 ID 不能为空");
}
}

View File

@ -0,0 +1,23 @@
using BuildFlow.Platform.Application.Permissions.DTOs;
using BuildFlow.Platform.Application.Permissions.Queries;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 按主键查询单个权限端点。
/// </summary>
public class GetPermissionByIdEndpoint(IMediator mediator) : Endpoint<GetPermissionByIdRequest, PermissionDto>
{
public override void Configure()
{
Get("/permissions/{Id}");
Permissions("permission:read");
}
public override async Task HandleAsync(GetPermissionByIdRequest req, CancellationToken ct)
{
var result = await mediator.Send(new GetPermissionByIdQuery(req.Id), ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -0,0 +1,6 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 按主键查询权限请求体。携带路由 Id。
/// </summary>
public record GetPermissionByIdRequest(Guid Id);

View File

@ -0,0 +1,12 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 按主键查询权限请求校验。
/// </summary>
public class GetPermissionByIdRequestValidator : Validator<GetPermissionByIdRequest>
{
public GetPermissionByIdRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("权限 ID 不能为空");
}
}

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Permissions.DTOs; using BuildFlow.Platform.Application.Permissions.DTOs;
using BuildFlow.Application.Permissions.Queries; using BuildFlow.Platform.Application.Permissions.Queries;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Permissions; namespace BuildFlow.Host.Endpoints.rag.Permissions;

View File

@ -0,0 +1,24 @@
using BuildFlow.Platform.Application.Permissions.Commands;
using BuildFlow.Platform.Application.Permissions.DTOs;
using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 更新权限端点。编码不可改。
/// </summary>
public class UpdatePermissionEndpoint(IMediator mediator) : Endpoint<UpdatePermissionEndpointRequest, PermissionDto>
{
public override void Configure()
{
Put("/permissions/{Id}");
Permissions("permission:update");
}
public override async Task HandleAsync(UpdatePermissionEndpointRequest req, CancellationToken ct)
{
var result = await mediator.Send(
new UpdatePermissionCommand(req.Id, req.Name, req.Description, req.Group), ct);
await Send.OkAsync(result, ct);
}
}

View File

@ -0,0 +1,6 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 更新权限请求体。携带路由 Id。
/// </summary>
public record UpdatePermissionEndpointRequest(Guid Id, string? Name, string? Description, string? Group);

View File

@ -0,0 +1,24 @@
namespace BuildFlow.Host.Endpoints.rag.Permissions;
/// <summary>
/// 更新权限请求校验。
/// </summary>
public class UpdatePermissionEndpointRequestValidator : Validator<UpdatePermissionEndpointRequest>
{
public UpdatePermissionEndpointRequestValidator()
{
RuleFor(x => x.Id).NotEmpty().WithMessage("权限 ID 不能为空");
When(x => x.Name is not null, () =>
{
RuleFor(x => x.Name!).Length(2, 50).WithMessage("权限名称长度 2-50 个字符");
});
When(x => x.Description is not null, () =>
{
RuleFor(x => x.Description!).MaximumLength(500).WithMessage("权限描述长度不能超过 500 个字符");
});
When(x => x.Group is not null, () =>
{
RuleFor(x => x.Group!).MaximumLength(50).WithMessage("权限分组长度不能超过 50 个字符");
});
}
}

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Roles.Commands; using BuildFlow.Platform.Application.Roles.Commands;
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Roles.Commands; using BuildFlow.Platform.Application.Roles.Commands;
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Roles.Commands; using BuildFlow.Platform.Application.Roles.Commands;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
using BuildFlow.Application.Roles.Queries; using BuildFlow.Platform.Application.Roles.Queries;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
using BuildFlow.Application.Roles.Queries; using BuildFlow.Platform.Application.Roles.Queries;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Roles.Commands; using BuildFlow.Platform.Application.Roles.Commands;
using BuildFlow.Application.Roles.DTOs; using BuildFlow.Platform.Application.Roles.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Roles; namespace BuildFlow.Host.Endpoints.rag.Roles;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Users.Commands; using BuildFlow.Platform.Application.Users.Commands;
using BuildFlow.Application.Users.DTOs; using BuildFlow.Platform.Application.Users.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Users; namespace BuildFlow.Host.Endpoints.rag.Users;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Users.Commands; using BuildFlow.Platform.Application.Users.Commands;
using BuildFlow.Application.Users.DTOs; using BuildFlow.Platform.Application.Users.DTOs;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Users; namespace BuildFlow.Host.Endpoints.rag.Users;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Users.DTOs; using BuildFlow.Platform.Application.Users.DTOs;
namespace BuildFlow.Host.Endpoints.rag.Users; namespace BuildFlow.Host.Endpoints.rag.Users;

View File

@ -1,4 +1,4 @@
using BuildFlow.Application.Users.Commands; using BuildFlow.Platform.Application.Users.Commands;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Users; namespace BuildFlow.Host.Endpoints.rag.Users;

View File

@ -1,5 +1,5 @@
using BuildFlow.Application.Users.DTOs; using BuildFlow.Platform.Application.Users.DTOs;
using BuildFlow.Application.Users.Queries; using BuildFlow.Platform.Application.Users.Queries;
using MediatR; using MediatR;
namespace BuildFlow.Host.Endpoints.rag.Users; namespace BuildFlow.Host.Endpoints.rag.Users;

Some files were not shown because too many files have changed in this diff Show More