diff --git a/BuildFlow.slnx b/BuildFlow.slnx index 23b4b38..7068979 100644 --- a/BuildFlow.slnx +++ b/BuildFlow.slnx @@ -18,6 +18,11 @@ + + + + + diff --git a/src/BuildFlow.Host/BuildFlow.Host.csproj b/src/BuildFlow.Host/BuildFlow.Host.csproj index 348bbba..e35448b 100644 --- a/src/BuildFlow.Host/BuildFlow.Host.csproj +++ b/src/BuildFlow.Host/BuildFlow.Host.csproj @@ -17,6 +17,8 @@ + + diff --git a/src/BuildFlow.Host/BuildFlowHostModule.cs b/src/BuildFlow.Host/BuildFlowHostModule.cs index 0f290ed..3aab9ca 100644 --- a/src/BuildFlow.Host/BuildFlowHostModule.cs +++ b/src/BuildFlow.Host/BuildFlowHostModule.cs @@ -1,6 +1,9 @@ using BuildFlow.Host.Configuration; 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 OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -12,6 +15,8 @@ using File.Infrastructure; using IM.Application; using IM.Domain.Common; using IM.Infrastructure; +using BuildFlow.Platform.Application; +using BuildFlow.Platform.Infrastructure.Persistence; using Volo.Abp; using Volo.Abp.Modularity; using Workflow.Application; @@ -26,6 +31,7 @@ namespace BuildFlow.Host; [DependsOn( typeof(BuildFlow.Application.RAGApplicationModule), typeof(BuildFlow.Infrastructure.RAGInfrastructureModule), + typeof(PlatformApplicationModule), typeof(FileApplicationModule), typeof(FileInfrastructureModule), typeof(IMApplicationModule), @@ -88,7 +94,7 @@ public class BuildFlowHostModule : AbpModule services.AddHostedService(); // 消息中心通知分发器(仅 rag 用) - services.AddScoped(); + services.AddScoped(); // 进程内文件存储:覆盖 rag 原有的 HTTP 版 FileStorageClient(后注册者胜出) services.AddScoped(); @@ -96,6 +102,12 @@ public class BuildFlowHostModule : AbpModule // JWT 认证 + CORS:使用共享内核扩展方法(消除重复的认证/跨域配置) services.AddSharedJwtAuthentication(config); services.AddSharedCors(); + + // 响应包裹中间件配置:文件下载为二进制流式响应,必须绕过 JSON 包裹与 MemoryStream 缓冲 + services.Configure(opts => + { + opts.PassthroughPrefixes.Add("/api/files/download"); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/src/BuildFlow.Host/Configuration/NotificationConfiguration.cs b/src/BuildFlow.Host/Configuration/NotificationConfiguration.cs index 3acfa15..7b58785 100644 --- a/src/BuildFlow.Host/Configuration/NotificationConfiguration.cs +++ b/src/BuildFlow.Host/Configuration/NotificationConfiguration.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using Workflow.Application.Notifications; using Workflow.Application.Scheduler; diff --git a/src/BuildFlow.Host/Endpoints/file/Buckets/CreateBucketEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Buckets/CreateBucketEndpoint.cs index be2486c..d3d7998 100644 --- a/src/BuildFlow.Host/Endpoints/file/Buckets/CreateBucketEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Buckets/CreateBucketEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Buckets; /// 创建存储桶端点。POST /api/buckets,按名称创建存储桶。 +[Authorize] public class CreateBucketEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/buckets"); diff --git a/src/BuildFlow.Host/Endpoints/file/Buckets/DeleteBucketEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Buckets/DeleteBucketEndpoint.cs index 194c320..5351e60 100644 --- a/src/BuildFlow.Host/Endpoints/file/Buckets/DeleteBucketEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Buckets/DeleteBucketEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Buckets; /// 删除存储桶端点。DELETE /api/buckets,按名称删除存储桶。 +[Authorize] public class DeleteBucketEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Delete("/buckets"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/CheckFileEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/CheckFileEndpoint.cs index f3d3405..c795d00 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/CheckFileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/CheckFileEndpoint.cs @@ -5,6 +5,7 @@ using MediatR; namespace BuildFlow.Host.Endpoints.file.Files; /// 秒传预检端点。POST /api/files/check,按哈希查同用户已存在文件。 +[Authorize] public class CheckFileEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/files/check"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/DeleteFileEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/DeleteFileEndpoint.cs index 36c7bbb..44dd2fc 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/DeleteFileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/DeleteFileEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Files; /// 删除文件端点。DELETE /api/files/delete,按对象键删除文件。 +[Authorize] public class DeleteFileEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Delete("/files/delete"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/GetFileContentEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/GetFileContentEndpoint.cs index 8f12021..46b2742 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/GetFileContentEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/GetFileContentEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files; /// 文件内容端点。GET /api/files/content,返回文件文本内容。 +[Authorize] public class GetFileContentEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/files/content"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/GetFilePreviewEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/GetFilePreviewEndpoint.cs index 7ecb179..d028ef8 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/GetFilePreviewEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/GetFilePreviewEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files; /// 文件预览端点。GET /api/files/preview,返回预签名预览 URL。 +[Authorize] public class GetFilePreviewEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/files/preview"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewEndpoint.cs new file mode 100644 index 0000000..4ed4433 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewEndpoint.cs @@ -0,0 +1,21 @@ +using File.Application.Features.Files.DTOs; +using File.Application.Features.Files.Queries; +using MediatR; + +namespace BuildFlow.Host.Endpoints.file.Files; + +/// +/// Office/PDF 文档在线预览端点。GET /api/files/office-preview, +/// 返回 KKFileView 预览地址,前端 window.open 打开即可渲染 Office 文档。 +/// +[Authorize] +public class GetOfficePreviewEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewRequest.cs b/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewRequest.cs new file mode 100644 index 0000000..061dfdd --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/file/Files/GetOfficePreviewRequest.cs @@ -0,0 +1,8 @@ +namespace BuildFlow.Host.Endpoints.file.Files; + +/// Office/PDF 文档预览请求。以查询参数指定存储桶与对象键。 +public class GetOfficePreviewRequest +{ + [QueryParam] public string BucketName { get; init; } = default!; + [QueryParam] public string ObjectKey { get; init; } = default!; +} diff --git a/src/BuildFlow.Host/Endpoints/file/Files/ListFilesEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/ListFilesEndpoint.cs index 4158cd1..a178686 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/ListFilesEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/ListFilesEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files; /// 列举文件端点。GET /api/files/list,支持前缀与分页查询。 +[Authorize] public class ListFilesEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/files/list"); diff --git a/src/BuildFlow.Host/Endpoints/file/Files/UploadFileEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/UploadFileEndpoint.cs index d7d78a8..9511943 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/UploadFileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/UploadFileEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Files; /// 上传文件端点。POST /api/files/upload,支持 multipart 文件上传。 +[Authorize] public class UploadFileEndpoint(IMediator mediator) : Endpoint { public override void Configure() diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/CreateFolderEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/CreateFolderEndpoint.cs index 138d97b..3ff960a 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/CreateFolderEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/CreateFolderEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 创建文件夹端点。POST /api/folders。 +[Authorize] public class CreateFolderEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/folders"); diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/DeleteFolderEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/DeleteFolderEndpoint.cs index 806d2e0..85ba15a 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/DeleteFolderEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/DeleteFolderEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 删除文件夹端点。DELETE /api/folders/{Id}。 +[Authorize] public class DeleteFolderEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Delete("/folders/{Id}"); diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/GetFolderEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/GetFolderEndpoint.cs index d49d4c0..18cad1a 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/GetFolderEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/GetFolderEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 获取文件夹详情端点。GET /api/folders/{Id}。 +[Authorize] public class GetFolderEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/folders/{Id}"); diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/MoveFileEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/MoveFileEndpoint.cs index 7b83a9c..319fb4d 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/MoveFileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/MoveFileEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 移动文件到目标文件夹端点。POST /api/files/{Id}/move。 +[Authorize] public class MoveFileEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/files/{Id}/move"); diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/RenameFolderEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/RenameFolderEndpoint.cs index 0f718e8..b5275c1 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/RenameFolderEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/RenameFolderEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 重命名文件夹端点。PUT /api/folders/{Id}。 +[Authorize] public class RenameFolderEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Put("/folders/{Id}"); diff --git a/src/BuildFlow.Host/Endpoints/file/Folders/UploadToFolderEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Folders/UploadToFolderEndpoint.cs index 5bde33a..b739115 100644 --- a/src/BuildFlow.Host/Endpoints/file/Folders/UploadToFolderEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Folders/UploadToFolderEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Folders; /// 上传文件到指定文件夹端点。POST /api/folders/{FolderId}/files。 +[Authorize] public class UploadToFolderEndpoint(IMediator mediator) : Endpoint { public override void Configure() diff --git a/src/BuildFlow.Host/Endpoints/file/Multipart/AbortMultipartEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Multipart/AbortMultipartEndpoint.cs index 8ddadcb..4ca157e 100644 --- a/src/BuildFlow.Host/Endpoints/file/Multipart/AbortMultipartEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Multipart/AbortMultipartEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart; /// 中止分片上传端点。POST /api/files/multipart/abort。 +[Authorize] public class AbortMultipartEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/files/multipart/abort"); diff --git a/src/BuildFlow.Host/Endpoints/file/Multipart/CompleteMultipartEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Multipart/CompleteMultipartEndpoint.cs index 38fc1a8..f751c20 100644 --- a/src/BuildFlow.Host/Endpoints/file/Multipart/CompleteMultipartEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Multipart/CompleteMultipartEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart; /// 完成分片上传端点。POST /api/files/multipart/complete。 +[Authorize] public class CompleteMultipartEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/files/multipart/complete"); diff --git a/src/BuildFlow.Host/Endpoints/file/Multipart/InitMultipartEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Multipart/InitMultipartEndpoint.cs index fea6579..86d137a 100644 --- a/src/BuildFlow.Host/Endpoints/file/Multipart/InitMultipartEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Multipart/InitMultipartEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart; /// 初始化分片上传端点。POST /api/files/multipart/init。 +[Authorize] public class InitMultipartEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/files/multipart/init"); diff --git a/src/BuildFlow.Host/Endpoints/file/Multipart/ListPartsEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Multipart/ListPartsEndpoint.cs index ad70ff4..67a9c69 100644 --- a/src/BuildFlow.Host/Endpoints/file/Multipart/ListPartsEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Multipart/ListPartsEndpoint.cs @@ -8,6 +8,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart; /// 查询分片上传已传分片清单端点。GET /api/files/multipart/parts?upload_id=xxx。 /// 断点续传恢复点:前端续传前调用,据此跳过已传 partNumber。 /// +[Authorize] public class ListPartsEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/files/multipart/parts"); diff --git a/src/BuildFlow.Host/Endpoints/file/Multipart/UploadPartEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Multipart/UploadPartEndpoint.cs index 7ed10e9..e90e349 100644 --- a/src/BuildFlow.Host/Endpoints/file/Multipart/UploadPartEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Multipart/UploadPartEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Multipart; /// 上传单个分片端点。PUT /api/files/multipart/part。 +[Authorize] public class UploadPartEndpoint(IMediator mediator) : Endpoint { public override void Configure() diff --git a/src/BuildFlow.Host/Endpoints/file/Shares/CreateShareEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Shares/CreateShareEndpoint.cs index e6cf0f5..5ec67a4 100644 --- a/src/BuildFlow.Host/Endpoints/file/Shares/CreateShareEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Shares/CreateShareEndpoint.cs @@ -6,6 +6,7 @@ namespace BuildFlow.Host.Endpoints.file.Shares; /// 创建分享链接端点。POST /api/share。 +[Authorize] public class CreateShareEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Post("/share"); diff --git a/src/BuildFlow.Host/Endpoints/file/Shares/DeleteShareEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Shares/DeleteShareEndpoint.cs index b5b174c..cc5e532 100644 --- a/src/BuildFlow.Host/Endpoints/file/Shares/DeleteShareEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Shares/DeleteShareEndpoint.cs @@ -5,6 +5,7 @@ namespace BuildFlow.Host.Endpoints.file.Shares; /// 删除分享端点。DELETE /api/share/{Id}。 +[Authorize] public class DeleteShareEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Delete("/share/{Id}"); diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/ChangePasswordEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/ChangePasswordEndpoint.cs similarity index 89% rename from src/BuildFlow.Host/Endpoints/rag/Auth/ChangePasswordEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/ChangePasswordEndpoint.cs index f9ebbc1..55ce463 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/ChangePasswordEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/ChangePasswordEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Infrastructure.Persistence; +using BuildFlow.Platform.Infrastructure.Persistence; using BuildFlow.Shared.Domain.Common; namespace BuildFlow.Host.Endpoints.rag.Auth; @@ -7,7 +7,7 @@ namespace BuildFlow.Host.Endpoints.rag.Auth; /// 修改当前用户密码。验证旧密码后更新为新密码。 /// POST /api/user/change-password /// -public class ChangePasswordEndpoint(RagDbContext db, ICurrentUserContext userContext) +public class ChangePasswordEndpoint(PlatformDbContext db, ICurrentUserContext userContext) : Endpoint { public override void Configure() diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/ChangePasswordRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/ChangePasswordRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Auth/ChangePasswordRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/ChangePasswordRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/GetAccessCodesEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/GetAccessCodesEndpoint.cs similarity index 93% rename from src/BuildFlow.Host/Endpoints/rag/Auth/GetAccessCodesEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/GetAccessCodesEndpoint.cs index 968a55c..a164bf6 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/GetAccessCodesEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/GetAccessCodesEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Application.Auth.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/GetCurrentUserEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/GetCurrentUserEndpoint.cs similarity index 94% rename from src/BuildFlow.Host/Endpoints/rag/Auth/GetCurrentUserEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/GetCurrentUserEndpoint.cs index dc65e9b..a399bf9 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/GetCurrentUserEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/GetCurrentUserEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Application.Auth.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/LoginEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/LoginEndpoint.cs similarity index 91% rename from src/BuildFlow.Host/Endpoints/rag/Auth/LoginEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/LoginEndpoint.cs index 3a72a70..623a5a2 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/LoginEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/LoginEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Auth.Commands; -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/LoginRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/LoginRequestValidator.cs similarity index 93% rename from src/BuildFlow.Host/Endpoints/rag/Auth/LoginRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/LoginRequestValidator.cs index 479d97d..987bbbc 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/LoginRequestValidator.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/LoginRequestValidator.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.DTOs; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/LogoutEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/LogoutEndpoint.cs similarity index 94% rename from src/BuildFlow.Host/Endpoints/rag/Auth/LogoutEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/LogoutEndpoint.cs index c6fb444..323410b 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/LogoutEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/LogoutEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/RefreshTokenEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/RefreshTokenEndpoint.cs similarity index 96% rename from src/BuildFlow.Host/Endpoints/rag/Auth/RefreshTokenEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/RefreshTokenEndpoint.cs index 11550b6..c75a1d9 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/RefreshTokenEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/RefreshTokenEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/RegisterEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/RegisterEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Auth/RegisterEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/RegisterEndpoint.cs index 584a890..25cf80e 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/RegisterEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/RegisterEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Auth.Commands; -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/RegisterRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/RegisterRequestValidator.cs similarity index 93% rename from src/BuildFlow.Host/Endpoints/rag/Auth/RegisterRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/RegisterRequestValidator.cs index 1b69a12..ef20364 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/RegisterRequestValidator.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/RegisterRequestValidator.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.DTOs; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenEndpoint.cs similarity index 84% rename from src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenEndpoint.cs index 4c086da..459ac9d 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Auth.Commands; -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenRequestValidator.cs similarity index 88% rename from src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenRequestValidator.cs index 1e2f33a..abd2614 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/RevokeTokenRequestValidator.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/RevokeTokenRequestValidator.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.DTOs; namespace BuildFlow.Host.Endpoints.rag.Auth; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/UpdateProfileEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/UpdateProfileEndpoint.cs similarity index 89% rename from src/BuildFlow.Host/Endpoints/rag/Auth/UpdateProfileEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/UpdateProfileEndpoint.cs index 22f3541..f19828d 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Auth/UpdateProfileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Auth/UpdateProfileEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Auth.Commands; -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Auth/UpdateProfileRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Auth/UpdateProfileRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Auth/UpdateProfileRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Auth/UpdateProfileRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuEndpoint.cs new file mode 100644 index 0000000..a0ee9e3 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuEndpoint.cs @@ -0,0 +1,41 @@ +using BuildFlow.Platform.Application.Menus.Commands; +using BuildFlow.Platform.Application.Menus.DTOs; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 创建菜单端点。 +/// +public class CreateMenuEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuRequestValidator.cs new file mode 100644 index 0000000..448e25f --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/CreateMenuRequestValidator.cs @@ -0,0 +1,27 @@ +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 创建菜单请求校验。 +/// +public class CreateMenuRequestValidator : Validator +{ + 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 个字符"); + }); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpoint.cs new file mode 100644 index 0000000..eb0ed9f --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpoint.cs @@ -0,0 +1,22 @@ +using BuildFlow.Platform.Application.Menus.Commands; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 删除菜单端点。 +/// +public class DeleteMenuEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequest.cs new file mode 100644 index 0000000..398efb8 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 删除菜单请求体。携带路由 Id。 +/// +public record DeleteMenuEndpointRequest(Guid Id); diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequestValidator.cs new file mode 100644 index 0000000..47918be --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/DeleteMenuEndpointRequestValidator.cs @@ -0,0 +1,12 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 删除菜单请求校验。 +/// +public class DeleteMenuEndpointRequestValidator : Validator +{ + public DeleteMenuEndpointRequestValidator() + { + RuleFor(x => x.Id).NotEmpty().WithMessage("菜单 ID 不能为空"); + } +} diff --git a/src/BuildFlow.Host/Endpoints/rag/Menus/GetAllMenusEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/GetAllMenusEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Menus/GetAllMenusEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Menus/GetAllMenusEndpoint.cs index 3121111..f82e60a 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Menus/GetAllMenusEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/GetAllMenusEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Menus.DTOs; -using BuildFlow.Application.Menus.Queries; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Application.Menus.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdEndpoint.cs new file mode 100644 index 0000000..c4ae6d8 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdEndpoint.cs @@ -0,0 +1,23 @@ +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Application.Menus.Queries; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 按主键查询单个菜单端点。 +/// +public class GetMenuByIdEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequest.cs new file mode 100644 index 0000000..fb5b4d1 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 按主键查询菜单请求体。携带路由 Id。 +/// +public record GetMenuByIdRequest(Guid Id); diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequestValidator.cs new file mode 100644 index 0000000..274c1b2 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuByIdRequestValidator.cs @@ -0,0 +1,12 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 按主键查询菜单请求校验。 +/// +public class GetMenuByIdRequestValidator : Validator +{ + public GetMenuByIdRequestValidator() + { + RuleFor(x => x.Id).NotEmpty().WithMessage("菜单 ID 不能为空"); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuTreeEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuTreeEndpoint.cs new file mode 100644 index 0000000..057a575 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/GetMenuTreeEndpoint.cs @@ -0,0 +1,23 @@ +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Application.Menus.Queries; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 菜单管理树端点。返回全部菜单,不做角色过滤,供管理端展示。 +/// +public class GetMenuTreeEndpoint(IMediator mediator) : EndpointWithoutRequest> +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpoint.cs new file mode 100644 index 0000000..9dc6da6 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpoint.cs @@ -0,0 +1,42 @@ +using BuildFlow.Platform.Application.Menus.Commands; +using BuildFlow.Platform.Application.Menus.DTOs; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 更新菜单端点。 +/// +public class UpdateMenuEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequest.cs new file mode 100644 index 0000000..f33d17c --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequest.cs @@ -0,0 +1,23 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 更新菜单请求体。携带路由 Id。 +/// +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); diff --git a/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequestValidator.cs new file mode 100644 index 0000000..ea2737c --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Menus/UpdateMenuEndpointRequestValidator.cs @@ -0,0 +1,20 @@ +namespace BuildFlow.Host.Endpoints.rag.Menus; + +/// +/// 更新菜单请求校验。 +/// +public class UpdateMenuEndpointRequestValidator : Validator +{ + 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 个字符"); + }); + } +} diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsEndpoint.cs similarity index 86% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsEndpoint.cs index 2a77a29..2a4d58e 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Notifications.DTOs; -using BuildFlow.Application.Notifications.Queries; +using BuildFlow.Platform.Application.Notifications.DTOs; +using BuildFlow.Platform.Application.Notifications.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/GetNotificationsRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/GetNotificationsRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetUnreadNotificationCountEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetUnreadNotificationCountEndpoint.cs similarity index 91% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/GetUnreadNotificationCountEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/GetUnreadNotificationCountEndpoint.cs index 9ee1a11..2197afe 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Notifications/GetUnreadNotificationCountEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Notifications/GetUnreadNotificationCountEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Notifications.Queries; +using BuildFlow.Platform.Application.Notifications.Queries; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/MarkAllNotificationsReadEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/MarkAllNotificationsReadEndpoint.cs similarity index 91% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/MarkAllNotificationsReadEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/MarkAllNotificationsReadEndpoint.cs index de267c2..bf923ec 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Notifications/MarkAllNotificationsReadEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Notifications/MarkAllNotificationsReadEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Application.Notifications.Commands; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/MarkNotificationReadEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/MarkNotificationReadEndpoint.cs similarity index 92% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/MarkNotificationReadEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/MarkNotificationReadEndpoint.cs index 699c4fe..6707852 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Notifications/MarkNotificationReadEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Notifications/MarkNotificationReadEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Application.Notifications.Commands; using BuildFlow.Shared.Domain.Common; using MediatR; diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/NotificationEndpointsExtensions.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/NotificationEndpointsExtensions.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/NotificationEndpointsExtensions.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/NotificationEndpointsExtensions.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationEndpoint.cs similarity index 92% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationEndpoint.cs index 0ec763f..07f0a01 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Application.Notifications.Commands; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Notifications; diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Notifications/PublishNotificationRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Notifications/PublishNotificationRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionEndpoint.cs new file mode 100644 index 0000000..131e2db --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionEndpoint.cs @@ -0,0 +1,24 @@ +using BuildFlow.Platform.Application.Permissions.Commands; +using BuildFlow.Platform.Application.Permissions.DTOs; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 创建权限端点。 +/// +public class CreatePermissionEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionRequestValidator.cs new file mode 100644 index 0000000..756732d --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/CreatePermissionRequestValidator.cs @@ -0,0 +1,28 @@ +using BuildFlow.Platform.Application.Permissions.DTOs; + +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 创建权限请求校验。 +/// +public class CreatePermissionRequestValidator : Validator +{ + 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 个字符"); + }); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpoint.cs new file mode 100644 index 0000000..054eaef --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpoint.cs @@ -0,0 +1,22 @@ +using BuildFlow.Platform.Application.Permissions.Commands; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 删除权限端点。 +/// +public class DeletePermissionEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequest.cs new file mode 100644 index 0000000..703e12d --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 删除权限请求体。携带路由 Id。 +/// +public record DeletePermissionEndpointRequest(Guid Id); diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequestValidator.cs new file mode 100644 index 0000000..ab54761 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/DeletePermissionEndpointRequestValidator.cs @@ -0,0 +1,12 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 删除权限请求校验。 +/// +public class DeletePermissionEndpointRequestValidator : Validator +{ + public DeletePermissionEndpointRequestValidator() + { + RuleFor(x => x.Id).NotEmpty().WithMessage("权限 ID 不能为空"); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdEndpoint.cs new file mode 100644 index 0000000..2bcce7e --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdEndpoint.cs @@ -0,0 +1,23 @@ +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Application.Permissions.Queries; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 按主键查询单个权限端点。 +/// +public class GetPermissionByIdEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequest.cs new file mode 100644 index 0000000..b27bb4e --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 按主键查询权限请求体。携带路由 Id。 +/// +public record GetPermissionByIdRequest(Guid Id); diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequestValidator.cs new file mode 100644 index 0000000..2ec7f7f --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionByIdRequestValidator.cs @@ -0,0 +1,12 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 按主键查询权限请求校验。 +/// +public class GetPermissionByIdRequestValidator : Validator +{ + public GetPermissionByIdRequestValidator() + { + RuleFor(x => x.Id).NotEmpty().WithMessage("权限 ID 不能为空"); + } +} diff --git a/src/BuildFlow.Host/Endpoints/rag/Permissions/GetPermissionListEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionListEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Permissions/GetPermissionListEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionListEndpoint.cs index fe1c976..399b503 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Permissions/GetPermissionListEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/GetPermissionListEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Permissions.DTOs; -using BuildFlow.Application.Permissions.Queries; +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Application.Permissions.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Permissions; diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpoint.cs new file mode 100644 index 0000000..233de64 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpoint.cs @@ -0,0 +1,24 @@ +using BuildFlow.Platform.Application.Permissions.Commands; +using BuildFlow.Platform.Application.Permissions.DTOs; +using MediatR; + +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 更新权限端点。编码不可改。 +/// +public class UpdatePermissionEndpoint(IMediator mediator) : Endpoint +{ + 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); + } +} diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequest.cs new file mode 100644 index 0000000..759a087 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 更新权限请求体。携带路由 Id。 +/// +public record UpdatePermissionEndpointRequest(Guid Id, string? Name, string? Description, string? Group); diff --git a/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequestValidator.cs new file mode 100644 index 0000000..bd18c25 --- /dev/null +++ b/src/BuildFlow.Host/Endpoints/platform/Permissions/UpdatePermissionEndpointRequestValidator.cs @@ -0,0 +1,24 @@ +namespace BuildFlow.Host.Endpoints.rag.Permissions; + +/// +/// 更新权限请求校验。 +/// +public class UpdatePermissionEndpointRequestValidator : Validator +{ + 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 个字符"); + }); + } +} diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpoint.cs similarity index 86% rename from src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpoint.cs index d48faa9..b93f158 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Roles.Commands; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/AssignPermissionsEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/AssignPermissionsEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleEndpoint.cs index 2c215c8..32c3280 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Roles.Commands; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleRequestValidator.cs similarity index 93% rename from src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleRequestValidator.cs index 60e59e1..e62beb0 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/CreateRoleRequestValidator.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/CreateRoleRequestValidator.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpoint.cs similarity index 91% rename from src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpoint.cs index e29c86c..c052483 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.Commands; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/DeleteRoleEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/DeleteRoleEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdEndpoint.cs similarity index 84% rename from src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdEndpoint.cs index bce56ae..9bdb7f8 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Application.Roles.Queries; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleByIdRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleByIdRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleListEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleListEndpoint.cs similarity index 83% rename from src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleListEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleListEndpoint.cs index 5e7f941..f543d6e 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/GetRoleListEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/GetRoleListEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Application.Roles.Queries; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpoint.cs index e6f3fdc..b876fe3 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Roles.Commands; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Roles; diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Roles/UpdateRoleEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Roles/UpdateRoleEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpoint.cs index dc8317c..1dfd7ef 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Users.Commands; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/AssignRolesEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/AssignRolesEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/CreateUserEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/CreateUserEndpoint.cs similarity index 86% rename from src/BuildFlow.Host/Endpoints/rag/Users/CreateUserEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/CreateUserEndpoint.cs index a6e9ef0..3108c68 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/CreateUserEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/CreateUserEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Users.Commands; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/CreateUserRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Users/CreateUserRequestValidator.cs similarity index 93% rename from src/BuildFlow.Host/Endpoints/rag/Users/CreateUserRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/CreateUserRequestValidator.cs index 04b30ca..d516bae 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/CreateUserRequestValidator.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/CreateUserRequestValidator.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpoint.cs similarity index 91% rename from src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpoint.cs index a6ac870..f06fb79 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.Commands; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/DeleteUserEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/DeleteUserEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdEndpoint.cs similarity index 84% rename from src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdEndpoint.cs index 35fc2e2..186bd38 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/GetUserByIdRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/GetUserByIdRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserListEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserListEndpoint.cs similarity index 84% rename from src/BuildFlow.Host/Endpoints/rag/Users/GetUserListEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/GetUserListEndpoint.cs index 725125f..bc1db37 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserListEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserListEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/GetUserListRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Users/GetUserListRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/GetUserListRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/GetUserListRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpoint.cs b/src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpoint.cs similarity index 85% rename from src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpoint.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpoint.cs index 91cfca1..b9b8eaa 100644 --- a/src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpoint.cs @@ -1,5 +1,5 @@ -using BuildFlow.Application.Users.Commands; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; using MediatR; namespace BuildFlow.Host.Endpoints.rag.Users; diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpointRequest.cs b/src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpointRequest.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpointRequest.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpointRequest.cs diff --git a/src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpointRequestValidator.cs b/src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpointRequestValidator.cs similarity index 100% rename from src/BuildFlow.Host/Endpoints/rag/Users/UpdateUserEndpointRequestValidator.cs rename to src/BuildFlow.Host/Endpoints/platform/Users/UpdateUserEndpointRequestValidator.cs diff --git a/src/BuildFlow.Host/Endpoints/workflow/BuildFlow/Applications/CreateBuildFlowApplicationEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/BuildFlow/Applications/CreateBuildFlowApplicationEndpoint.cs index e63902b..1bb5589 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/BuildFlow/Applications/CreateBuildFlowApplicationEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/BuildFlow/Applications/CreateBuildFlowApplicationEndpoint.cs @@ -21,4 +21,4 @@ public class CreateBuildFlowApplicationEndpoint(IMediator mediator) ct); await Send.ResponseAsync(result, 201, ct); } -} +} \ No newline at end of file diff --git a/src/BuildFlow.Host/Endpoints/workflow/Notification/GetNotificationsEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/Notification/GetNotificationsEndpoint.cs index a1581d4..c519f95 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/Notification/GetNotificationsEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/Notification/GetNotificationsEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using BuildFlow.Shared.Domain.Common; using MediatR; using Workflow.Application.Common; diff --git a/src/BuildFlow.Host/Endpoints/workflow/Notification/GetUnreadNotificationCountEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/Notification/GetUnreadNotificationCountEndpoint.cs index 1ad1805..7a9f000 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/Notification/GetUnreadNotificationCountEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/Notification/GetUnreadNotificationCountEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using BuildFlow.Shared.Domain.Common; using MediatR; using Workflow.Application.Features.Notifications; diff --git a/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkAllNotificationsReadEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkAllNotificationsReadEndpoint.cs index 6dba92c..8251d25 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkAllNotificationsReadEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkAllNotificationsReadEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using BuildFlow.Shared.Domain.Common; using MediatR; using Workflow.Application.Features.Notifications; diff --git a/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkNotificationReadEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkNotificationReadEndpoint.cs index 8cba561..308f0e0 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkNotificationReadEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/Notification/MarkNotificationReadEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using BuildFlow.Shared.Domain.Common; using MediatR; using Workflow.Application.Features.Notifications; diff --git a/src/BuildFlow.Host/Endpoints/workflow/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs b/src/BuildFlow.Host/Endpoints/workflow/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs index b437a0d..8e570a8 100644 --- a/src/BuildFlow.Host/Endpoints/workflow/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/workflow/WorkflowInstance/WithdrawWorkflowInstanceEndpoint.cs @@ -1,4 +1,4 @@ -using BuildFlow.Host.Services; +using BuildFlow.Host.Services.Users; using BuildFlow.Shared.Domain.Common; using MediatR; using Workflow.Application.Features.WorkflowInstances.Commands; diff --git a/src/BuildFlow.Host/Program.cs b/src/BuildFlow.Host/Program.cs index 7b2b4ec..bda3422 100644 --- a/src/BuildFlow.Host/Program.cs +++ b/src/BuildFlow.Host/Program.cs @@ -44,6 +44,7 @@ using (var scope = app.Services.CreateScope()) // 各模块 DbContext 自动 Migrate(有 migration 的)或 EnsureCreated(无 migration 的) // rag/workflow 均有未提交的模型变更(PendingModelChanges),已在模块注册时抑制该警告 await MigrateDbAsync(sp); + await MigrateDbAsync(sp); await MigrateDbAsync(sp); await MigrateDbAsync(sp); await MigrateDbAsync(sp, suppressPendingChanges: true); @@ -59,16 +60,12 @@ using (var scope = app.Services.CreateScope()) // seed 数据 try { - var ragDb = sp.GetRequiredService(); - if (!ragDb.Users.Any()) - await BuildFlow.Infrastructure.Persistence.SeedData.SeedAsync(ragDb); - // 菜单种子独立执行:SeedMenusAsync 内部已按 Id 幂等补种(缺失才插), - // 必须始终调用——否则升级新增菜单时已有 menus 数据会卡在「库非空」跳过逻辑里拿不到新项。 - await BuildFlow.Infrastructure.Persistence.SeedData.SeedMenusAsync(ragDb); + // 平台种子(权限/角色/用户/菜单)——用 PlatformDbContext + var platformDb = sp.GetRequiredService(); + await BuildFlow.Platform.Infrastructure.Persistence.SeedData.SeedAsync(platformDb); + // workflow 种子(组件注册) var wfDb = sp.GetRequiredService(); - // 组件注册是基础设施、按 Id 幂等补插,必须始终执行——否则数据库已有流程定义时 - // (!WorkflowDefinitions.Any() 为 false)会连带跳过组件注册,导致新组件永不入库。 await SeedData.SeedAsync(wfDb); } catch (Exception ex) { Console.WriteLine($"[warn] Seed 跳过: {ex.Message}"); } diff --git a/src/BuildFlow.Host/Services/TimeoutSchedulerService.cs b/src/BuildFlow.Host/Services/BackgroundServices/TimeoutSchedulerService.cs similarity index 97% rename from src/BuildFlow.Host/Services/TimeoutSchedulerService.cs rename to src/BuildFlow.Host/Services/BackgroundServices/TimeoutSchedulerService.cs index 28b23e8..03caaec 100644 --- a/src/BuildFlow.Host/Services/TimeoutSchedulerService.cs +++ b/src/BuildFlow.Host/Services/BackgroundServices/TimeoutSchedulerService.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Options; using Workflow.Application.Notifications; using Workflow.Application.Scheduler; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.BackgroundServices; /// /// 超时任务调度器:后台 HostedService 壳,周期性调用 OverdueTaskProcessor 扫描逾期任务。 diff --git a/src/BuildFlow.Host/Services/WebhookDispatcherService.cs b/src/BuildFlow.Host/Services/BackgroundServices/WebhookDispatcherService.cs similarity index 99% rename from src/BuildFlow.Host/Services/WebhookDispatcherService.cs rename to src/BuildFlow.Host/Services/BackgroundServices/WebhookDispatcherService.cs index f9c8bfc..29bbfa8 100644 --- a/src/BuildFlow.Host/Services/WebhookDispatcherService.cs +++ b/src/BuildFlow.Host/Services/BackgroundServices/WebhookDispatcherService.cs @@ -4,7 +4,7 @@ using Workflow.Application.Notifications; using Workflow.Domain.Entities; using Workflow.Infrastructure.Persistence; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.BackgroundServices; /// /// Webhook 后台投递服务:轮询 wf_webhook_deliveries 中 status=pending 且到下次重试时间的记录, diff --git a/src/BuildFlow.Host/Services/InProcessFileStorageClient.cs b/src/BuildFlow.Host/Services/Integration/InProcessFileStorageClient.cs similarity index 83% rename from src/BuildFlow.Host/Services/InProcessFileStorageClient.cs rename to src/BuildFlow.Host/Services/Integration/InProcessFileStorageClient.cs index dc06fa9..d277cdc 100644 --- a/src/BuildFlow.Host/Services/InProcessFileStorageClient.cs +++ b/src/BuildFlow.Host/Services/Integration/InProcessFileStorageClient.cs @@ -3,7 +3,7 @@ using BuildFlow.Domain.Interfaces; using File.Application.Features.Files.Commands; using File.Application.Features.Files.Queries; -namespace BuildFlow.Host; +namespace BuildFlow.Host.Services.Integration; /// /// 进程内文件存储实现,替代 rag-backend 原有的 HTTP 版 FileStorageClient。 @@ -36,9 +36,8 @@ public class InProcessFileStorageClient(IMediator mediator) : IFileStorageClient CancellationToken ct = default) { // 通过 MediatR 查询调用 file 模块的 DownloadFileQueryHandler。 - // 注意:DownloadFileQuery 把对象 buffer 成 byte[](file 模块的查询契约),这里包成 MemoryStream 还原成 Stream。 - // 文档处理场景下文件多为 MB 级,内存拷贝可接受;如需真正的流式,可在 file 模块加一个返回 Stream 的查询。 + // DownloadFileQuery 现在直接返回 S3 原始流(不再 buffer 成 byte[]),此处原样透传。 var result = await mediator.Send(new DownloadFileQuery(bucket, objectKey), ct); - return new MemoryStream(result.Data); + return result.Stream; } } diff --git a/src/BuildFlow.Host/Services/InProcessUserInfoService.cs b/src/BuildFlow.Host/Services/Integration/InProcessUserInfoService.cs similarity index 85% rename from src/BuildFlow.Host/Services/InProcessUserInfoService.cs rename to src/BuildFlow.Host/Services/Integration/InProcessUserInfoService.cs index d7b3942..7ee3c37 100644 --- a/src/BuildFlow.Host/Services/InProcessUserInfoService.cs +++ b/src/BuildFlow.Host/Services/Integration/InProcessUserInfoService.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.Queries; using IM.Domain.Common; -namespace BuildFlow.Host; +namespace BuildFlow.Host.Services.Integration; /// /// 进程内用户信息查询实现,替代 IM 原有的 gRPC 版 AuthGrpcClientService。 /// 通过 MediatR 查询调用 rag 模块的 处理器, -/// 而非直接注入 RagDbContext——这样 im 模块对 rag 的依赖从「直接引用 DbContext」 +/// 而非直接注入 PlatformDbContext——这样 im 模块对 rag 的依赖从「直接引用 DbContext」 /// 降级为「发 MediatR 消息」,实现模块间解耦。im 只依赖消息契约,不感知 rag 的数据访问实现。 /// /// 容错策略与 gRPC 版一致:查询失败返回空字典,避免 IM 核心链路因数据库抖动而整体不可用。 @@ -24,7 +24,7 @@ public class InProcessUserInfoService(IMediator mediator) : IUserInfoService try { - // 通过 MediatR 查询调用 rag 模块的 GetUserNamesQueryHandler(它内部查 RagDbContext.Users) + // 通过 MediatR 查询调用 rag 模块的 GetUserNamesQueryHandler(它内部查 PlatformDbContext.Users) var names = await mediator.Send(new GetUserNamesQuery(idList), ct); // rag 的 User 无 Avatar 字段(gRPC 服务端也返回 Avatar=""),故头像恒为 null diff --git a/src/BuildFlow.Host/Services/SignalRNotificationDispatcher.cs b/src/BuildFlow.Host/Services/Notifications/SignalRNotificationDispatcher.cs similarity index 90% rename from src/BuildFlow.Host/Services/SignalRNotificationDispatcher.cs rename to src/BuildFlow.Host/Services/Notifications/SignalRNotificationDispatcher.cs index 5f30826..ba13a28 100644 --- a/src/BuildFlow.Host/Services/SignalRNotificationDispatcher.cs +++ b/src/BuildFlow.Host/Services/Notifications/SignalRNotificationDispatcher.cs @@ -1,9 +1,9 @@ -using BuildFlow.Application.Notifications; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Application.Notifications; +using BuildFlow.Platform.Domain.Entities; using BuildFlow.Host.Hubs; using Microsoft.AspNetCore.SignalR; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.Notifications; /// /// 基于 SignalR 的通知分发实现。把已落库的通知按接收人推送到对应分组(user:{id}), diff --git a/src/BuildFlow.Host/Services/SeedData.IM.cs b/src/BuildFlow.Host/Services/SeedData.IM.cs deleted file mode 100644 index 5d9e5ce..0000000 --- a/src/BuildFlow.Host/Services/SeedData.IM.cs +++ /dev/null @@ -1,278 +0,0 @@ -using IM.Domain.Entities; -using IM.Domain.Enums; -using IM.Infrastructure.Mongo; -using IM.Infrastructure.Persistence; -using MongoDB.Driver; -using Group = IM.Domain.Entities.Group; - -namespace BuildFlow.Host.Services; - -/// -/// IM 模块种子数据。初始化演示用的测试群组与成员关系。 -/// -public static class SeedData -{ - // Mock 用户(AdminId 匹配 rag-backend JWT 中的 sub claim) - private static readonly Guid AdminId = Guid.Parse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"); - private static readonly Guid User1Id = Guid.Parse("30000000-0000-0000-0000-000000000002"); - private static readonly Guid User2Id = Guid.Parse("30000000-0000-0000-0000-000000000003"); - private static readonly Guid User3Id = Guid.Parse("30000000-0000-0000-0000-000000000004"); - - public static async Task SeedAsync(IServiceProvider serviceProvider) - { - using var scope = serviceProvider.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); - var mongoDb = scope.ServiceProvider.GetRequiredService(); - - // 检查是否已有 seed 数据(通过固定 ID 检测) - var existingCount = await db.Conversations.CountAsync(); - if (existingCount > 0) - { - Console.WriteLine($"SeedData: 清除旧数据 ({existingCount} 条会话)..."); - db.GroupMembers.RemoveRange(db.GroupMembers); - db.Groups.RemoveRange(db.Groups); - db.ConversationParticipants.RemoveRange(db.ConversationParticipants); - db.Conversations.RemoveRange(db.Conversations); - await db.SaveChangesAsync(); - - // 清除 MongoDB 旧消息 - await mongoDb.Messages.DeleteManyAsync(FilterDefinition.Empty); - } - - var now = DateTime.UtcNow; - var systemUser = "system"; - - // ===== 1. 私聊会话:Admin ↔ User1 ===== - var privateConv1 = new Conversation - { - Id = Guid.NewGuid(), - Type = ConversationType.Private, - LastMessageContent = "项目进展如何?", - LastMessageAt = now.AddMinutes(-30), - CreatedBy = systemUser, CreatedAt = now.AddDays(-3), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-30), - OperatorIP = "127.0.0.1" - }; - - privateConv1.Participants = - [ - new ConversationParticipant - { - ConversationId = privateConv1.Id, UserId = AdminId, - JoinedAt = now.AddDays(-3), UnreadCount = 0, - CreatedBy = systemUser, CreatedAt = now.AddDays(-3), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-30) - }, - new ConversationParticipant - { - ConversationId = privateConv1.Id, UserId = User1Id, - JoinedAt = now.AddDays(-3), UnreadCount = 2, - CreatedBy = systemUser, CreatedAt = now.AddDays(-3), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-30) - } - ]; - - // ===== 2. 私聊会话:Admin ↔ User2 ===== - var privateConv2 = new Conversation - { - Id = Guid.NewGuid(), - Type = ConversationType.Private, - LastMessageContent = "下午有空讨论一下技术方案吗?", - LastMessageAt = now.AddHours(-2), - CreatedBy = systemUser, CreatedAt = now.AddDays(-1), - UpdatedBy = systemUser, UpdatedAt = now.AddHours(-2), - OperatorIP = "127.0.0.1" - }; - - privateConv2.Participants = - [ - new ConversationParticipant - { - ConversationId = privateConv2.Id, UserId = AdminId, - JoinedAt = now.AddDays(-1), UnreadCount = 1, - CreatedBy = systemUser, CreatedAt = now.AddDays(-1), - UpdatedBy = systemUser, UpdatedAt = now.AddHours(-2) - }, - new ConversationParticipant - { - ConversationId = privateConv2.Id, UserId = User2Id, - JoinedAt = now.AddDays(-1), UnreadCount = 0, - CreatedBy = systemUser, CreatedAt = now.AddDays(-1), - UpdatedBy = systemUser, UpdatedAt = now.AddHours(-2) - } - ]; - - // ===== 3. 群聊会话:项目组 ===== - var groupConv = new Conversation - { - Id = Guid.NewGuid(), - Type = ConversationType.Group, - LastMessageContent = "[图片]", - LastMessageAt = now.AddMinutes(-10), - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10), - OperatorIP = "127.0.0.1" - }; - - var group = new Group - { - Id = Guid.NewGuid(), - ConversationId = groupConv.Id, - Name = "RAG 项目组", - Description = "RAG 全栈项目开发讨论", - OwnerId = AdminId, - MaxMembers = 500, - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10), - OperatorIP = "127.0.0.1" - }; - - group.Members = - [ - new GroupMember - { - GroupId = group.Id, UserId = AdminId, Role = GroupMemberRole.Owner, - JoinedAt = now.AddDays(-5), - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddDays(-5) - }, - new GroupMember - { - GroupId = group.Id, UserId = User1Id, Role = GroupMemberRole.Admin, - JoinedAt = now.AddDays(-5), - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddDays(-5) - }, - new GroupMember - { - GroupId = group.Id, UserId = User2Id, Role = GroupMemberRole.Member, - JoinedAt = now.AddDays(-4), - CreatedBy = systemUser, CreatedAt = now.AddDays(-4), - UpdatedBy = systemUser, UpdatedAt = now.AddDays(-4) - }, - new GroupMember - { - GroupId = group.Id, UserId = User3Id, Role = GroupMemberRole.Member, - JoinedAt = now.AddDays(-3), - CreatedBy = systemUser, CreatedAt = now.AddDays(-3), - UpdatedBy = systemUser, UpdatedAt = now.AddDays(-3) - } - ]; - - groupConv.Participants = - [ - new ConversationParticipant - { - ConversationId = groupConv.Id, UserId = AdminId, - JoinedAt = now.AddDays(-5), UnreadCount = 3, - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10) - }, - new ConversationParticipant - { - ConversationId = groupConv.Id, UserId = User1Id, - JoinedAt = now.AddDays(-5), UnreadCount = 1, - CreatedBy = systemUser, CreatedAt = now.AddDays(-5), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10) - }, - new ConversationParticipant - { - ConversationId = groupConv.Id, UserId = User2Id, - JoinedAt = now.AddDays(-4), UnreadCount = 5, - CreatedBy = systemUser, CreatedAt = now.AddDays(-4), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10) - }, - new ConversationParticipant - { - ConversationId = groupConv.Id, UserId = User3Id, - JoinedAt = now.AddDays(-3), UnreadCount = 0, - CreatedBy = systemUser, CreatedAt = now.AddDays(-3), - UpdatedBy = systemUser, UpdatedAt = now.AddMinutes(-10) - } - ]; - - groupConv.Group = group; - - db.Conversations.AddRange(privateConv1, privateConv2, groupConv); - db.Groups.Add(group); - await db.SaveChangesAsync(); - - // ===== MongoDB 消息 ===== - var messages = new List(); - - // 私聊1 消息 - AddMessages(messages, privateConv1.Id, [ - (AdminId, "小赵", MessageContentType.Text, "你好,项目进展如何?", now.AddDays(-3).AddHours(1)), - (User1Id, "张三", MessageContentType.Text, "还在开发中,预计下周完成", now.AddDays(-3).AddHours(1).AddMinutes(2)), - (User1Id, "张三", MessageContentType.Text, "后端 API 已经基本完成了", now.AddDays(-3).AddHours(1).AddMinutes(3)), - (AdminId, "小赵", MessageContentType.Text, "太好了,前端这边也快了", now.AddDays(-3).AddHours(1).AddMinutes(5)), - (AdminId, "小赵", MessageContentType.Emoji, "👍", now.AddDays(-2).AddHours(3)), - (User1Id, "张三", MessageContentType.Text, "今天完成了用户模块的对接", now.AddDays(-1).AddHours(5)), - (AdminId, "小赵", MessageContentType.Text, "项目进展如何?", now.AddMinutes(-30)), - ]); - - // 私聊2 消息 - AddMessages(messages, privateConv2.Id, [ - (User2Id, "李四", MessageContentType.Text, "Hi,我是新加入的李四", now.AddDays(-1).AddHours(1)), - (AdminId, "小赵", MessageContentType.Text, "欢迎欢迎!", now.AddDays(-1).AddHours(1).AddMinutes(1)), - (User2Id, "李四", MessageContentType.Emoji, "🎉", now.AddDays(-1).AddHours(1).AddMinutes(2)), - (User2Id, "李四", MessageContentType.Text, "下午有空讨论一下技术方案吗?", now.AddHours(-2)), - ]); - - // 群聊消息 - AddMessages(messages, groupConv.Id, [ - (AdminId, "小赵", MessageContentType.Text, "大家好,这是我们的项目讨论群", now.AddDays(-5)), - (User1Id, "张三", MessageContentType.Text, "收到!", now.AddDays(-5).AddMinutes(1)), - (User2Id, "李四", MessageContentType.Text, "好的,以后在这里讨论", now.AddDays(-5).AddMinutes(2)), - (AdminId, "小赵", MessageContentType.Text, "先同步一下目前的项目进度", now.AddDays(-4)), - (AdminId, "小赵", MessageContentType.Text, "1. 后端 Auth 模块已完成\n2. 前端框架搭建完成\n3. 文件系统服务基础功能已上线", now.AddDays(-4).AddMinutes(1)), - (User1Id, "张三", MessageContentType.Text, "看起来进展很顺利", now.AddDays(-4).AddMinutes(3)), - (User3Id, "王五", MessageContentType.Emoji, "💪", now.AddDays(-3).AddHours(2)), - (User2Id, "李四", MessageContentType.Text, "我这边 IM 系统的设计文档写好了", now.AddDays(-2)), - (AdminId, "小赵", MessageContentType.Text, "不错,我们 review 一下", now.AddDays(-2).AddMinutes(10)), - (User1Id, "张三", MessageContentType.Text, "我看了,整体架构很清晰", now.AddDays(-2).AddMinutes(15)), - (User3Id, "王五", MessageContentType.Text, "MongoDB 存消息这个方案挺好的", now.AddDays(-1).AddHours(3)), - (User2Id, "李四", MessageContentType.Text, "是的,读扩散模式对群聊场景很合适", now.AddDays(-1).AddHours(3).AddMinutes(2)), - (AdminId, "小赵", MessageContentType.Text, "那就定这个方案,开始实现", now.AddDays(-1).AddHours(3).AddMinutes(5)), - (User1Id, "张三", MessageContentType.Text, "SignalR Hub 的设计也很简洁", now.AddHours(-5)), - (AdminId, "小赵", MessageContentType.Text, "对,保持简单,后续有需求再扩展", now.AddHours(-5).AddMinutes(1)), - (User3Id, "王五", MessageContentType.Text, "前端聊天页面需要支持表情选择器吗?", now.AddMinutes(-30)), - (AdminId, "小赵", MessageContentType.Text, "先做基础的文本和表情,后面再加选择器", now.AddMinutes(-30).AddMinutes(1)), - (User2Id, "李四", MessageContentType.Text, "了解了", now.AddMinutes(-20)), - (User1Id, "张三", MessageContentType.Text, "我来负责前端这块", now.AddMinutes(-15)), - (AdminId, "小赵", MessageContentType.System, "小赵 创建了群聊「RAG 项目组」", now.AddMinutes(-10)), - ]); - - // 更新 Conversation LastMessageId - privateConv1.LastMessageId = messages.Last(m => m.ConversationId == privateConv1.Id).Id; - privateConv2.LastMessageId = messages.Last(m => m.ConversationId == privateConv2.Id).Id; - groupConv.LastMessageId = messages.Last(m => m.ConversationId == groupConv.Id).Id; - await db.SaveChangesAsync(); - - if (messages.Count > 0) - { - await mongoDb.Messages.InsertManyAsync(messages); - } - - Console.WriteLine($"SeedData: 插入 {3} 个会话, {group.Members.Count} 个群成员, {messages.Count} 条消息"); - } - - private static void AddMessages( - List list, - Guid conversationId, - (Guid SenderId, string SenderName, MessageContentType Type, string Content, DateTime Time)[] items) - { - foreach (var (senderId, senderName, type, content, time) in items) - { - list.Add(new MessageDocument - { - ConversationId = conversationId, - SenderId = senderId, - SenderName = senderName, - ContentType = type, - Content = content, - CreatedAt = time - }); - } - } -} diff --git a/src/BuildFlow.Host/Services/CurrentUserContext.cs b/src/BuildFlow.Host/Services/Users/CurrentUserContext.cs similarity index 97% rename from src/BuildFlow.Host/Services/CurrentUserContext.cs rename to src/BuildFlow.Host/Services/Users/CurrentUserContext.cs index 3d3146b..0d85995 100644 --- a/src/BuildFlow.Host/Services/CurrentUserContext.cs +++ b/src/BuildFlow.Host/Services/Users/CurrentUserContext.cs @@ -2,7 +2,7 @@ using System.Security.Claims; using BuildFlow.Shared.Domain.Common; using Microsoft.IdentityModel.JsonWebTokens; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.Users; /// /// 基于 HttpContext 的当前用户上下文实现。从已认证的 JWT claims 中读取用户 ID diff --git a/src/BuildFlow.Host/Services/CurrentUserContextExtensions.cs b/src/BuildFlow.Host/Services/Users/CurrentUserContextExtensions.cs similarity index 94% rename from src/BuildFlow.Host/Services/CurrentUserContextExtensions.cs rename to src/BuildFlow.Host/Services/Users/CurrentUserContextExtensions.cs index 015b569..b9e766d 100644 --- a/src/BuildFlow.Host/Services/CurrentUserContextExtensions.cs +++ b/src/BuildFlow.Host/Services/Users/CurrentUserContextExtensions.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.Users; /// /// work-flow 业务层适配扩展:审计字段已迁移为 string(内核 ICurrentUserContext.UserId), diff --git a/src/BuildFlow.Host/Services/SystemUserContext.cs b/src/BuildFlow.Host/Services/Users/SystemUserContext.cs similarity index 95% rename from src/BuildFlow.Host/Services/SystemUserContext.cs rename to src/BuildFlow.Host/Services/Users/SystemUserContext.cs index 01a052d..a580446 100644 --- a/src/BuildFlow.Host/Services/SystemUserContext.cs +++ b/src/BuildFlow.Host/Services/Users/SystemUserContext.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Host.Services; +namespace BuildFlow.Host.Services.Users; /// /// 系统身份上下文:供后台调度器(TimeoutSchedulerService / WebhookDispatcherService)等 diff --git a/src/BuildFlow.Host/appsettings.json b/src/BuildFlow.Host/appsettings.json index 89a8e06..cf45417 100644 --- a/src/BuildFlow.Host/appsettings.json +++ b/src/BuildFlow.Host/appsettings.json @@ -26,7 +26,8 @@ "Rag": "rag", "File": "file_system", "Im": "im_system", - "Workflow": "workflow" + "Workflow": "workflow", + "Platform": "platform" }, "RabbitMq": { "Host": "43.134.235.58", @@ -40,6 +41,9 @@ "SecretKey": "REDACTED_S3_SECRET", "Region": "us-east-1" }, + "KkFileView": { + "BaseUrl": "http://localhost:8012" + }, "Jwt": { "RequireHttps": false, "SigningKey": "REDACTED_JWT_SIGNING_KEY_MIN_32_CHARS!!", diff --git a/src/modules/file/Application/Features/Files/DTOs/OfficePreviewResult.cs b/src/modules/file/Application/Features/Files/DTOs/OfficePreviewResult.cs new file mode 100644 index 0000000..a8dafaf --- /dev/null +++ b/src/modules/file/Application/Features/Files/DTOs/OfficePreviewResult.cs @@ -0,0 +1,7 @@ +namespace File.Application.Features.Files.DTOs; + +/// +/// Office/PDF 文档在线预览结果。返回 KKFileView 可直接打开的预览地址。 +/// 地址由 S3 预签名 URL 经 Base64 编码后拼入 KKFileView 的 onlinePreview 接口构成。 +/// +public record OfficePreviewResult(string PreviewUrl); diff --git a/src/modules/file/Application/Features/Files/Handlers/GetOfficePreviewQueryHandler.cs b/src/modules/file/Application/Features/Files/Handlers/GetOfficePreviewQueryHandler.cs new file mode 100644 index 0000000..353f804 --- /dev/null +++ b/src/modules/file/Application/Features/Files/Handlers/GetOfficePreviewQueryHandler.cs @@ -0,0 +1,30 @@ +using File.Application.Features.Files.DTOs; +using File.Application.Features.Files.Queries; +using File.Infrastructure.Storage; +using MediatR; +using Microsoft.Extensions.Options; + +namespace File.Application.Features.Files.Handlers; + +/// +/// Office/PDF 文档预览查询处理器。生成 S3 预签名 URL(1 小时有效), +/// 按 KKFileView 约定 Base64 编码后拼成 onlinePreview 地址返回。 +/// +public class GetOfficePreviewQueryHandler(IS3StorageService s3, IOptions kkOptions) + : IRequestHandler +{ + /// Office 文档预签名有效期较短(1 小时),降低链接泄露风险。 + private static readonly TimeSpan PreviewExpiry = TimeSpan.FromHours(1); + + public async Task Handle(GetOfficePreviewQuery req, CancellationToken ct) + { + var presignedUrl = await s3.GeneratePresignedUrlAsync(req.BucketName, req.ObjectKey, PreviewExpiry, ct); + + // KKFileView onlinePreview 约定:url 参数为 Base64 编码的文件直链 + var encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(presignedUrl)); + var baseUrl = kkOptions.Value.BaseUrl.TrimEnd('/'); + var previewUrl = $"{baseUrl}/onlinePreview?url={encoded}"; + + return new OfficePreviewResult(previewUrl); + } +} diff --git a/src/modules/file/Application/Features/Files/Queries/GetOfficePreviewQuery.cs b/src/modules/file/Application/Features/Files/Queries/GetOfficePreviewQuery.cs new file mode 100644 index 0000000..3bfb325 --- /dev/null +++ b/src/modules/file/Application/Features/Files/Queries/GetOfficePreviewQuery.cs @@ -0,0 +1,10 @@ +using File.Application.Features.Files.DTOs; +using MediatR; + +namespace File.Application.Features.Files.Queries; + +/// +/// 获取 Office/PDF 文档在线预览地址查询。 +/// 生成 S3 预签名 URL 后拼装为 KKFileView 预览链接,供前端 window.open 打开。 +/// +public record GetOfficePreviewQuery(string BucketName, string ObjectKey) : IRequest; diff --git a/src/modules/file/Infrastructure/FileInfrastructureModule.cs b/src/modules/file/Infrastructure/FileInfrastructureModule.cs index f858eaa..0066300 100644 --- a/src/modules/file/Infrastructure/FileInfrastructureModule.cs +++ b/src/modules/file/Infrastructure/FileInfrastructureModule.cs @@ -37,5 +37,8 @@ public class FileInfrastructureModule : AbpModule var options = sp.GetRequiredService>().Value; return new S3StorageService(Options.Create(options)); }); + + // KKFileView 在线预览服务配置(Office/PDF 文档预览,生成预览地址时读取) + services.Configure(config.GetSection(KkFileViewOptions.SectionName)); } } diff --git a/src/modules/file/Infrastructure/Storage/KkFileViewOptions.cs b/src/modules/file/Infrastructure/Storage/KkFileViewOptions.cs new file mode 100644 index 0000000..60b2e30 --- /dev/null +++ b/src/modules/file/Infrastructure/Storage/KkFileViewOptions.cs @@ -0,0 +1,13 @@ +namespace File.Infrastructure.Storage; + +/// +/// KKFileView 在线预览服务配置,从 appsettings.json 的 KkFileView 节读取。 +/// KKFileView 是开源文档预览服务,支持 Office/PDF/图片等 30+ 格式。 +/// +public class KkFileViewOptions +{ + public const string SectionName = "KkFileView"; + + /// KKFileView 服务基地址,如 http://localhost:8012。 + public string BaseUrl { get; set; } = default!; +} diff --git a/src/modules/rag/Application/Auth/Commands/LoginCommand.cs b/src/modules/platform/Application/Auth/Commands/LoginCommand.cs similarity index 64% rename from src/modules/rag/Application/Auth/Commands/LoginCommand.cs rename to src/modules/platform/Application/Auth/Commands/LoginCommand.cs index 6c8f3c4..2fa3d4e 100644 --- a/src/modules/rag/Application/Auth/Commands/LoginCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/LoginCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.DTOs; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// diff --git a/src/modules/rag/Application/Auth/Commands/LogoutCommand.cs b/src/modules/platform/Application/Auth/Commands/LogoutCommand.cs similarity index 74% rename from src/modules/rag/Application/Auth/Commands/LogoutCommand.cs rename to src/modules/platform/Application/Auth/Commands/LogoutCommand.cs index cb05fbd..6593372 100644 --- a/src/modules/rag/Application/Auth/Commands/LogoutCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/LogoutCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// diff --git a/src/modules/rag/Application/Auth/Commands/RefreshTokenCommand.cs b/src/modules/platform/Application/Auth/Commands/RefreshTokenCommand.cs similarity index 64% rename from src/modules/rag/Application/Auth/Commands/RefreshTokenCommand.cs rename to src/modules/platform/Application/Auth/Commands/RefreshTokenCommand.cs index 5523f83..141bc7c 100644 --- a/src/modules/rag/Application/Auth/Commands/RefreshTokenCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/RefreshTokenCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Auth.DTOs; +using BuildFlow.Platform.Application.Auth.DTOs; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// diff --git a/src/modules/rag/Application/Auth/Commands/RegisterCommand.cs b/src/modules/platform/Application/Auth/Commands/RegisterCommand.cs similarity index 77% rename from src/modules/rag/Application/Auth/Commands/RegisterCommand.cs rename to src/modules/platform/Application/Auth/Commands/RegisterCommand.cs index 2622fa2..8919709 100644 --- a/src/modules/rag/Application/Auth/Commands/RegisterCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/RegisterCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// diff --git a/src/modules/rag/Application/Auth/Commands/RevokeTokenCommand.cs b/src/modules/platform/Application/Auth/Commands/RevokeTokenCommand.cs similarity index 76% rename from src/modules/rag/Application/Auth/Commands/RevokeTokenCommand.cs rename to src/modules/platform/Application/Auth/Commands/RevokeTokenCommand.cs index 954880e..5cc4125 100644 --- a/src/modules/rag/Application/Auth/Commands/RevokeTokenCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/RevokeTokenCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// diff --git a/src/modules/rag/Application/Auth/Commands/UpdateProfileCommand.cs b/src/modules/platform/Application/Auth/Commands/UpdateProfileCommand.cs similarity index 78% rename from src/modules/rag/Application/Auth/Commands/UpdateProfileCommand.cs rename to src/modules/platform/Application/Auth/Commands/UpdateProfileCommand.cs index c7effca..3a0df7f 100644 --- a/src/modules/rag/Application/Auth/Commands/UpdateProfileCommand.cs +++ b/src/modules/platform/Application/Auth/Commands/UpdateProfileCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Application.Auth.Queries; -namespace BuildFlow.Application.Auth.Commands; +namespace BuildFlow.Platform.Application.Auth.Commands; /// /// 更新当前用户个人资料(姓名、头像、简介)。 diff --git a/src/modules/rag/Application/Auth/DTOs/LoginRequest.cs b/src/modules/platform/Application/Auth/DTOs/LoginRequest.cs similarity index 69% rename from src/modules/rag/Application/Auth/DTOs/LoginRequest.cs rename to src/modules/platform/Application/Auth/DTOs/LoginRequest.cs index 75f9448..454df37 100644 --- a/src/modules/rag/Application/Auth/DTOs/LoginRequest.cs +++ b/src/modules/platform/Application/Auth/DTOs/LoginRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Auth.DTOs; +namespace BuildFlow.Platform.Application.Auth.DTOs; /// /// 登录请求体。 diff --git a/src/modules/rag/Application/Auth/DTOs/RefreshTokenRequest.cs b/src/modules/platform/Application/Auth/DTOs/RefreshTokenRequest.cs similarity index 69% rename from src/modules/rag/Application/Auth/DTOs/RefreshTokenRequest.cs rename to src/modules/platform/Application/Auth/DTOs/RefreshTokenRequest.cs index c9c4ca1..d5ed8a0 100644 --- a/src/modules/rag/Application/Auth/DTOs/RefreshTokenRequest.cs +++ b/src/modules/platform/Application/Auth/DTOs/RefreshTokenRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Auth.DTOs; +namespace BuildFlow.Platform.Application.Auth.DTOs; /// /// 刷新令牌请求体。 diff --git a/src/modules/rag/Application/Auth/DTOs/RegisterRequest.cs b/src/modules/platform/Application/Auth/DTOs/RegisterRequest.cs similarity index 72% rename from src/modules/rag/Application/Auth/DTOs/RegisterRequest.cs rename to src/modules/platform/Application/Auth/DTOs/RegisterRequest.cs index 47ecdb0..0a6ee94 100644 --- a/src/modules/rag/Application/Auth/DTOs/RegisterRequest.cs +++ b/src/modules/platform/Application/Auth/DTOs/RegisterRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Auth.DTOs; +namespace BuildFlow.Platform.Application.Auth.DTOs; /// /// 注册请求体。 diff --git a/src/modules/rag/Application/Auth/DTOs/RevokeTokenRequest.cs b/src/modules/platform/Application/Auth/DTOs/RevokeTokenRequest.cs similarity index 69% rename from src/modules/rag/Application/Auth/DTOs/RevokeTokenRequest.cs rename to src/modules/platform/Application/Auth/DTOs/RevokeTokenRequest.cs index 7d71dd6..3992e80 100644 --- a/src/modules/rag/Application/Auth/DTOs/RevokeTokenRequest.cs +++ b/src/modules/platform/Application/Auth/DTOs/RevokeTokenRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Auth.DTOs; +namespace BuildFlow.Platform.Application.Auth.DTOs; /// /// 吊销令牌请求体。 diff --git a/src/modules/rag/Application/Auth/DTOs/TokenResponse.cs b/src/modules/platform/Application/Auth/DTOs/TokenResponse.cs similarity index 73% rename from src/modules/rag/Application/Auth/DTOs/TokenResponse.cs rename to src/modules/platform/Application/Auth/DTOs/TokenResponse.cs index a639f04..642decf 100644 --- a/src/modules/rag/Application/Auth/DTOs/TokenResponse.cs +++ b/src/modules/platform/Application/Auth/DTOs/TokenResponse.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Auth.DTOs; +namespace BuildFlow.Platform.Application.Auth.DTOs; /// /// 令牌响应体。 diff --git a/src/modules/rag/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs b/src/modules/platform/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs similarity index 73% rename from src/modules/rag/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs rename to src/modules/platform/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs index 18e70d8..9ed595a 100644 --- a/src/modules/rag/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/GetAccessCodesQueryHandler.cs @@ -1,14 +1,14 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Queries; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// GetAccessCodes查询处理器。执行业务校验后持久化。 /// -public class GetAccessCodesQueryHandler(RagDbContext db) : IRequestHandler> +public class GetAccessCodesQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetAccessCodesQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs b/src/modules/platform/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs similarity index 77% rename from src/modules/rag/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs rename to src/modules/platform/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs index 9a6ce0e..0ddd6e6 100644 --- a/src/modules/rag/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/GetCurrentUserQueryHandler.cs @@ -1,14 +1,14 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Queries; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Queries; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// GetCurrent用户查询处理器。执行业务校验后持久化。 /// -public class GetCurrentUserQueryHandler(RagDbContext db) : IRequestHandler +public class GetCurrentUserQueryHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(GetCurrentUserQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/LoginCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/LoginCommandHandler.cs similarity index 93% rename from src/modules/rag/Application/Auth/Handlers/LoginCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/LoginCommandHandler.cs index 80b480e..793afe3 100644 --- a/src/modules/rag/Application/Auth/Handlers/LoginCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/LoginCommandHandler.cs @@ -6,17 +6,17 @@ using System.Text; using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; -using BuildFlow.Application.Auth.DTOs; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 登录Command处理器。执行业务校验后持久化。 /// -public class LoginCommandHandler(RagDbContext db, IConfiguration config) : IRequestHandler +public class LoginCommandHandler(PlatformDbContext db, IConfiguration config) : IRequestHandler { // 进程内登录失败计数器(未持久化,重启即清零)。键统一小写化,避免大小写差异绕过锁定。 private static readonly Dictionary LoginAttempts = new(); diff --git a/src/modules/rag/Application/Auth/Handlers/LogoutCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/LogoutCommandHandler.cs similarity index 69% rename from src/modules/rag/Application/Auth/Handlers/LogoutCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/LogoutCommandHandler.cs index 248d0f8..09e295c 100644 --- a/src/modules/rag/Application/Auth/Handlers/LogoutCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/LogoutCommandHandler.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 登出Command处理器。执行业务校验后持久化。 /// -public class LogoutCommandHandler(RagDbContext db) : IRequestHandler +public class LogoutCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(LogoutCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/RefreshTokenCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/RefreshTokenCommandHandler.cs similarity index 88% rename from src/modules/rag/Application/Auth/Handlers/RefreshTokenCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/RefreshTokenCommandHandler.cs index 1f1e43a..53c5d4a 100644 --- a/src/modules/rag/Application/Auth/Handlers/RefreshTokenCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/RefreshTokenCommandHandler.cs @@ -5,16 +5,16 @@ using System.Text; using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; -using BuildFlow.Application.Auth.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 刷新令牌Command处理器。执行业务校验后持久化。 /// -public class RefreshTokenCommandHandler(RagDbContext db, IConfiguration config) : IRequestHandler +public class RefreshTokenCommandHandler(PlatformDbContext db, IConfiguration config) : IRequestHandler { public async Task Handle(RefreshTokenCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/RegisterCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/RegisterCommandHandler.cs similarity index 79% rename from src/modules/rag/Application/Auth/Handlers/RegisterCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/RegisterCommandHandler.cs index 1b88b6d..a10c767 100644 --- a/src/modules/rag/Application/Auth/Handlers/RegisterCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/RegisterCommandHandler.cs @@ -1,15 +1,15 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 注册Command处理器。执行业务校验后持久化。 /// -public class RegisterCommandHandler(RagDbContext db) : IRequestHandler +public class RegisterCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(RegisterCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/RevokeTokenCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/RevokeTokenCommandHandler.cs similarity index 66% rename from src/modules/rag/Application/Auth/Handlers/RevokeTokenCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/RevokeTokenCommandHandler.cs index 7dfa671..ff0b4a9 100644 --- a/src/modules/rag/Application/Auth/Handlers/RevokeTokenCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/RevokeTokenCommandHandler.cs @@ -1,14 +1,14 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 吊销令牌Command处理器。执行业务校验后持久化。 /// -public class RevokeTokenCommandHandler(RagDbContext db) : IRequestHandler +public class RevokeTokenCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(RevokeTokenCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Auth/Handlers/UpdateProfileCommandHandler.cs b/src/modules/platform/Application/Auth/Handlers/UpdateProfileCommandHandler.cs similarity index 83% rename from src/modules/rag/Application/Auth/Handlers/UpdateProfileCommandHandler.cs rename to src/modules/platform/Application/Auth/Handlers/UpdateProfileCommandHandler.cs index 9318a16..f13d7b0 100644 --- a/src/modules/rag/Application/Auth/Handlers/UpdateProfileCommandHandler.cs +++ b/src/modules/platform/Application/Auth/Handlers/UpdateProfileCommandHandler.cs @@ -1,15 +1,15 @@ using MediatR; -using BuildFlow.Application.Auth.Queries; -using BuildFlow.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Auth.Queries; +using BuildFlow.Platform.Infrastructure.Persistence; using BuildFlow.Shared.Domain.Exceptions; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Handlers; +namespace BuildFlow.Platform.Application.Auth.Handlers; /// /// 更新个人资料处理器:只更新 RealName/Avatar/Description,其他字段不动。 /// -public class UpdateProfileCommandHandler(RagDbContext db) +public class UpdateProfileCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(UpdateProfileCommand request, CancellationToken ct) diff --git a/src/modules/rag/Application/Auth/Queries/CurrentUserInfo.cs b/src/modules/platform/Application/Auth/Queries/CurrentUserInfo.cs similarity index 84% rename from src/modules/rag/Application/Auth/Queries/CurrentUserInfo.cs rename to src/modules/platform/Application/Auth/Queries/CurrentUserInfo.cs index 6c8305a..f886808 100644 --- a/src/modules/rag/Application/Auth/Queries/CurrentUserInfo.cs +++ b/src/modules/platform/Application/Auth/Queries/CurrentUserInfo.cs @@ -1,4 +1,4 @@ -namespace BuildFlow.Application.Auth.Queries; +namespace BuildFlow.Platform.Application.Auth.Queries; /// /// 当前用户信息(前端 getUserInfoApi 用)。 diff --git a/src/modules/rag/Application/Auth/Queries/GetAccessCodesQuery.cs b/src/modules/platform/Application/Auth/Queries/GetAccessCodesQuery.cs similarity index 77% rename from src/modules/rag/Application/Auth/Queries/GetAccessCodesQuery.cs rename to src/modules/platform/Application/Auth/Queries/GetAccessCodesQuery.cs index 04605a9..dad3831 100644 --- a/src/modules/rag/Application/Auth/Queries/GetAccessCodesQuery.cs +++ b/src/modules/platform/Application/Auth/Queries/GetAccessCodesQuery.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Auth.Queries; +namespace BuildFlow.Platform.Application.Auth.Queries; /// diff --git a/src/modules/rag/Application/Auth/Queries/GetCurrentUserQuery.cs b/src/modules/platform/Application/Auth/Queries/GetCurrentUserQuery.cs similarity index 77% rename from src/modules/rag/Application/Auth/Queries/GetCurrentUserQuery.cs rename to src/modules/platform/Application/Auth/Queries/GetCurrentUserQuery.cs index eb978ba..997b8e0 100644 --- a/src/modules/rag/Application/Auth/Queries/GetCurrentUserQuery.cs +++ b/src/modules/platform/Application/Auth/Queries/GetCurrentUserQuery.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Auth.Queries; +namespace BuildFlow.Platform.Application.Auth.Queries; /// diff --git a/src/modules/rag/Application/Auth/Validators/LoginCommandValidator.cs b/src/modules/platform/Application/Auth/Validators/LoginCommandValidator.cs similarity index 85% rename from src/modules/rag/Application/Auth/Validators/LoginCommandValidator.cs rename to src/modules/platform/Application/Auth/Validators/LoginCommandValidator.cs index 022b974..28b3732 100644 --- a/src/modules/rag/Application/Auth/Validators/LoginCommandValidator.cs +++ b/src/modules/platform/Application/Auth/Validators/LoginCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Validators; +namespace BuildFlow.Platform.Application.Auth.Validators; /// /// 登录Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Auth/Validators/LogoutCommandValidator.cs b/src/modules/platform/Application/Auth/Validators/LogoutCommandValidator.cs similarity index 74% rename from src/modules/rag/Application/Auth/Validators/LogoutCommandValidator.cs rename to src/modules/platform/Application/Auth/Validators/LogoutCommandValidator.cs index 5b37fc9..59ff084 100644 --- a/src/modules/rag/Application/Auth/Validators/LogoutCommandValidator.cs +++ b/src/modules/platform/Application/Auth/Validators/LogoutCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Validators; +namespace BuildFlow.Platform.Application.Auth.Validators; /// /// 登出Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Auth/Validators/RefreshTokenCommandValidator.cs b/src/modules/platform/Application/Auth/Validators/RefreshTokenCommandValidator.cs similarity index 76% rename from src/modules/rag/Application/Auth/Validators/RefreshTokenCommandValidator.cs rename to src/modules/platform/Application/Auth/Validators/RefreshTokenCommandValidator.cs index ca1efb0..8414651 100644 --- a/src/modules/rag/Application/Auth/Validators/RefreshTokenCommandValidator.cs +++ b/src/modules/platform/Application/Auth/Validators/RefreshTokenCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Validators; +namespace BuildFlow.Platform.Application.Auth.Validators; /// /// 刷新令牌Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Auth/Validators/RegisterCommandValidator.cs b/src/modules/platform/Application/Auth/Validators/RegisterCommandValidator.cs similarity index 86% rename from src/modules/rag/Application/Auth/Validators/RegisterCommandValidator.cs rename to src/modules/platform/Application/Auth/Validators/RegisterCommandValidator.cs index c2d1efe..7b499f2 100644 --- a/src/modules/rag/Application/Auth/Validators/RegisterCommandValidator.cs +++ b/src/modules/platform/Application/Auth/Validators/RegisterCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Validators; +namespace BuildFlow.Platform.Application.Auth.Validators; /// /// 注册Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Auth/Validators/RevokeTokenCommandValidator.cs b/src/modules/platform/Application/Auth/Validators/RevokeTokenCommandValidator.cs similarity index 76% rename from src/modules/rag/Application/Auth/Validators/RevokeTokenCommandValidator.cs rename to src/modules/platform/Application/Auth/Validators/RevokeTokenCommandValidator.cs index d972262..4f69355 100644 --- a/src/modules/rag/Application/Auth/Validators/RevokeTokenCommandValidator.cs +++ b/src/modules/platform/Application/Auth/Validators/RevokeTokenCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Auth.Commands; +using BuildFlow.Platform.Application.Auth.Commands; -namespace BuildFlow.Application.Auth.Validators; +namespace BuildFlow.Platform.Application.Auth.Validators; /// /// 吊销令牌Command的请求校验器(FluentValidation)。 diff --git a/src/modules/platform/Application/Common/ValidationBehavior.cs b/src/modules/platform/Application/Common/ValidationBehavior.cs new file mode 100644 index 0000000..ad5baa3 --- /dev/null +++ b/src/modules/platform/Application/Common/ValidationBehavior.cs @@ -0,0 +1,28 @@ +using FluentValidation; +using MediatR; + +namespace BuildFlow.Platform.Application.Common; + +/// +/// MediatR 管线行为:在 Handler 执行前自动运行所有匹配的 FluentValidation 校验器。 +/// 任一校验失败即抛 ValidationException(被 GlobalExceptionMiddleware 映射为 400), +/// 使业务 Handler 内无需重复写校验代码,统一在管线层拦截非法请求。 +/// +public class ValidationBehavior(IEnumerable> validators) + : IPipelineBehavior where TRequest : IRequest +{ + public async Task Handle(TRequest request, RequestHandlerDelegate next, + CancellationToken cancellationToken) + { + if (validators.Any()) + { + var context = new ValidationContext(request); + var results = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken))); + var failures = results.SelectMany(r => r.Errors).Where(f => f != null).ToList(); + if (failures.Count != 0) + throw new ValidationException(failures); + } + + return await next(); + } +} diff --git a/src/modules/platform/Application/GlobalUsings.cs b/src/modules/platform/Application/GlobalUsings.cs new file mode 100644 index 0000000..a5ee52f --- /dev/null +++ b/src/modules/platform/Application/GlobalUsings.cs @@ -0,0 +1,2 @@ +// 全局 using:Handler 普遍使用 EF Core 异步查询扩展方法 +global using Microsoft.EntityFrameworkCore; diff --git a/src/modules/platform/Application/Menus/Commands/CreateMenuCommand.cs b/src/modules/platform/Application/Menus/Commands/CreateMenuCommand.cs new file mode 100644 index 0000000..5f95c2e --- /dev/null +++ b/src/modules/platform/Application/Menus/Commands/CreateMenuCommand.cs @@ -0,0 +1,27 @@ +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Platform.Application.Menus.Commands; + +/// +/// 创建菜单命令。 +/// +public record CreateMenuCommand : IRequest +{ + public Guid? ParentId { get; init; } + public string Name { get; init; } = default!; + public string Path { get; init; } = default!; + public string? Component { get; init; } + public string? Redirect { get; init; } + public string Title { get; init; } = default!; + public string? Icon { get; init; } + public string? ActiveIcon { get; init; } + public int Order { get; init; } + public bool HideInMenu { get; init; } + public bool? KeepAlive { get; init; } + public bool? AffixTab { get; init; } + public string? Link { get; init; } + public bool NoBasicLayout { get; init; } + public bool MenuVisibleWithForbidden { get; init; } + public string? Authority { get; init; } +} diff --git a/src/modules/platform/Application/Menus/Commands/DeleteMenuCommand.cs b/src/modules/platform/Application/Menus/Commands/DeleteMenuCommand.cs new file mode 100644 index 0000000..25976aa --- /dev/null +++ b/src/modules/platform/Application/Menus/Commands/DeleteMenuCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace BuildFlow.Platform.Application.Menus.Commands; + +/// +/// 删除菜单命令。有子菜单时拒绝删除,避免误删整个子树。 +/// +public record DeleteMenuCommand(Guid Id) : IRequest; diff --git a/src/modules/platform/Application/Menus/Commands/UpdateMenuCommand.cs b/src/modules/platform/Application/Menus/Commands/UpdateMenuCommand.cs new file mode 100644 index 0000000..cbdeea8 --- /dev/null +++ b/src/modules/platform/Application/Menus/Commands/UpdateMenuCommand.cs @@ -0,0 +1,28 @@ +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Platform.Application.Menus.Commands; + +/// +/// 更新菜单命令。支持部分字段更新。 +/// +public record UpdateMenuCommand : IRequest +{ + public Guid Id { get; init; } + public Guid? ParentId { get; init; } + public string? Name { get; init; } + public string? Path { get; init; } + public string? Component { get; init; } + public string? Redirect { get; init; } + public string? Title { get; init; } + public string? Icon { get; init; } + public string? ActiveIcon { get; init; } + public int? Order { get; init; } + public bool? HideInMenu { get; init; } + public bool? KeepAlive { get; init; } + public bool? AffixTab { get; init; } + public string? Link { get; init; } + public bool? NoBasicLayout { get; init; } + public bool? MenuVisibleWithForbidden { get; init; } + public string? Authority { get; init; } +} diff --git a/src/modules/platform/Application/Menus/DTOs/CreateMenuRequest.cs b/src/modules/platform/Application/Menus/DTOs/CreateMenuRequest.cs new file mode 100644 index 0000000..fca9829 --- /dev/null +++ b/src/modules/platform/Application/Menus/DTOs/CreateMenuRequest.cs @@ -0,0 +1,22 @@ +namespace BuildFlow.Platform.Application.Menus.DTOs; + +/// +/// 创建菜单请求体。 +/// +public record CreateMenuRequest( + 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); diff --git a/src/modules/platform/Application/Menus/DTOs/MenuDto.cs b/src/modules/platform/Application/Menus/DTOs/MenuDto.cs new file mode 100644 index 0000000..92b663a --- /dev/null +++ b/src/modules/platform/Application/Menus/DTOs/MenuDto.cs @@ -0,0 +1,24 @@ +namespace BuildFlow.Platform.Application.Menus.DTOs; + +/// +/// 菜单扁平 DTO,用于管理端增删改查与列表展示。 +/// 区别于 MenuRouteDto:包含全部管理字段(ParentId/Authority 等),不做角色过滤,非树形。 +/// +public record MenuDto( + 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); diff --git a/src/modules/rag/Application/Menus/DTOs/MenuMetaDto.cs b/src/modules/platform/Application/Menus/DTOs/MenuMetaDto.cs similarity index 94% rename from src/modules/rag/Application/Menus/DTOs/MenuMetaDto.cs rename to src/modules/platform/Application/Menus/DTOs/MenuMetaDto.cs index f1532af..2ac8a4e 100644 --- a/src/modules/rag/Application/Menus/DTOs/MenuMetaDto.cs +++ b/src/modules/platform/Application/Menus/DTOs/MenuMetaDto.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Menus.DTOs; +namespace BuildFlow.Platform.Application.Menus.DTOs; /// /// 菜单Meta展示 DTO。 diff --git a/src/modules/rag/Application/Menus/DTOs/MenuRouteDto.cs b/src/modules/platform/Application/Menus/DTOs/MenuRouteDto.cs similarity index 88% rename from src/modules/rag/Application/Menus/DTOs/MenuRouteDto.cs rename to src/modules/platform/Application/Menus/DTOs/MenuRouteDto.cs index 5d485ac..ca94643 100644 --- a/src/modules/rag/Application/Menus/DTOs/MenuRouteDto.cs +++ b/src/modules/platform/Application/Menus/DTOs/MenuRouteDto.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Menus.DTOs; +namespace BuildFlow.Platform.Application.Menus.DTOs; /// /// 菜单Route展示 DTO。 diff --git a/src/modules/platform/Application/Menus/DTOs/MenuTreeNodeDto.cs b/src/modules/platform/Application/Menus/DTOs/MenuTreeNodeDto.cs new file mode 100644 index 0000000..844c19e --- /dev/null +++ b/src/modules/platform/Application/Menus/DTOs/MenuTreeNodeDto.cs @@ -0,0 +1,11 @@ +namespace BuildFlow.Platform.Application.Menus.DTOs; + +/// +/// 菜单管理树节点。扁平 MenuDto 包装为带 Children 的树,供管理端 Tree 展示。 +/// 不过滤角色,返回全部菜单。 +/// +public record MenuTreeNodeDto +{ + public required MenuDto Menu { get; init; } + public List Children { get; init; } = []; +} diff --git a/src/modules/platform/Application/Menus/DTOs/UpdateMenuRequest.cs b/src/modules/platform/Application/Menus/DTOs/UpdateMenuRequest.cs new file mode 100644 index 0000000..0824550 --- /dev/null +++ b/src/modules/platform/Application/Menus/DTOs/UpdateMenuRequest.cs @@ -0,0 +1,22 @@ +namespace BuildFlow.Platform.Application.Menus.DTOs; + +/// +/// 更新菜单请求体。Name 为路由 name,更新后需前端刷新路由。 +/// +public record UpdateMenuRequest( + 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); diff --git a/src/modules/platform/Application/Menus/Handlers/CreateMenuCommandHandler.cs b/src/modules/platform/Application/Menus/Handlers/CreateMenuCommandHandler.cs new file mode 100644 index 0000000..bc03ef7 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/CreateMenuCommandHandler.cs @@ -0,0 +1,52 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Commands; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// 创建菜单命令处理器。校验路由 name 唯一与父菜单存在性。 +/// +public class CreateMenuCommandHandler(PlatformDbContext db) : IRequestHandler +{ + public async Task Handle(CreateMenuCommand request, CancellationToken ct) + { + // 路由 name 全局唯一,避免前端路由冲突。 + if (await db.Menus.AnyAsync(m => m.Name == request.Name, ct)) + throw new BusinessException("菜单路由名称已存在"); + + // 若指定父菜单,必须真实存在(软删除的也视为不存在)。 + if (request.ParentId is not null + && !await db.Menus.AnyAsync(m => m.Id == request.ParentId.Value, ct)) + { + throw new NotFoundException("父菜单不存在"); + } + + var menu = new Menu + { + ParentId = request.ParentId, + Name = request.Name, + Path = request.Path, + Component = request.Component, + Redirect = request.Redirect, + Title = request.Title, + Icon = request.Icon, + ActiveIcon = request.ActiveIcon, + Order = request.Order, + HideInMenu = request.HideInMenu, + KeepAlive = request.KeepAlive, + AffixTab = request.AffixTab, + Link = request.Link, + NoBasicLayout = request.NoBasicLayout, + MenuVisibleWithForbidden = request.MenuVisibleWithForbidden, + Authority = request.Authority, + }; + await db.Menus.AddAsync(menu, ct); + await db.SaveChangesAsync(ct); + + return MenuHandlerMapping.ToDto(menu); + } +} diff --git a/src/modules/platform/Application/Menus/Handlers/DeleteMenuCommandHandler.cs b/src/modules/platform/Application/Menus/Handlers/DeleteMenuCommandHandler.cs new file mode 100644 index 0000000..5224513 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/DeleteMenuCommandHandler.cs @@ -0,0 +1,26 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Commands; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// 删除菜单命令处理器。存在子菜单时拒绝,要求先清理子菜单。 +/// 不使用 EF 的级联删除,避免误删整棵子树。 +/// +public class DeleteMenuCommandHandler(PlatformDbContext db) : IRequestHandler +{ + public async Task Handle(DeleteMenuCommand request, CancellationToken ct) + { + var menu = await db.Menus.FirstOrDefaultAsync(m => m.Id == request.Id, ct) + ?? throw new NotFoundException("菜单不存在"); + + if (await db.Menus.AnyAsync(m => m.ParentId == request.Id, ct)) + throw new BusinessException("存在子菜单,请先删除子菜单"); + + db.Menus.Remove(menu); // ISoftDelete -> AuditInterceptor 软删除 + await db.SaveChangesAsync(ct); + return Unit.Value; + } +} diff --git a/src/modules/rag/Application/Menus/Handlers/GetAllMenusQueryHandler.cs b/src/modules/platform/Application/Menus/Handlers/GetAllMenusQueryHandler.cs similarity index 89% rename from src/modules/rag/Application/Menus/Handlers/GetAllMenusQueryHandler.cs rename to src/modules/platform/Application/Menus/Handlers/GetAllMenusQueryHandler.cs index e78b532..2ab7944 100644 --- a/src/modules/rag/Application/Menus/Handlers/GetAllMenusQueryHandler.cs +++ b/src/modules/platform/Application/Menus/Handlers/GetAllMenusQueryHandler.cs @@ -1,14 +1,14 @@ using MediatR; -using BuildFlow.Application.Menus.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Menus.Queries; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Queries; -namespace BuildFlow.Application.Menus.Handlers; +namespace BuildFlow.Platform.Application.Menus.Handlers; /// /// GetAll菜单s查询处理器。执行业务校验后持久化。 /// -public class GetAllMenusQueryHandler(RagDbContext db) : IRequestHandler> +public class GetAllMenusQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetAllMenusQuery request, CancellationToken ct) { diff --git a/src/modules/platform/Application/Menus/Handlers/GetMenuByIdQueryHandler.cs b/src/modules/platform/Application/Menus/Handlers/GetMenuByIdQueryHandler.cs new file mode 100644 index 0000000..64bf380 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/GetMenuByIdQueryHandler.cs @@ -0,0 +1,20 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Queries; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// 按主键查询单个菜单处理器。 +/// +public class GetMenuByIdQueryHandler(PlatformDbContext db) : IRequestHandler +{ + public async Task Handle(GetMenuByIdQuery request, CancellationToken ct) + { + var menu = await db.Menus.FirstOrDefaultAsync(m => m.Id == request.Id, ct) + ?? throw new NotFoundException("菜单不存在"); + return MenuHandlerMapping.ToDto(menu); + } +} diff --git a/src/modules/platform/Application/Menus/Handlers/GetMenuTreeQueryHandler.cs b/src/modules/platform/Application/Menus/Handlers/GetMenuTreeQueryHandler.cs new file mode 100644 index 0000000..d0b4035 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/GetMenuTreeQueryHandler.cs @@ -0,0 +1,30 @@ +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Queries; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// 菜单管理树查询处理器。返回全部菜单,按 Order 排序递归构建树。 +/// +public class GetMenuTreeQueryHandler(PlatformDbContext db) : IRequestHandler> +{ + public async Task> Handle(GetMenuTreeQuery request, CancellationToken ct) + { + var menus = await db.Menus.OrderBy(m => m.Order).ToListAsync(ct); + return BuildTree(menus, null); + } + + private static List BuildTree(List menus, Guid? parentId) + { + return menus + .Where(m => m.ParentId == parentId) + .Select(m => new MenuTreeNodeDto + { + Menu = MenuHandlerMapping.ToDto(m), + Children = BuildTree(menus, m.Id), + }) + .ToList(); + } +} diff --git a/src/modules/platform/Application/Menus/Handlers/MenuHandlerMapping.cs b/src/modules/platform/Application/Menus/Handlers/MenuHandlerMapping.cs new file mode 100644 index 0000000..d6a00c8 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/MenuHandlerMapping.cs @@ -0,0 +1,28 @@ +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// Menu 实体到 MenuDto 的手动映射,供各 Menu handler 复用。 +/// +internal static class MenuHandlerMapping +{ + public static MenuDto ToDto(Domain.Entities.Menu m) => new( + m.Id, + m.ParentId, + m.Name, + m.Path, + m.Component, + m.Redirect, + m.Title, + m.Icon, + m.ActiveIcon, + m.Order, + m.HideInMenu, + m.KeepAlive, + m.AffixTab, + m.Link, + m.NoBasicLayout, + m.MenuVisibleWithForbidden, + m.Authority); +} diff --git a/src/modules/platform/Application/Menus/Handlers/UpdateMenuCommandHandler.cs b/src/modules/platform/Application/Menus/Handlers/UpdateMenuCommandHandler.cs new file mode 100644 index 0000000..e360f71 --- /dev/null +++ b/src/modules/platform/Application/Menus/Handlers/UpdateMenuCommandHandler.cs @@ -0,0 +1,56 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Menus.Commands; + +namespace BuildFlow.Platform.Application.Menus.Handlers; + +/// +/// 更新菜单命令处理器。若改 name 需校验唯一;改 ParentId 需防自引用。 +/// +public class UpdateMenuCommandHandler(PlatformDbContext db) : IRequestHandler +{ + public async Task Handle(UpdateMenuCommand request, CancellationToken ct) + { + var menu = await db.Menus.FirstOrDefaultAsync(m => m.Id == request.Id, ct) + ?? throw new NotFoundException("菜单不存在"); + + // 更新 name 时校验全局唯一(排除自身)。 + if (request.Name is not null && request.Name != menu.Name + && await db.Menus.AnyAsync(m => m.Name == request.Name && m.Id != request.Id, ct)) + { + throw new BusinessException("菜单路由名称已存在"); + } + + // 防止把父菜单指向自身,造成自引用环。 + if (request.ParentId is not null && request.ParentId == request.Id) + throw new BusinessException("不能将菜单的父级指向自身"); + + if (request.ParentId.HasValue + && !await db.Menus.AnyAsync(m => m.Id == request.ParentId.Value, ct)) + { + throw new NotFoundException("父菜单不存在"); + } + + if (request.ParentId is not null) menu.ParentId = request.ParentId; + if (request.Name is not null) menu.Name = request.Name; + if (request.Path is not null) menu.Path = request.Path; + if (request.Component is not null) menu.Component = request.Component; + if (request.Redirect is not null) menu.Redirect = request.Redirect; + if (request.Title is not null) menu.Title = request.Title; + if (request.Icon is not null) menu.Icon = request.Icon; + if (request.ActiveIcon is not null) menu.ActiveIcon = request.ActiveIcon; + if (request.Order is not null) menu.Order = request.Order.Value; + if (request.HideInMenu is not null) menu.HideInMenu = request.HideInMenu.Value; + if (request.KeepAlive is not null) menu.KeepAlive = request.KeepAlive; + if (request.AffixTab is not null) menu.AffixTab = request.AffixTab; + if (request.Link is not null) menu.Link = request.Link; + if (request.NoBasicLayout is not null) menu.NoBasicLayout = request.NoBasicLayout.Value; + if (request.MenuVisibleWithForbidden is not null) menu.MenuVisibleWithForbidden = request.MenuVisibleWithForbidden.Value; + if (request.Authority is not null) menu.Authority = request.Authority; + + await db.SaveChangesAsync(ct); + return MenuHandlerMapping.ToDto(menu); + } +} diff --git a/src/modules/rag/Application/Menus/Queries/GetAllMenusQuery.cs b/src/modules/platform/Application/Menus/Queries/GetAllMenusQuery.cs similarity index 64% rename from src/modules/rag/Application/Menus/Queries/GetAllMenusQuery.cs rename to src/modules/platform/Application/Menus/Queries/GetAllMenusQuery.cs index ac01afe..bac3906 100644 --- a/src/modules/rag/Application/Menus/Queries/GetAllMenusQuery.cs +++ b/src/modules/platform/Application/Menus/Queries/GetAllMenusQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Menus.DTOs; +using BuildFlow.Platform.Application.Menus.DTOs; -namespace BuildFlow.Application.Menus.Queries; +namespace BuildFlow.Platform.Application.Menus.Queries; /// diff --git a/src/modules/platform/Application/Menus/Queries/GetMenuByIdQuery.cs b/src/modules/platform/Application/Menus/Queries/GetMenuByIdQuery.cs new file mode 100644 index 0000000..0b7d22f --- /dev/null +++ b/src/modules/platform/Application/Menus/Queries/GetMenuByIdQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Platform.Application.Menus.Queries; + +/// +/// 按主键查询单个菜单。 +/// +public record GetMenuByIdQuery(Guid Id) : IRequest; diff --git a/src/modules/platform/Application/Menus/Queries/GetMenuTreeQuery.cs b/src/modules/platform/Application/Menus/Queries/GetMenuTreeQuery.cs new file mode 100644 index 0000000..fdebe26 --- /dev/null +++ b/src/modules/platform/Application/Menus/Queries/GetMenuTreeQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using BuildFlow.Platform.Application.Menus.DTOs; + +namespace BuildFlow.Platform.Application.Menus.Queries; + +/// +/// 查询菜单管理树。返回全部菜单,不做角色过滤。 +/// +public record GetMenuTreeQuery : IRequest>; diff --git a/src/modules/platform/Application/Menus/Validators/CreateMenuCommandValidator.cs b/src/modules/platform/Application/Menus/Validators/CreateMenuCommandValidator.cs new file mode 100644 index 0000000..2ace4b9 --- /dev/null +++ b/src/modules/platform/Application/Menus/Validators/CreateMenuCommandValidator.cs @@ -0,0 +1,32 @@ +using FluentValidation; +using BuildFlow.Platform.Application.Menus.Commands; + +namespace BuildFlow.Platform.Application.Menus.Validators; + +/// +/// 创建菜单命令校验。 +/// +public class CreateMenuCommandValidator : AbstractValidator +{ + public CreateMenuCommandValidator() + { + 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.Icon is not null, () => + { + RuleFor(x => x.Icon!).MaximumLength(100).WithMessage("图标长度不能超过 100 个字符"); + }); + When(x => x.Authority is not null, () => + { + RuleFor(x => x.Authority!).MaximumLength(200).WithMessage("Authority 长度不能超过 200 个字符"); + }); + } +} diff --git a/src/modules/rag/Application/Menus/Validators/GetAllMenusQueryValidator.cs b/src/modules/platform/Application/Menus/Validators/GetAllMenusQueryValidator.cs similarity index 75% rename from src/modules/rag/Application/Menus/Validators/GetAllMenusQueryValidator.cs rename to src/modules/platform/Application/Menus/Validators/GetAllMenusQueryValidator.cs index a30014e..1152a3d 100644 --- a/src/modules/rag/Application/Menus/Validators/GetAllMenusQueryValidator.cs +++ b/src/modules/platform/Application/Menus/Validators/GetAllMenusQueryValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Menus.Queries; +using BuildFlow.Platform.Application.Menus.Queries; -namespace BuildFlow.Application.Menus.Validators; +namespace BuildFlow.Platform.Application.Menus.Validators; /// /// GetAll菜单s查询的请求校验器(FluentValidation)。 diff --git a/src/modules/platform/Application/Menus/Validators/UpdateMenuCommandValidator.cs b/src/modules/platform/Application/Menus/Validators/UpdateMenuCommandValidator.cs new file mode 100644 index 0000000..0f705e1 --- /dev/null +++ b/src/modules/platform/Application/Menus/Validators/UpdateMenuCommandValidator.cs @@ -0,0 +1,27 @@ +using FluentValidation; +using BuildFlow.Platform.Application.Menus.Commands; + +namespace BuildFlow.Platform.Application.Menus.Validators; + +/// +/// 更新菜单命令校验。 +/// +public class UpdateMenuCommandValidator : AbstractValidator +{ + public UpdateMenuCommandValidator() + { + 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 个字符"); + }); + When(x => x.Title is not null, () => + { + RuleFor(x => x.Title!).MaximumLength(100).WithMessage("菜单标题长度不能超过 100 个字符"); + }); + } +} diff --git a/src/modules/rag/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs b/src/modules/platform/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs similarity index 75% rename from src/modules/rag/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs rename to src/modules/platform/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs index 814a213..9bc9759 100644 --- a/src/modules/rag/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs +++ b/src/modules/platform/Application/Notifications/Commands/MarkAllNotificationsReadCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Notifications.Commands; +namespace BuildFlow.Platform.Application.Notifications.Commands; /// /// MarkAll通知sRead命令。封装写操作入参与意图。 diff --git a/src/modules/rag/Application/Notifications/Commands/MarkNotificationReadCommand.cs b/src/modules/platform/Application/Notifications/Commands/MarkNotificationReadCommand.cs similarity index 76% rename from src/modules/rag/Application/Notifications/Commands/MarkNotificationReadCommand.cs rename to src/modules/platform/Application/Notifications/Commands/MarkNotificationReadCommand.cs index 624a753..4a7312b 100644 --- a/src/modules/rag/Application/Notifications/Commands/MarkNotificationReadCommand.cs +++ b/src/modules/platform/Application/Notifications/Commands/MarkNotificationReadCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Notifications.Commands; +namespace BuildFlow.Platform.Application.Notifications.Commands; /// diff --git a/src/modules/rag/Application/Notifications/Commands/PublishNotificationCommand.cs b/src/modules/platform/Application/Notifications/Commands/PublishNotificationCommand.cs similarity index 85% rename from src/modules/rag/Application/Notifications/Commands/PublishNotificationCommand.cs rename to src/modules/platform/Application/Notifications/Commands/PublishNotificationCommand.cs index e2effdb..1c97b2a 100644 --- a/src/modules/rag/Application/Notifications/Commands/PublishNotificationCommand.cs +++ b/src/modules/platform/Application/Notifications/Commands/PublishNotificationCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Notifications.Commands; +namespace BuildFlow.Platform.Application.Notifications.Commands; /// diff --git a/src/modules/rag/Application/Notifications/DTOs/NotificationDtos.cs b/src/modules/platform/Application/Notifications/DTOs/NotificationDtos.cs similarity index 85% rename from src/modules/rag/Application/Notifications/DTOs/NotificationDtos.cs rename to src/modules/platform/Application/Notifications/DTOs/NotificationDtos.cs index 9a86fc1..9707b23 100644 --- a/src/modules/rag/Application/Notifications/DTOs/NotificationDtos.cs +++ b/src/modules/platform/Application/Notifications/DTOs/NotificationDtos.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Notifications.DTOs; +namespace BuildFlow.Platform.Application.Notifications.DTOs; /// diff --git a/src/modules/rag/Application/Notifications/DTOs/PagedResult.cs b/src/modules/platform/Application/Notifications/DTOs/PagedResult.cs similarity index 69% rename from src/modules/rag/Application/Notifications/DTOs/PagedResult.cs rename to src/modules/platform/Application/Notifications/DTOs/PagedResult.cs index 4ea7688..cebbbcf 100644 --- a/src/modules/rag/Application/Notifications/DTOs/PagedResult.cs +++ b/src/modules/platform/Application/Notifications/DTOs/PagedResult.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Notifications.DTOs; +namespace BuildFlow.Platform.Application.Notifications.DTOs; /// /// PagedResult。 diff --git a/src/modules/rag/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs b/src/modules/platform/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs similarity index 79% rename from src/modules/rag/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs rename to src/modules/platform/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs index 82927fa..8cc2d6c 100644 --- a/src/modules/rag/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs +++ b/src/modules/platform/Application/Notifications/Handlers/GetNotificationsQueryHandler.cs @@ -1,14 +1,14 @@ using MediatR; -using BuildFlow.Application.Notifications.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Notifications.Queries; +using BuildFlow.Platform.Application.Notifications.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Notifications.Queries; -namespace BuildFlow.Application.Notifications.Handlers; +namespace BuildFlow.Platform.Application.Notifications.Handlers; /// /// Get通知s查询处理器。执行业务校验后持久化。 /// -public class GetNotificationsQueryHandler(RagDbContext db) +public class GetNotificationsQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetNotificationsQuery request, CancellationToken ct) diff --git a/src/modules/rag/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs b/src/modules/platform/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs similarity index 61% rename from src/modules/rag/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs rename to src/modules/platform/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs index e3ae676..6614106 100644 --- a/src/modules/rag/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs +++ b/src/modules/platform/Application/Notifications/Handlers/GetUnreadNotificationCountQueryHandler.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Notifications.Queries; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Notifications.Queries; -namespace BuildFlow.Application.Notifications.Handlers; +namespace BuildFlow.Platform.Application.Notifications.Handlers; /// /// GetUnread通知Count查询处理器。执行业务校验后持久化。 /// -public class GetUnreadNotificationCountQueryHandler(RagDbContext db) +public class GetUnreadNotificationCountQueryHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(GetUnreadNotificationCountQuery request, CancellationToken ct) diff --git a/src/modules/rag/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs b/src/modules/platform/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs similarity index 72% rename from src/modules/rag/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs rename to src/modules/platform/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs index 7acdc55..76fd504 100644 --- a/src/modules/rag/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs +++ b/src/modules/platform/Application/Notifications/Handlers/MarkAllNotificationsReadCommandHandler.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Notifications.Commands; -namespace BuildFlow.Application.Notifications.Handlers; +namespace BuildFlow.Platform.Application.Notifications.Handlers; /// /// MarkAll通知sReadCommand处理器。执行业务校验后持久化。 /// -public class MarkAllNotificationsReadCommandHandler(RagDbContext db) +public class MarkAllNotificationsReadCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(MarkAllNotificationsReadCommand request, CancellationToken ct) diff --git a/src/modules/rag/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs b/src/modules/platform/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs similarity index 76% rename from src/modules/rag/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs rename to src/modules/platform/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs index 5a12b73..a49abe8 100644 --- a/src/modules/rag/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs +++ b/src/modules/platform/Application/Notifications/Handlers/MarkNotificationReadCommandHandler.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Notifications.Commands; -namespace BuildFlow.Application.Notifications.Handlers; +namespace BuildFlow.Platform.Application.Notifications.Handlers; /// /// Mark通知ReadCommand处理器。执行业务校验后持久化。 /// -public class MarkNotificationReadCommandHandler(RagDbContext db) +public class MarkNotificationReadCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(MarkNotificationReadCommand request, CancellationToken ct) diff --git a/src/modules/rag/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs b/src/modules/platform/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs similarity index 89% rename from src/modules/rag/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs rename to src/modules/platform/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs index 3b1d124..630c36a 100644 --- a/src/modules/rag/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs +++ b/src/modules/platform/Application/Notifications/Handlers/PublishNotificationCommandHandler.cs @@ -1,15 +1,15 @@ using MediatR; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Notifications.Commands; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Notifications.Commands; -namespace BuildFlow.Application.Notifications.Handlers; +namespace BuildFlow.Platform.Application.Notifications.Handlers; /// /// Publish通知Command处理器。执行业务校验后持久化。 /// public class PublishNotificationCommandHandler( - RagDbContext db, + PlatformDbContext db, INotificationDispatcher dispatcher) : IRequestHandler { public async Task Handle(PublishNotificationCommand request, CancellationToken ct) diff --git a/src/modules/rag/Application/Notifications/INotificationDispatcher.cs b/src/modules/platform/Application/Notifications/INotificationDispatcher.cs similarity index 84% rename from src/modules/rag/Application/Notifications/INotificationDispatcher.cs rename to src/modules/platform/Application/Notifications/INotificationDispatcher.cs index ca45a15..ba60ebc 100644 --- a/src/modules/rag/Application/Notifications/INotificationDispatcher.cs +++ b/src/modules/platform/Application/Notifications/INotificationDispatcher.cs @@ -1,6 +1,6 @@ -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Application.Notifications; +namespace BuildFlow.Platform.Application.Notifications; /// /// 通知分发器抽象。职责:把已落库的通知实时推送给接收人(如 SignalR)。 diff --git a/src/modules/rag/Application/Notifications/Queries/GetNotificationsQuery.cs b/src/modules/platform/Application/Notifications/Queries/GetNotificationsQuery.cs similarity index 69% rename from src/modules/rag/Application/Notifications/Queries/GetNotificationsQuery.cs rename to src/modules/platform/Application/Notifications/Queries/GetNotificationsQuery.cs index 9e965e3..9dd27e3 100644 --- a/src/modules/rag/Application/Notifications/Queries/GetNotificationsQuery.cs +++ b/src/modules/platform/Application/Notifications/Queries/GetNotificationsQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Notifications.DTOs; +using BuildFlow.Platform.Application.Notifications.DTOs; -namespace BuildFlow.Application.Notifications.Queries; +namespace BuildFlow.Platform.Application.Notifications.Queries; /// diff --git a/src/modules/rag/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs b/src/modules/platform/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs similarity index 76% rename from src/modules/rag/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs rename to src/modules/platform/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs index fe8e7c3..90cc937 100644 --- a/src/modules/rag/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs +++ b/src/modules/platform/Application/Notifications/Queries/GetUnreadNotificationCountQuery.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Notifications.Queries; +namespace BuildFlow.Platform.Application.Notifications.Queries; /// /// GetUnread通知Count查询。封装读操作入参与过滤条件。 diff --git a/src/modules/platform/Application/Permissions/Commands/CreatePermissionCommand.cs b/src/modules/platform/Application/Permissions/Commands/CreatePermissionCommand.cs new file mode 100644 index 0000000..2612650 --- /dev/null +++ b/src/modules/platform/Application/Permissions/Commands/CreatePermissionCommand.cs @@ -0,0 +1,10 @@ +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; + +namespace BuildFlow.Platform.Application.Permissions.Commands; + +/// +/// 创建权限命令。校验编码唯一性后写入权限表。 +/// +public record CreatePermissionCommand(string Name, string Code, string? Description, string? Group) + : IRequest; diff --git a/src/modules/platform/Application/Permissions/Commands/DeletePermissionCommand.cs b/src/modules/platform/Application/Permissions/Commands/DeletePermissionCommand.cs new file mode 100644 index 0000000..dcc3b31 --- /dev/null +++ b/src/modules/platform/Application/Permissions/Commands/DeletePermissionCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace BuildFlow.Platform.Application.Permissions.Commands; + +/// +/// 删除权限命令。同时清理角色-权限关联,软删除权限本身。 +/// +public record DeletePermissionCommand(Guid Id) : IRequest; diff --git a/src/modules/platform/Application/Permissions/Commands/UpdatePermissionCommand.cs b/src/modules/platform/Application/Permissions/Commands/UpdatePermissionCommand.cs new file mode 100644 index 0000000..abbe825 --- /dev/null +++ b/src/modules/platform/Application/Permissions/Commands/UpdatePermissionCommand.cs @@ -0,0 +1,10 @@ +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; + +namespace BuildFlow.Platform.Application.Permissions.Commands; + +/// +/// 更新权限命令。支持部分字段更新,编码不可改。 +/// +public record UpdatePermissionCommand(Guid Id, string? Name, string? Description, string? Group) + : IRequest; diff --git a/src/modules/platform/Application/Permissions/DTOs/CreatePermissionRequest.cs b/src/modules/platform/Application/Permissions/DTOs/CreatePermissionRequest.cs new file mode 100644 index 0000000..30ebeff --- /dev/null +++ b/src/modules/platform/Application/Permissions/DTOs/CreatePermissionRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Platform.Application.Permissions.DTOs; + +/// +/// 创建权限请求体。 +/// +public record CreatePermissionRequest(string Name, string Code, string? Description, string? Group); diff --git a/src/modules/rag/Application/Permissions/DTOs/PermissionDtos.cs b/src/modules/platform/Application/Permissions/DTOs/PermissionDtos.cs similarity index 72% rename from src/modules/rag/Application/Permissions/DTOs/PermissionDtos.cs rename to src/modules/platform/Application/Permissions/DTOs/PermissionDtos.cs index 4dceb53..3d23984 100644 --- a/src/modules/rag/Application/Permissions/DTOs/PermissionDtos.cs +++ b/src/modules/platform/Application/Permissions/DTOs/PermissionDtos.cs @@ -1,4 +1,4 @@ -namespace BuildFlow.Application.Permissions.DTOs; +namespace BuildFlow.Platform.Application.Permissions.DTOs; /// /// 权限展示 DTO。 diff --git a/src/modules/platform/Application/Permissions/DTOs/UpdatePermissionRequest.cs b/src/modules/platform/Application/Permissions/DTOs/UpdatePermissionRequest.cs new file mode 100644 index 0000000..1773dcd --- /dev/null +++ b/src/modules/platform/Application/Permissions/DTOs/UpdatePermissionRequest.cs @@ -0,0 +1,6 @@ +namespace BuildFlow.Platform.Application.Permissions.DTOs; + +/// +/// 更新权限请求体。编码不可更新。 +/// +public record UpdatePermissionRequest(string? Name, string? Description, string? Group); diff --git a/src/modules/platform/Application/Permissions/Handlers/CreatePermissionCommandHandler.cs b/src/modules/platform/Application/Permissions/Handlers/CreatePermissionCommandHandler.cs new file mode 100644 index 0000000..2f5f50b --- /dev/null +++ b/src/modules/platform/Application/Permissions/Handlers/CreatePermissionCommandHandler.cs @@ -0,0 +1,33 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Permissions.Commands; + +namespace BuildFlow.Platform.Application.Permissions.Handlers; + +/// +/// 创建权限命令处理器。编码唯一性校验后写入。 +/// +public class CreatePermissionCommandHandler(PlatformDbContext db) + : IRequestHandler +{ + public async Task Handle(CreatePermissionCommand request, CancellationToken ct) + { + if (await db.Permissions.AnyAsync(p => p.Code == request.Code, ct)) + throw new BusinessException("权限编码已存在"); + + var permission = new Permission + { + Name = request.Name, + Code = request.Code, + Description = request.Description, + Group = request.Group, + }; + await db.Permissions.AddAsync(permission, ct); + await db.SaveChangesAsync(ct); + + return new PermissionDto(permission.Id, permission.Name, permission.Code, permission.Description, permission.Group); + } +} diff --git a/src/modules/platform/Application/Permissions/Handlers/DeletePermissionCommandHandler.cs b/src/modules/platform/Application/Permissions/Handlers/DeletePermissionCommandHandler.cs new file mode 100644 index 0000000..f853d2f --- /dev/null +++ b/src/modules/platform/Application/Permissions/Handlers/DeletePermissionCommandHandler.cs @@ -0,0 +1,27 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Permissions.Commands; + +namespace BuildFlow.Platform.Application.Permissions.Handlers; + +/// +/// 删除权限命令处理器。先清理角色-权限关联,再软删除权限,避免外键孤儿。 +/// +public class DeletePermissionCommandHandler(PlatformDbContext db) + : IRequestHandler +{ + public async Task Handle(DeletePermissionCommand request, CancellationToken ct) + { + var permission = await db.Permissions.FirstOrDefaultAsync(p => p.Id == request.Id, ct) + ?? throw new NotFoundException("权限不存在"); + + // 先解除已分配该权限的所有角色关联,避免删除后角色仍持失效权限。 + var links = db.RolePermissions.Where(rp => rp.PermissionId == request.Id); + db.RolePermissions.RemoveRange(links); + + db.Permissions.Remove(permission); // ISoftDelete -> AuditInterceptor 软删除 + await db.SaveChangesAsync(ct); + return Unit.Value; + } +} diff --git a/src/modules/platform/Application/Permissions/Handlers/GetPermissionByIdQueryHandler.cs b/src/modules/platform/Application/Permissions/Handlers/GetPermissionByIdQueryHandler.cs new file mode 100644 index 0000000..cbdffd5 --- /dev/null +++ b/src/modules/platform/Application/Permissions/Handlers/GetPermissionByIdQueryHandler.cs @@ -0,0 +1,23 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Permissions.Queries; + +namespace BuildFlow.Platform.Application.Permissions.Handlers; + +/// +/// 按主键查询单个权限处理器。 +/// +public class GetPermissionByIdQueryHandler(PlatformDbContext db) + : IRequestHandler +{ + public async Task Handle(GetPermissionByIdQuery request, CancellationToken ct) + { + return await db.Permissions + .Where(p => p.Id == request.Id) + .Select(p => new PermissionDto(p.Id, p.Name, p.Code, p.Description, p.Group)) + .FirstOrDefaultAsync(ct) + ?? throw new NotFoundException("权限不存在"); + } +} diff --git a/src/modules/rag/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs b/src/modules/platform/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs similarity index 52% rename from src/modules/rag/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs rename to src/modules/platform/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs index eb86217..361b567 100644 --- a/src/modules/rag/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs +++ b/src/modules/platform/Application/Permissions/Handlers/GetPermissionListQueryHandler.cs @@ -1,14 +1,14 @@ using MediatR; -using BuildFlow.Application.Permissions.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Permissions.Queries; +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Permissions.Queries; -namespace BuildFlow.Application.Permissions.Handlers; +namespace BuildFlow.Platform.Application.Permissions.Handlers; /// /// Get权限List查询处理器。执行业务校验后持久化。 /// -public class GetPermissionListQueryHandler(RagDbContext db) : IRequestHandler> +public class GetPermissionListQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetPermissionListQuery request, CancellationToken ct) { diff --git a/src/modules/platform/Application/Permissions/Handlers/UpdatePermissionCommandHandler.cs b/src/modules/platform/Application/Permissions/Handlers/UpdatePermissionCommandHandler.cs new file mode 100644 index 0000000..05fc56e --- /dev/null +++ b/src/modules/platform/Application/Permissions/Handlers/UpdatePermissionCommandHandler.cs @@ -0,0 +1,27 @@ +using BuildFlow.Shared.Domain.Exceptions; +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Permissions.Commands; + +namespace BuildFlow.Platform.Application.Permissions.Handlers; + +/// +/// 更新权限命令处理器。编码不可改,其余字段按需更新。 +/// +public class UpdatePermissionCommandHandler(PlatformDbContext db) + : IRequestHandler +{ + public async Task Handle(UpdatePermissionCommand request, CancellationToken ct) + { + var permission = await db.Permissions.FirstOrDefaultAsync(p => p.Id == request.Id, ct) + ?? throw new NotFoundException("权限不存在"); + + if (request.Name is not null) permission.Name = request.Name; + if (request.Description is not null) permission.Description = request.Description; + if (request.Group is not null) permission.Group = request.Group; + await db.SaveChangesAsync(ct); + + return new PermissionDto(permission.Id, permission.Name, permission.Code, permission.Description, permission.Group); + } +} diff --git a/src/modules/platform/Application/Permissions/Queries/GetPermissionByIdQuery.cs b/src/modules/platform/Application/Permissions/Queries/GetPermissionByIdQuery.cs new file mode 100644 index 0000000..a776b39 --- /dev/null +++ b/src/modules/platform/Application/Permissions/Queries/GetPermissionByIdQuery.cs @@ -0,0 +1,9 @@ +using MediatR; +using BuildFlow.Platform.Application.Permissions.DTOs; + +namespace BuildFlow.Platform.Application.Permissions.Queries; + +/// +/// 按主键查询单个权限。 +/// +public record GetPermissionByIdQuery(Guid Id) : IRequest; diff --git a/src/modules/rag/Application/Permissions/Queries/GetPermissionListQuery.cs b/src/modules/platform/Application/Permissions/Queries/GetPermissionListQuery.cs similarity index 61% rename from src/modules/rag/Application/Permissions/Queries/GetPermissionListQuery.cs rename to src/modules/platform/Application/Permissions/Queries/GetPermissionListQuery.cs index cdddc6e..fe0418e 100644 --- a/src/modules/rag/Application/Permissions/Queries/GetPermissionListQuery.cs +++ b/src/modules/platform/Application/Permissions/Queries/GetPermissionListQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Permissions.DTOs; +using BuildFlow.Platform.Application.Permissions.DTOs; -namespace BuildFlow.Application.Permissions.Queries; +namespace BuildFlow.Platform.Application.Permissions.Queries; /// diff --git a/src/modules/platform/Application/Permissions/Validators/CreatePermissionCommandValidator.cs b/src/modules/platform/Application/Permissions/Validators/CreatePermissionCommandValidator.cs new file mode 100644 index 0000000..4be6a0c --- /dev/null +++ b/src/modules/platform/Application/Permissions/Validators/CreatePermissionCommandValidator.cs @@ -0,0 +1,26 @@ +using FluentValidation; +using BuildFlow.Platform.Application.Permissions.Commands; + +namespace BuildFlow.Platform.Application.Permissions.Validators; + +/// +/// 创建权限命令校验。 +/// +public class CreatePermissionCommandValidator : AbstractValidator +{ + public CreatePermissionCommandValidator() + { + 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 个字符"); + }); + } +} diff --git a/src/modules/platform/Application/Permissions/Validators/UpdatePermissionCommandValidator.cs b/src/modules/platform/Application/Permissions/Validators/UpdatePermissionCommandValidator.cs new file mode 100644 index 0000000..e9fa99a --- /dev/null +++ b/src/modules/platform/Application/Permissions/Validators/UpdatePermissionCommandValidator.cs @@ -0,0 +1,27 @@ +using FluentValidation; +using BuildFlow.Platform.Application.Permissions.Commands; + +namespace BuildFlow.Platform.Application.Permissions.Validators; + +/// +/// 更新权限命令校验。 +/// +public class UpdatePermissionCommandValidator : AbstractValidator +{ + public UpdatePermissionCommandValidator() + { + 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 个字符"); + }); + } +} diff --git a/src/modules/platform/Application/Platform.Application.csproj b/src/modules/platform/Application/Platform.Application.csproj new file mode 100644 index 0000000..0d16ba1 --- /dev/null +++ b/src/modules/platform/Application/Platform.Application.csproj @@ -0,0 +1,18 @@ + + + net10.0 + enable + enable + true + + + + + + + + + + + + diff --git a/src/modules/platform/Application/PlatformApplicationModule.cs b/src/modules/platform/Application/PlatformApplicationModule.cs new file mode 100644 index 0000000..71ea589 --- /dev/null +++ b/src/modules/platform/Application/PlatformApplicationModule.cs @@ -0,0 +1,21 @@ +using BuildFlow.Platform.Application.Common; +using BuildFlow.Platform.Infrastructure; +using FluentValidation; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace BuildFlow.Platform.Application; + +/// 平台基础能力应用层模块。注册 MediatR + FluentValidation。 +[DependsOn(typeof(PlatformInfrastructureModule))] +public class PlatformApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var services = context.Services; + services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(PlatformApplicationModule).Assembly)); + services.AddValidatorsFromAssembly(typeof(PlatformApplicationModule).Assembly); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); + } +} diff --git a/src/modules/rag/Application/Roles/Commands/AssignPermissionsCommand.cs b/src/modules/platform/Application/Roles/Commands/AssignPermissionsCommand.cs similarity index 66% rename from src/modules/rag/Application/Roles/Commands/AssignPermissionsCommand.cs rename to src/modules/platform/Application/Roles/Commands/AssignPermissionsCommand.cs index d84a6e4..0726405 100644 --- a/src/modules/rag/Application/Roles/Commands/AssignPermissionsCommand.cs +++ b/src/modules/platform/Application/Roles/Commands/AssignPermissionsCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; -namespace BuildFlow.Application.Roles.Commands; +namespace BuildFlow.Platform.Application.Roles.Commands; /// diff --git a/src/modules/rag/Application/Roles/Commands/CreateRoleCommand.cs b/src/modules/platform/Application/Roles/Commands/CreateRoleCommand.cs similarity index 64% rename from src/modules/rag/Application/Roles/Commands/CreateRoleCommand.cs rename to src/modules/platform/Application/Roles/Commands/CreateRoleCommand.cs index 53a4421..c7df055 100644 --- a/src/modules/rag/Application/Roles/Commands/CreateRoleCommand.cs +++ b/src/modules/platform/Application/Roles/Commands/CreateRoleCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; -namespace BuildFlow.Application.Roles.Commands; +namespace BuildFlow.Platform.Application.Roles.Commands; /// diff --git a/src/modules/rag/Application/Roles/Commands/DeleteRoleCommand.cs b/src/modules/platform/Application/Roles/Commands/DeleteRoleCommand.cs similarity index 74% rename from src/modules/rag/Application/Roles/Commands/DeleteRoleCommand.cs rename to src/modules/platform/Application/Roles/Commands/DeleteRoleCommand.cs index f620872..9a030f7 100644 --- a/src/modules/rag/Application/Roles/Commands/DeleteRoleCommand.cs +++ b/src/modules/platform/Application/Roles/Commands/DeleteRoleCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Roles.Commands; +namespace BuildFlow.Platform.Application.Roles.Commands; /// diff --git a/src/modules/rag/Application/Roles/Commands/UpdateRoleCommand.cs b/src/modules/platform/Application/Roles/Commands/UpdateRoleCommand.cs similarity index 65% rename from src/modules/rag/Application/Roles/Commands/UpdateRoleCommand.cs rename to src/modules/platform/Application/Roles/Commands/UpdateRoleCommand.cs index 28673c9..448fafc 100644 --- a/src/modules/rag/Application/Roles/Commands/UpdateRoleCommand.cs +++ b/src/modules/platform/Application/Roles/Commands/UpdateRoleCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; -namespace BuildFlow.Application.Roles.Commands; +namespace BuildFlow.Platform.Application.Roles.Commands; /// diff --git a/src/modules/rag/Application/Roles/DTOs/AssignPermissionsRequest.cs b/src/modules/platform/Application/Roles/DTOs/AssignPermissionsRequest.cs similarity index 70% rename from src/modules/rag/Application/Roles/DTOs/AssignPermissionsRequest.cs rename to src/modules/platform/Application/Roles/DTOs/AssignPermissionsRequest.cs index 58c3e91..88941dd 100644 --- a/src/modules/rag/Application/Roles/DTOs/AssignPermissionsRequest.cs +++ b/src/modules/platform/Application/Roles/DTOs/AssignPermissionsRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Roles.DTOs; +namespace BuildFlow.Platform.Application.Roles.DTOs; /// /// Assign权限s请求体。 diff --git a/src/modules/rag/Application/Roles/DTOs/CreateRoleRequest.cs b/src/modules/platform/Application/Roles/DTOs/CreateRoleRequest.cs similarity index 70% rename from src/modules/rag/Application/Roles/DTOs/CreateRoleRequest.cs rename to src/modules/platform/Application/Roles/DTOs/CreateRoleRequest.cs index 31fe615..6617248 100644 --- a/src/modules/rag/Application/Roles/DTOs/CreateRoleRequest.cs +++ b/src/modules/platform/Application/Roles/DTOs/CreateRoleRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Roles.DTOs; +namespace BuildFlow.Platform.Application.Roles.DTOs; /// /// Create角色请求体。 diff --git a/src/modules/rag/Application/Roles/DTOs/RoleDtos.cs b/src/modules/platform/Application/Roles/DTOs/RoleDtos.cs similarity index 76% rename from src/modules/rag/Application/Roles/DTOs/RoleDtos.cs rename to src/modules/platform/Application/Roles/DTOs/RoleDtos.cs index 0e95edf..df41b51 100644 --- a/src/modules/rag/Application/Roles/DTOs/RoleDtos.cs +++ b/src/modules/platform/Application/Roles/DTOs/RoleDtos.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Roles.DTOs; +namespace BuildFlow.Platform.Application.Roles.DTOs; /// diff --git a/src/modules/rag/Application/Roles/DTOs/UpdateRoleRequest.cs b/src/modules/platform/Application/Roles/DTOs/UpdateRoleRequest.cs similarity index 70% rename from src/modules/rag/Application/Roles/DTOs/UpdateRoleRequest.cs rename to src/modules/platform/Application/Roles/DTOs/UpdateRoleRequest.cs index f5f78c5..dcfed19 100644 --- a/src/modules/rag/Application/Roles/DTOs/UpdateRoleRequest.cs +++ b/src/modules/platform/Application/Roles/DTOs/UpdateRoleRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Roles.DTOs; +namespace BuildFlow.Platform.Application.Roles.DTOs; /// /// Update角色请求体。 diff --git a/src/modules/rag/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs b/src/modules/platform/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs similarity index 77% rename from src/modules/rag/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs rename to src/modules/platform/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs index b34fd76..c3ceb83 100644 --- a/src/modules/rag/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/AssignPermissionsCommandHandler.cs @@ -1,16 +1,16 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Assign权限sCommand处理器。执行业务校验后持久化。 /// -public class AssignPermissionsCommandHandler(RagDbContext db) : IRequestHandler +public class AssignPermissionsCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(AssignPermissionsCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Handlers/CreateRoleCommandHandler.cs b/src/modules/platform/Application/Roles/Handlers/CreateRoleCommandHandler.cs similarity index 64% rename from src/modules/rag/Application/Roles/Handlers/CreateRoleCommandHandler.cs rename to src/modules/platform/Application/Roles/Handlers/CreateRoleCommandHandler.cs index b76e1d6..345f193 100644 --- a/src/modules/rag/Application/Roles/Handlers/CreateRoleCommandHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/CreateRoleCommandHandler.cs @@ -1,16 +1,16 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Create角色Command处理器。执行业务校验后持久化。 /// -public class CreateRoleCommandHandler(RagDbContext db) : IRequestHandler +public class CreateRoleCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(CreateRoleCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Handlers/DeleteRoleCommandHandler.cs b/src/modules/platform/Application/Roles/Handlers/DeleteRoleCommandHandler.cs similarity index 65% rename from src/modules/rag/Application/Roles/Handlers/DeleteRoleCommandHandler.cs rename to src/modules/platform/Application/Roles/Handlers/DeleteRoleCommandHandler.cs index 96a2f5f..b161fb6 100644 --- a/src/modules/rag/Application/Roles/Handlers/DeleteRoleCommandHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/DeleteRoleCommandHandler.cs @@ -1,14 +1,14 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Delete角色Command处理器。执行业务校验后持久化。 /// -public class DeleteRoleCommandHandler(RagDbContext db) : IRequestHandler +public class DeleteRoleCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(DeleteRoleCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs b/src/modules/platform/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs similarity index 67% rename from src/modules/rag/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs rename to src/modules/platform/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs index 97d0ece..a834b48 100644 --- a/src/modules/rag/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/GetRoleByIdQueryHandler.cs @@ -1,15 +1,15 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Queries; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Queries; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Get角色ById查询处理器。执行业务校验后持久化。 /// -public class GetRoleByIdQueryHandler(RagDbContext db) : IRequestHandler +public class GetRoleByIdQueryHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(GetRoleByIdQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Handlers/GetRoleListQueryHandler.cs b/src/modules/platform/Application/Roles/Handlers/GetRoleListQueryHandler.cs similarity index 62% rename from src/modules/rag/Application/Roles/Handlers/GetRoleListQueryHandler.cs rename to src/modules/platform/Application/Roles/Handlers/GetRoleListQueryHandler.cs index 9101cc3..bc33e09 100644 --- a/src/modules/rag/Application/Roles/Handlers/GetRoleListQueryHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/GetRoleListQueryHandler.cs @@ -1,14 +1,14 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Queries; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Queries; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Get角色List查询处理器。执行业务校验后持久化。 /// -public class GetRoleListQueryHandler(RagDbContext db) : IRequestHandler> +public class GetRoleListQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetRoleListQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Handlers/UpdateRoleCommandHandler.cs b/src/modules/platform/Application/Roles/Handlers/UpdateRoleCommandHandler.cs similarity index 72% rename from src/modules/rag/Application/Roles/Handlers/UpdateRoleCommandHandler.cs rename to src/modules/platform/Application/Roles/Handlers/UpdateRoleCommandHandler.cs index 4720e8a..9c13726 100644 --- a/src/modules/rag/Application/Roles/Handlers/UpdateRoleCommandHandler.cs +++ b/src/modules/platform/Application/Roles/Handlers/UpdateRoleCommandHandler.cs @@ -1,15 +1,15 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Roles.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Handlers; +namespace BuildFlow.Platform.Application.Roles.Handlers; /// /// Update角色Command处理器。执行业务校验后持久化。 /// -public class UpdateRoleCommandHandler(RagDbContext db) : IRequestHandler +public class UpdateRoleCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(UpdateRoleCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Roles/Queries/GetRoleByIdQuery.cs b/src/modules/platform/Application/Roles/Queries/GetRoleByIdQuery.cs similarity index 62% rename from src/modules/rag/Application/Roles/Queries/GetRoleByIdQuery.cs rename to src/modules/platform/Application/Roles/Queries/GetRoleByIdQuery.cs index 1e34ac0..ff439b6 100644 --- a/src/modules/rag/Application/Roles/Queries/GetRoleByIdQuery.cs +++ b/src/modules/platform/Application/Roles/Queries/GetRoleByIdQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; -namespace BuildFlow.Application.Roles.Queries; +namespace BuildFlow.Platform.Application.Roles.Queries; /// diff --git a/src/modules/rag/Application/Roles/Queries/GetRoleListQuery.cs b/src/modules/platform/Application/Roles/Queries/GetRoleListQuery.cs similarity index 61% rename from src/modules/rag/Application/Roles/Queries/GetRoleListQuery.cs rename to src/modules/platform/Application/Roles/Queries/GetRoleListQuery.cs index 0031bba..e43a2d8 100644 --- a/src/modules/rag/Application/Roles/Queries/GetRoleListQuery.cs +++ b/src/modules/platform/Application/Roles/Queries/GetRoleListQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Roles.DTOs; +using BuildFlow.Platform.Application.Roles.DTOs; -namespace BuildFlow.Application.Roles.Queries; +namespace BuildFlow.Platform.Application.Roles.Queries; /// diff --git a/src/modules/rag/Application/Roles/Validators/AssignPermissionsCommandValidator.cs b/src/modules/platform/Application/Roles/Validators/AssignPermissionsCommandValidator.cs similarity index 80% rename from src/modules/rag/Application/Roles/Validators/AssignPermissionsCommandValidator.cs rename to src/modules/platform/Application/Roles/Validators/AssignPermissionsCommandValidator.cs index 24e15b3..20b0d26 100644 --- a/src/modules/rag/Application/Roles/Validators/AssignPermissionsCommandValidator.cs +++ b/src/modules/platform/Application/Roles/Validators/AssignPermissionsCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Validators; +namespace BuildFlow.Platform.Application.Roles.Validators; /// /// Assign权限sCommand的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Roles/Validators/CreateRoleCommandValidator.cs b/src/modules/platform/Application/Roles/Validators/CreateRoleCommandValidator.cs similarity index 78% rename from src/modules/rag/Application/Roles/Validators/CreateRoleCommandValidator.cs rename to src/modules/platform/Application/Roles/Validators/CreateRoleCommandValidator.cs index 2afb062..902a72e 100644 --- a/src/modules/rag/Application/Roles/Validators/CreateRoleCommandValidator.cs +++ b/src/modules/platform/Application/Roles/Validators/CreateRoleCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Validators; +namespace BuildFlow.Platform.Application.Roles.Validators; /// /// Create角色Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Roles/Validators/DeleteRoleCommandValidator.cs b/src/modules/platform/Application/Roles/Validators/DeleteRoleCommandValidator.cs similarity index 74% rename from src/modules/rag/Application/Roles/Validators/DeleteRoleCommandValidator.cs rename to src/modules/platform/Application/Roles/Validators/DeleteRoleCommandValidator.cs index 1dae5e6..cea9e1e 100644 --- a/src/modules/rag/Application/Roles/Validators/DeleteRoleCommandValidator.cs +++ b/src/modules/platform/Application/Roles/Validators/DeleteRoleCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Validators; +namespace BuildFlow.Platform.Application.Roles.Validators; /// /// Delete角色Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Roles/Validators/GetRoleByIdQueryValidator.cs b/src/modules/platform/Application/Roles/Validators/GetRoleByIdQueryValidator.cs similarity index 74% rename from src/modules/rag/Application/Roles/Validators/GetRoleByIdQueryValidator.cs rename to src/modules/platform/Application/Roles/Validators/GetRoleByIdQueryValidator.cs index 62a4ff1..754f912 100644 --- a/src/modules/rag/Application/Roles/Validators/GetRoleByIdQueryValidator.cs +++ b/src/modules/platform/Application/Roles/Validators/GetRoleByIdQueryValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Roles.Queries; +using BuildFlow.Platform.Application.Roles.Queries; -namespace BuildFlow.Application.Roles.Validators; +namespace BuildFlow.Platform.Application.Roles.Validators; /// /// Get角色ById查询的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Roles/Validators/UpdateRoleCommandValidator.cs b/src/modules/platform/Application/Roles/Validators/UpdateRoleCommandValidator.cs similarity index 81% rename from src/modules/rag/Application/Roles/Validators/UpdateRoleCommandValidator.cs rename to src/modules/platform/Application/Roles/Validators/UpdateRoleCommandValidator.cs index 50e642c..260fc05 100644 --- a/src/modules/rag/Application/Roles/Validators/UpdateRoleCommandValidator.cs +++ b/src/modules/platform/Application/Roles/Validators/UpdateRoleCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Roles.Commands; +using BuildFlow.Platform.Application.Roles.Commands; -namespace BuildFlow.Application.Roles.Validators; +namespace BuildFlow.Platform.Application.Roles.Validators; /// /// Update角色Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Users/Commands/AssignRolesCommand.cs b/src/modules/platform/Application/Users/Commands/AssignRolesCommand.cs similarity index 64% rename from src/modules/rag/Application/Users/Commands/AssignRolesCommand.cs rename to src/modules/platform/Application/Users/Commands/AssignRolesCommand.cs index 5bde18a..28e9d41 100644 --- a/src/modules/rag/Application/Users/Commands/AssignRolesCommand.cs +++ b/src/modules/platform/Application/Users/Commands/AssignRolesCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; -namespace BuildFlow.Application.Users.Commands; +namespace BuildFlow.Platform.Application.Users.Commands; /// diff --git a/src/modules/rag/Application/Users/Commands/CreateUserCommand.cs b/src/modules/platform/Application/Users/Commands/CreateUserCommand.cs similarity index 66% rename from src/modules/rag/Application/Users/Commands/CreateUserCommand.cs rename to src/modules/platform/Application/Users/Commands/CreateUserCommand.cs index 9884893..84c889e 100644 --- a/src/modules/rag/Application/Users/Commands/CreateUserCommand.cs +++ b/src/modules/platform/Application/Users/Commands/CreateUserCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; -namespace BuildFlow.Application.Users.Commands; +namespace BuildFlow.Platform.Application.Users.Commands; /// diff --git a/src/modules/rag/Application/Users/Commands/DeleteUserCommand.cs b/src/modules/platform/Application/Users/Commands/DeleteUserCommand.cs similarity index 74% rename from src/modules/rag/Application/Users/Commands/DeleteUserCommand.cs rename to src/modules/platform/Application/Users/Commands/DeleteUserCommand.cs index f7af44d..0d23ab7 100644 --- a/src/modules/rag/Application/Users/Commands/DeleteUserCommand.cs +++ b/src/modules/platform/Application/Users/Commands/DeleteUserCommand.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Users.Commands; +namespace BuildFlow.Platform.Application.Users.Commands; /// diff --git a/src/modules/rag/Application/Users/Commands/UpdateUserCommand.cs b/src/modules/platform/Application/Users/Commands/UpdateUserCommand.cs similarity index 67% rename from src/modules/rag/Application/Users/Commands/UpdateUserCommand.cs rename to src/modules/platform/Application/Users/Commands/UpdateUserCommand.cs index 7adc361..7fd68f8 100644 --- a/src/modules/rag/Application/Users/Commands/UpdateUserCommand.cs +++ b/src/modules/platform/Application/Users/Commands/UpdateUserCommand.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; -namespace BuildFlow.Application.Users.Commands; +namespace BuildFlow.Platform.Application.Users.Commands; /// diff --git a/src/modules/rag/Application/Users/DTOs/AssignRolesRequest.cs b/src/modules/platform/Application/Users/DTOs/AssignRolesRequest.cs similarity index 68% rename from src/modules/rag/Application/Users/DTOs/AssignRolesRequest.cs rename to src/modules/platform/Application/Users/DTOs/AssignRolesRequest.cs index eb702ea..4b43bc6 100644 --- a/src/modules/rag/Application/Users/DTOs/AssignRolesRequest.cs +++ b/src/modules/platform/Application/Users/DTOs/AssignRolesRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Users.DTOs; +namespace BuildFlow.Platform.Application.Users.DTOs; /// /// Assign角色s请求体。 diff --git a/src/modules/rag/Application/Users/DTOs/CreateUserRequest.cs b/src/modules/platform/Application/Users/DTOs/CreateUserRequest.cs similarity index 72% rename from src/modules/rag/Application/Users/DTOs/CreateUserRequest.cs rename to src/modules/platform/Application/Users/DTOs/CreateUserRequest.cs index 8efbb1c..899b07e 100644 --- a/src/modules/rag/Application/Users/DTOs/CreateUserRequest.cs +++ b/src/modules/platform/Application/Users/DTOs/CreateUserRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Users.DTOs; +namespace BuildFlow.Platform.Application.Users.DTOs; /// /// Create用户请求体。 diff --git a/src/modules/rag/Application/Users/DTOs/UpdateUserRequest.cs b/src/modules/platform/Application/Users/DTOs/UpdateUserRequest.cs similarity index 72% rename from src/modules/rag/Application/Users/DTOs/UpdateUserRequest.cs rename to src/modules/platform/Application/Users/DTOs/UpdateUserRequest.cs index 882f062..ec5e9fb 100644 --- a/src/modules/rag/Application/Users/DTOs/UpdateUserRequest.cs +++ b/src/modules/platform/Application/Users/DTOs/UpdateUserRequest.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Users.DTOs; +namespace BuildFlow.Platform.Application.Users.DTOs; /// /// Update用户请求体。 diff --git a/src/modules/rag/Application/Users/DTOs/UserDtos.cs b/src/modules/platform/Application/Users/DTOs/UserDtos.cs similarity index 76% rename from src/modules/rag/Application/Users/DTOs/UserDtos.cs rename to src/modules/platform/Application/Users/DTOs/UserDtos.cs index 111e059..bf1fd72 100644 --- a/src/modules/rag/Application/Users/DTOs/UserDtos.cs +++ b/src/modules/platform/Application/Users/DTOs/UserDtos.cs @@ -1,6 +1,6 @@ -namespace BuildFlow.Application.Users.DTOs; +namespace BuildFlow.Platform.Application.Users.DTOs; /// diff --git a/src/modules/rag/Application/Users/Handlers/AssignRolesCommandHandler.cs b/src/modules/platform/Application/Users/Handlers/AssignRolesCommandHandler.cs similarity index 74% rename from src/modules/rag/Application/Users/Handlers/AssignRolesCommandHandler.cs rename to src/modules/platform/Application/Users/Handlers/AssignRolesCommandHandler.cs index c292dc3..ad80538 100644 --- a/src/modules/rag/Application/Users/Handlers/AssignRolesCommandHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/AssignRolesCommandHandler.cs @@ -1,16 +1,16 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Assign角色sCommand处理器。执行业务校验后持久化。 /// -public class AssignRolesCommandHandler(RagDbContext db) : IRequestHandler +public class AssignRolesCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(AssignRolesCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Handlers/CreateUserCommandHandler.cs b/src/modules/platform/Application/Users/Handlers/CreateUserCommandHandler.cs similarity index 75% rename from src/modules/rag/Application/Users/Handlers/CreateUserCommandHandler.cs rename to src/modules/platform/Application/Users/Handlers/CreateUserCommandHandler.cs index b6134b3..7f437d8 100644 --- a/src/modules/rag/Application/Users/Handlers/CreateUserCommandHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/CreateUserCommandHandler.cs @@ -1,16 +1,16 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Domain.Entities; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Create用户Command处理器。执行业务校验后持久化。 /// -public class CreateUserCommandHandler(RagDbContext db) : IRequestHandler +public class CreateUserCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(CreateUserCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Handlers/DeleteUserCommandHandler.cs b/src/modules/platform/Application/Users/Handlers/DeleteUserCommandHandler.cs similarity index 69% rename from src/modules/rag/Application/Users/Handlers/DeleteUserCommandHandler.cs rename to src/modules/platform/Application/Users/Handlers/DeleteUserCommandHandler.cs index d6fbdef..1de41ab 100644 --- a/src/modules/rag/Application/Users/Handlers/DeleteUserCommandHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/DeleteUserCommandHandler.cs @@ -1,14 +1,14 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Delete用户Command处理器。执行业务校验后持久化。 /// -public class DeleteUserCommandHandler(RagDbContext db) : IRequestHandler +public class DeleteUserCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(DeleteUserCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Handlers/GetUserByIdQueryHandler.cs b/src/modules/platform/Application/Users/Handlers/GetUserByIdQueryHandler.cs similarity index 67% rename from src/modules/rag/Application/Users/Handlers/GetUserByIdQueryHandler.cs rename to src/modules/platform/Application/Users/Handlers/GetUserByIdQueryHandler.cs index a70f0da..044ed09 100644 --- a/src/modules/rag/Application/Users/Handlers/GetUserByIdQueryHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/GetUserByIdQueryHandler.cs @@ -1,15 +1,15 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Queries; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Get用户ById查询处理器。执行业务校验后持久化。 /// -public class GetUserByIdQueryHandler(RagDbContext db) : IRequestHandler +public class GetUserByIdQueryHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(GetUserByIdQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Handlers/GetUserListQueryHandler.cs b/src/modules/platform/Application/Users/Handlers/GetUserListQueryHandler.cs similarity index 72% rename from src/modules/rag/Application/Users/Handlers/GetUserListQueryHandler.cs rename to src/modules/platform/Application/Users/Handlers/GetUserListQueryHandler.cs index e3005ab..b661599 100644 --- a/src/modules/rag/Application/Users/Handlers/GetUserListQueryHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/GetUserListQueryHandler.cs @@ -1,14 +1,14 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Queries; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Get用户List查询处理器。执行业务校验后持久化。 /// -public class GetUserListQueryHandler(RagDbContext db) : IRequestHandler> +public class GetUserListQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetUserListQuery request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Handlers/GetUserNamesQueryHandler.cs b/src/modules/platform/Application/Users/Handlers/GetUserNamesQueryHandler.cs similarity index 71% rename from src/modules/rag/Application/Users/Handlers/GetUserNamesQueryHandler.cs rename to src/modules/platform/Application/Users/Handlers/GetUserNamesQueryHandler.cs index 7aab433..b76fe41 100644 --- a/src/modules/rag/Application/Users/Handlers/GetUserNamesQueryHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/GetUserNamesQueryHandler.cs @@ -1,13 +1,13 @@ using MediatR; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Queries; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Get用户Names查询处理器。执行业务校验后持久化。 /// -public class GetUserNamesQueryHandler(RagDbContext db) +public class GetUserNamesQueryHandler(PlatformDbContext db) : IRequestHandler> { public async Task> Handle(GetUserNamesQuery request, CancellationToken ct) diff --git a/src/modules/rag/Application/Users/Handlers/UpdateUserCommandHandler.cs b/src/modules/platform/Application/Users/Handlers/UpdateUserCommandHandler.cs similarity index 74% rename from src/modules/rag/Application/Users/Handlers/UpdateUserCommandHandler.cs rename to src/modules/platform/Application/Users/Handlers/UpdateUserCommandHandler.cs index c0e89aa..ee44bbd 100644 --- a/src/modules/rag/Application/Users/Handlers/UpdateUserCommandHandler.cs +++ b/src/modules/platform/Application/Users/Handlers/UpdateUserCommandHandler.cs @@ -1,15 +1,15 @@ using BuildFlow.Shared.Domain.Exceptions; using MediatR; -using BuildFlow.Application.Users.DTOs; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.DTOs; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Handlers; +namespace BuildFlow.Platform.Application.Users.Handlers; /// /// Update用户Command处理器。执行业务校验后持久化。 /// -public class UpdateUserCommandHandler(RagDbContext db) : IRequestHandler +public class UpdateUserCommandHandler(PlatformDbContext db) : IRequestHandler { public async Task Handle(UpdateUserCommand request, CancellationToken ct) { diff --git a/src/modules/rag/Application/Users/Queries/GetUserByIdQuery.cs b/src/modules/platform/Application/Users/Queries/GetUserByIdQuery.cs similarity index 62% rename from src/modules/rag/Application/Users/Queries/GetUserByIdQuery.cs rename to src/modules/platform/Application/Users/Queries/GetUserByIdQuery.cs index 519b6df..caa2fc8 100644 --- a/src/modules/rag/Application/Users/Queries/GetUserByIdQuery.cs +++ b/src/modules/platform/Application/Users/Queries/GetUserByIdQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; -namespace BuildFlow.Application.Users.Queries; +namespace BuildFlow.Platform.Application.Users.Queries; /// diff --git a/src/modules/rag/Application/Users/Queries/GetUserListQuery.cs b/src/modules/platform/Application/Users/Queries/GetUserListQuery.cs similarity index 64% rename from src/modules/rag/Application/Users/Queries/GetUserListQuery.cs rename to src/modules/platform/Application/Users/Queries/GetUserListQuery.cs index be25e13..9ce1314 100644 --- a/src/modules/rag/Application/Users/Queries/GetUserListQuery.cs +++ b/src/modules/platform/Application/Users/Queries/GetUserListQuery.cs @@ -1,7 +1,7 @@ using MediatR; -using BuildFlow.Application.Users.DTOs; +using BuildFlow.Platform.Application.Users.DTOs; -namespace BuildFlow.Application.Users.Queries; +namespace BuildFlow.Platform.Application.Users.Queries; /// diff --git a/src/modules/rag/Application/Users/Queries/GetUserNamesQuery.cs b/src/modules/platform/Application/Users/Queries/GetUserNamesQuery.cs similarity index 80% rename from src/modules/rag/Application/Users/Queries/GetUserNamesQuery.cs rename to src/modules/platform/Application/Users/Queries/GetUserNamesQuery.cs index cb87a37..ff88df7 100644 --- a/src/modules/rag/Application/Users/Queries/GetUserNamesQuery.cs +++ b/src/modules/platform/Application/Users/Queries/GetUserNamesQuery.cs @@ -1,6 +1,6 @@ using MediatR; -namespace BuildFlow.Application.Users.Queries; +namespace BuildFlow.Platform.Application.Users.Queries; /// diff --git a/src/modules/rag/Application/Users/Validators/AssignRolesCommandValidator.cs b/src/modules/platform/Application/Users/Validators/AssignRolesCommandValidator.cs similarity index 79% rename from src/modules/rag/Application/Users/Validators/AssignRolesCommandValidator.cs rename to src/modules/platform/Application/Users/Validators/AssignRolesCommandValidator.cs index 8c89ade..a3ce90b 100644 --- a/src/modules/rag/Application/Users/Validators/AssignRolesCommandValidator.cs +++ b/src/modules/platform/Application/Users/Validators/AssignRolesCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Validators; +namespace BuildFlow.Platform.Application.Users.Validators; /// /// Assign角色sCommand的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Users/Validators/CreateUserCommandValidator.cs b/src/modules/platform/Application/Users/Validators/CreateUserCommandValidator.cs similarity index 86% rename from src/modules/rag/Application/Users/Validators/CreateUserCommandValidator.cs rename to src/modules/platform/Application/Users/Validators/CreateUserCommandValidator.cs index 0130068..07b0e94 100644 --- a/src/modules/rag/Application/Users/Validators/CreateUserCommandValidator.cs +++ b/src/modules/platform/Application/Users/Validators/CreateUserCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Validators; +namespace BuildFlow.Platform.Application.Users.Validators; /// /// Create用户Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Users/Validators/DeleteUserCommandValidator.cs b/src/modules/platform/Application/Users/Validators/DeleteUserCommandValidator.cs similarity index 74% rename from src/modules/rag/Application/Users/Validators/DeleteUserCommandValidator.cs rename to src/modules/platform/Application/Users/Validators/DeleteUserCommandValidator.cs index a1f5864..b8dfd18 100644 --- a/src/modules/rag/Application/Users/Validators/DeleteUserCommandValidator.cs +++ b/src/modules/platform/Application/Users/Validators/DeleteUserCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Validators; +namespace BuildFlow.Platform.Application.Users.Validators; /// /// Delete用户Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Users/Validators/GetUserByIdQueryValidator.cs b/src/modules/platform/Application/Users/Validators/GetUserByIdQueryValidator.cs similarity index 74% rename from src/modules/rag/Application/Users/Validators/GetUserByIdQueryValidator.cs rename to src/modules/platform/Application/Users/Validators/GetUserByIdQueryValidator.cs index eea65ac..f0cdc01 100644 --- a/src/modules/rag/Application/Users/Validators/GetUserByIdQueryValidator.cs +++ b/src/modules/platform/Application/Users/Validators/GetUserByIdQueryValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Users.Queries; +using BuildFlow.Platform.Application.Users.Queries; -namespace BuildFlow.Application.Users.Validators; +namespace BuildFlow.Platform.Application.Users.Validators; /// /// Get用户ById查询的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Application/Users/Validators/UpdateUserCommandValidator.cs b/src/modules/platform/Application/Users/Validators/UpdateUserCommandValidator.cs similarity index 85% rename from src/modules/rag/Application/Users/Validators/UpdateUserCommandValidator.cs rename to src/modules/platform/Application/Users/Validators/UpdateUserCommandValidator.cs index 96d6144..6f8a65b 100644 --- a/src/modules/rag/Application/Users/Validators/UpdateUserCommandValidator.cs +++ b/src/modules/platform/Application/Users/Validators/UpdateUserCommandValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; -using BuildFlow.Application.Users.Commands; +using BuildFlow.Platform.Application.Users.Commands; -namespace BuildFlow.Application.Users.Validators; +namespace BuildFlow.Platform.Application.Users.Validators; /// /// Update用户Command的请求校验器(FluentValidation)。 diff --git a/src/modules/rag/Domain/Entities/Menu.cs b/src/modules/platform/Domain/Entities/Menu.cs similarity index 97% rename from src/modules/rag/Domain/Entities/Menu.cs rename to src/modules/platform/Domain/Entities/Menu.cs index 0bd8c17..511eb59 100644 --- a/src/modules/rag/Domain/Entities/Menu.cs +++ b/src/modules/platform/Domain/Entities/Menu.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 菜单实体,自引用树形结构。配合前端 backend 模式的动态菜单 API 使用。 diff --git a/src/modules/rag/Domain/Entities/Notification.cs b/src/modules/platform/Domain/Entities/Notification.cs similarity index 97% rename from src/modules/rag/Domain/Entities/Notification.cs rename to src/modules/platform/Domain/Entities/Notification.cs index e055156..bb8b9d9 100644 --- a/src/modules/rag/Domain/Entities/Notification.cs +++ b/src/modules/platform/Domain/Entities/Notification.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 消息中心通知(站内信)。采用 Fanout 扇出策略:一条业务消息为每个接收人生成一条独立记录, diff --git a/src/modules/rag/Domain/Entities/Permission.cs b/src/modules/platform/Domain/Entities/Permission.cs similarity index 95% rename from src/modules/rag/Domain/Entities/Permission.cs rename to src/modules/platform/Domain/Entities/Permission.cs index 46ca268..bddc4ac 100644 --- a/src/modules/rag/Domain/Entities/Permission.cs +++ b/src/modules/platform/Domain/Entities/Permission.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 权限实体,实现完整审计。Code 为权限唯一编码(如 "user:create"),用于程序内硬编码鉴权。 diff --git a/src/modules/rag/Domain/Entities/RefreshToken.cs b/src/modules/platform/Domain/Entities/RefreshToken.cs similarity index 93% rename from src/modules/rag/Domain/Entities/RefreshToken.cs rename to src/modules/platform/Domain/Entities/RefreshToken.cs index a7f4bf8..d8355f5 100644 --- a/src/modules/rag/Domain/Entities/RefreshToken.cs +++ b/src/modules/platform/Domain/Entities/RefreshToken.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 刷新令牌实体,仅实现 IAuditable(不做软删除,令牌通过 IsRevoked 或到期自动失效)。 diff --git a/src/modules/rag/Domain/Entities/Role.cs b/src/modules/platform/Domain/Entities/Role.cs similarity index 94% rename from src/modules/rag/Domain/Entities/Role.cs rename to src/modules/platform/Domain/Entities/Role.cs index 477c3b5..ba1f826 100644 --- a/src/modules/rag/Domain/Entities/Role.cs +++ b/src/modules/platform/Domain/Entities/Role.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 角色实体,实现完整审计。删除时走软删除,关联的 UserRole/RolePermission 由拦截器或业务层处理。 diff --git a/src/modules/rag/Domain/Entities/RolePermission.cs b/src/modules/platform/Domain/Entities/RolePermission.cs similarity index 93% rename from src/modules/rag/Domain/Entities/RolePermission.cs rename to src/modules/platform/Domain/Entities/RolePermission.cs index 98d427f..be9a0e7 100644 --- a/src/modules/rag/Domain/Entities/RolePermission.cs +++ b/src/modules/platform/Domain/Entities/RolePermission.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 角色-权限关联表(复合主键),仅实现 IAuditable 追踪权限分配记录,不需要软删除。 diff --git a/src/modules/rag/Domain/Entities/User.cs b/src/modules/platform/Domain/Entities/User.cs similarity index 97% rename from src/modules/rag/Domain/Entities/User.cs rename to src/modules/platform/Domain/Entities/User.cs index 2675d77..7defb82 100644 --- a/src/modules/rag/Domain/Entities/User.cs +++ b/src/modules/platform/Domain/Entities/User.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 用户实体,实现完整审计(创建/修改追踪 + 软删除 + IP记录)。 diff --git a/src/modules/rag/Domain/Entities/UserRole.cs b/src/modules/platform/Domain/Entities/UserRole.cs similarity index 93% rename from src/modules/rag/Domain/Entities/UserRole.cs rename to src/modules/platform/Domain/Entities/UserRole.cs index 10847f4..15bf58f 100644 --- a/src/modules/rag/Domain/Entities/UserRole.cs +++ b/src/modules/platform/Domain/Entities/UserRole.cs @@ -1,6 +1,6 @@ using BuildFlow.Shared.Domain.Common; -namespace BuildFlow.Domain.Entities; +namespace BuildFlow.Platform.Domain.Entities; /// /// 用户-角色关联表(复合主键),仅实现 IAuditable 追踪角色分配记录,不需要软删除。 diff --git a/src/modules/platform/Domain/Platform.Domain.csproj b/src/modules/platform/Domain/Platform.Domain.csproj new file mode 100644 index 0000000..e7a0e22 --- /dev/null +++ b/src/modules/platform/Domain/Platform.Domain.csproj @@ -0,0 +1,15 @@ + + + net10.0 + enable + enable + true + + + + + + + + + diff --git a/src/modules/platform/Domain/PlatformDomainModule.cs b/src/modules/platform/Domain/PlatformDomainModule.cs new file mode 100644 index 0000000..c45da3b --- /dev/null +++ b/src/modules/platform/Domain/PlatformDomainModule.cs @@ -0,0 +1,6 @@ +using Volo.Abp.Modularity; + +namespace BuildFlow.Platform.Domain; + +/// 平台基础能力领域层模块(用户/角色/权限/菜单/通知)。 +public class PlatformDomainModule : AbpModule; diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/MenuConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/MenuConfiguration.cs similarity index 96% rename from src/modules/rag/Infrastructure/Persistence/Configurations/MenuConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/MenuConfiguration.cs index ee9ebce..e55ee8c 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/MenuConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/MenuConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 菜单表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs similarity index 95% rename from src/modules/rag/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs index 2836fea..57e63d9 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/NotificationConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 通知表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs similarity index 93% rename from src/modules/rag/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs index 004d4dd..df191b9 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/PermissionConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 权限表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs similarity index 92% rename from src/modules/rag/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs index 0ec6a09..b171a6a 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/RefreshTokenConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 刷新令牌表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/RoleConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/RoleConfiguration.cs similarity index 93% rename from src/modules/rag/Infrastructure/Persistence/Configurations/RoleConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/RoleConfiguration.cs index 8f6dc40..8a75293 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/RoleConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/RoleConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 角色表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs similarity index 89% rename from src/modules/rag/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs index 1ec5b0d..fac87cb 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/RolePermissionConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 角色权限表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/UserConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/UserConfiguration.cs similarity index 93% rename from src/modules/rag/Infrastructure/Persistence/Configurations/UserConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/UserConfiguration.cs index 9eed159..d388e33 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/UserConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/UserConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 用户表 EF Core 映射配置。 diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs b/src/modules/platform/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs similarity index 88% rename from src/modules/rag/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs rename to src/modules/platform/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs index 0b4b64b..3ba1e62 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs +++ b/src/modules/platform/Infrastructure/Persistence/Configurations/UserRoleConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using BuildFlow.Domain.Entities; +using BuildFlow.Platform.Domain.Entities; -namespace BuildFlow.Infrastructure.Persistence.Configurations; +namespace BuildFlow.Platform.Infrastructure.Persistence.Configurations; /// /// 用户角色表 EF Core 映射配置。 diff --git a/src/modules/platform/Infrastructure/Persistence/PlatformDbContext.cs b/src/modules/platform/Infrastructure/Persistence/PlatformDbContext.cs new file mode 100644 index 0000000..6ee1b18 --- /dev/null +++ b/src/modules/platform/Infrastructure/Persistence/PlatformDbContext.cs @@ -0,0 +1,41 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using BuildFlow.Shared.Domain.Common; +using BuildFlow.Platform.Domain.Entities; + +namespace BuildFlow.Platform.Infrastructure.Persistence; + +/// +/// 平台基础能力 DbContext。管理用户/角色/权限/菜单/通知等平台表。 +/// 连接 platform 数据库(与 RAG 业务库分离)。 +/// +public class PlatformDbContext : DbContext +{ + public PlatformDbContext(DbContextOptions options) : base(options) { } + + public DbSet Users => Set(); + public DbSet Roles => Set(); + public DbSet Permissions => Set(); + public DbSet UserRoles => Set(); + public DbSet RolePermissions => Set(); + public DbSet RefreshTokens => Set(); + public DbSet Menus => Set(); + public DbSet Notifications => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly); + + // 为所有实现 ISoftDelete 的实体自动注册全局查询过滤器 + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType)) + { + var parameter = Expression.Parameter(entityType.ClrType, "e"); + var property = Expression.Property(parameter, nameof(ISoftDelete.IsDeleted)); + var filter = Expression.Lambda(Expression.Not(property), parameter); + modelBuilder.Entity(entityType.ClrType).HasQueryFilter(filter); + } + } + } +} diff --git a/src/modules/platform/Infrastructure/Persistence/SeedData.cs b/src/modules/platform/Infrastructure/Persistence/SeedData.cs new file mode 100644 index 0000000..551f725 --- /dev/null +++ b/src/modules/platform/Infrastructure/Persistence/SeedData.cs @@ -0,0 +1,382 @@ +using BuildFlow.Platform.Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace BuildFlow.Platform.Infrastructure.Persistence; + +/// +/// SeedData类型。 +/// +public static class SeedData +{ + public static async Task SeedAsync(PlatformDbContext db) + { + // 权限 + await SeedPermissionsAsync(db); + + // 角色 + if (!await db.Roles.AnyAsync()) + { + var superAdminRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000001"), Name = "SuperAdmin", Description = "超级管理员" }; + var adminRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000002"), Name = "Admin", Description = "管理员" }; + var userRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000003"), Name = "User", Description = "普通用户" }; + await db.Roles.AddRangeAsync(superAdminRole, adminRole, userRole); + await db.SaveChangesAsync(); + } + + // 角色-权限关联 + if (!await db.RolePermissions.AnyAsync()) + { + var superAdminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000001"); + var adminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000002"); + var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); + + var allPerms = await db.Permissions.ToListAsync(); + await db.RolePermissions.AddRangeAsync(allPerms.Select(p => new RolePermission { RoleId = superAdminRoleId, PermissionId = p.Id })); + await db.RolePermissions.AddRangeAsync(allPerms.Where(p => p.Code is "user:read" or "user:create" or "user:update" or "role:read" or "permission:read").Select(p => new RolePermission { RoleId = adminRoleId, PermissionId = p.Id })); + await db.RolePermissions.AddRangeAsync(allPerms.Where(p => p.Code is "user:read" or "role:read" or "permission:read").Select(p => new RolePermission { RoleId = userRoleId, PermissionId = p.Id })); + await db.SaveChangesAsync(); + } + await EnsureDefaultRolePermissionsAsync(db); + + // 管理员用户 + var adminUserId = Guid.Parse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"); + var existingAdmin = await db.Users.FirstOrDefaultAsync(u => u.Id == adminUserId); + if (existingAdmin == null) + { + existingAdmin = await db.Users.FirstOrDefaultAsync(u => u.Username == "admin"); + if (existingAdmin == null) + { + existingAdmin = new User + { + Id = adminUserId, + Username = "admin", + Email = "admin@rag.local", + PasswordHash = BCrypt.Net.BCrypt.HashPassword("admin123") + }; + await db.Users.AddAsync(existingAdmin); + await db.SaveChangesAsync(); + } + } + + // 确保管理员有 SuperAdmin 角色 + var superAdminId = Guid.Parse("20000000-0000-0000-0000-000000000001"); + var hasRole = await db.UserRoles.AnyAsync(ur => ur.UserId == existingAdmin.Id && ur.RoleId == superAdminId); + if (!hasRole) + { + await db.UserRoles.AddAsync(new UserRole { UserId = existingAdmin.Id, RoleId = superAdminId }); + await db.SaveChangesAsync(); + } + + // IM 演示用户:ID 与 im-system SeedData 保持一致,供通讯录和 Auth gRPC 查询使用 + await SeedImDemoUsersAsync(db); + + // ===== 菜单种子数据 ===== + await SeedMenusAsync(db); + } + + private static async Task SeedPermissionsAsync(PlatformDbContext db) + { + var permissionSeeds = new[] + { + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000001"), "查看用户", "user:read", "UserManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000002"), "创建用户", "user:create", "UserManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000003"), "更新用户", "user:update", "UserManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000004"), "删除用户", "user:delete", "UserManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000005"), "分配角色", "user:assign-role", "UserManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000006"), "查看角色", "role:read", "RoleManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000007"), "创建角色", "role:create", "RoleManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000008"), "更新角色", "role:update", "RoleManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000009"), "删除角色", "role:delete", "RoleManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000010"), "分配权限", "role:assign-permission", "RoleManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000011"), "查看权限", "permission:read", "SystemManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000012"), "创建权限", "permission:create", "SystemManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000013"), "更新权限", "permission:update", "SystemManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000014"), "删除权限", "permission:delete", "SystemManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000015"), "查看菜单", "menu:read", "MenuManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000016"), "创建菜单", "menu:create", "MenuManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000017"), "更新菜单", "menu:update", "MenuManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000020"), "删除菜单", "menu:delete", "MenuManagement"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000018"), "发布通知", "notification:publish", "NotificationCenter"), + new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000019"), "查看通知", "notification:read", "NotificationCenter"), + }; + + var existingCodes = await db.Permissions.Select(p => p.Code).ToListAsync(); + var permissions = permissionSeeds + .Where(p => !existingCodes.Contains(p.Code)) + .Select(p => new Permission { Id = p.Id, Name = p.Name, Code = p.Code, Group = p.Group }) + .ToList(); + + if (permissions.Count == 0) return; + + await db.Permissions.AddRangeAsync(permissions); + await db.SaveChangesAsync(); + } + + private static async Task EnsureDefaultRolePermissionsAsync(PlatformDbContext db) + { + var superAdminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000001"); + var adminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000002"); + var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); + + var allPermissionCodes = await db.Permissions.Select(p => p.Code).ToListAsync(); + await EnsureRolePermissionsAsync(db, superAdminRoleId, allPermissionCodes); + await EnsureRolePermissionsAsync(db, adminRoleId, [ + "user:read", + "user:create", + "user:update", + "role:read", + "permission:read", + "menu:read" + ]); + await EnsureRolePermissionsAsync(db, userRoleId, [ + "user:read", + "role:read", + "permission:read" + ]); + } + + private static async Task EnsureRolePermissionsAsync(PlatformDbContext db, Guid roleId, IReadOnlyCollection permissionCodes) + { + var roleExists = await db.Roles.AnyAsync(r => r.Id == roleId); + if (!roleExists) return; + + var codes = permissionCodes.ToArray(); + var permissionIds = await db.Permissions + .Where(p => codes.Contains(p.Code)) + .Select(p => p.Id) + .ToListAsync(); + + var existingPermissionIds = await db.RolePermissions + .Where(rp => rp.RoleId == roleId && permissionIds.Contains(rp.PermissionId)) + .Select(rp => rp.PermissionId) + .ToListAsync(); + + var missingRolePermissions = permissionIds + .Except(existingPermissionIds) + .Select(permissionId => new RolePermission { RoleId = roleId, PermissionId = permissionId }) + .ToList(); + + if (missingRolePermissions.Count == 0) return; + + await db.RolePermissions.AddRangeAsync(missingRolePermissions); + await db.SaveChangesAsync(); + } + + private static async Task SeedImDemoUsersAsync(PlatformDbContext db) + { + var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); + var users = new[] + { + new SeedUser( + Guid.Parse("30000000-0000-0000-0000-000000000002"), + "zhangsan", + "zhangsan@rag.local"), + new SeedUser( + Guid.Parse("30000000-0000-0000-0000-000000000003"), + "lisi", + "lisi@rag.local"), + new SeedUser( + Guid.Parse("30000000-0000-0000-0000-000000000004"), + "wangwu", + "wangwu@rag.local"), + }; + + foreach (var seedUser in users) + { + var exists = await db.Users.AnyAsync(u => + u.Id == seedUser.Id || + u.Username == seedUser.Username || + u.Email == seedUser.Email); + if (exists) continue; + + await db.Users.AddAsync(new User + { + Id = seedUser.Id, + Username = seedUser.Username, + Email = seedUser.Email, + PasswordHash = BCrypt.Net.BCrypt.HashPassword("user123"), + IsActive = true + }); + } + + await db.SaveChangesAsync(); + + foreach (var seedUser in users) + { + var userExists = await db.Users.AnyAsync(u => u.Id == seedUser.Id); + if (!userExists) continue; + + var hasUserRole = await db.UserRoles.AnyAsync(ur => + ur.UserId == seedUser.Id && ur.RoleId == userRoleId); + if (hasUserRole) continue; + + await db.UserRoles.AddAsync(new UserRole + { + UserId = seedUser.Id, + RoleId = userRoleId + }); + } + + await db.SaveChangesAsync(); + } + + /// 权限种子记录(Id 固定 Guid,保证幂等重跑不重复插入)。 + private sealed record SeedPermission(Guid Id, string Name, string Code, string Group); + + /// 用户种子记录(Id 固定,密码在主流程中 BCrypt 哈希后写入)。 + private sealed record SeedUser(Guid Id, string Username, string Email); + + /// + /// 全量菜单种子入口。幂等:menus 表非空时跳过。供 BuildFlow Host 启动时独立调用, + /// 不依赖用户种子是否执行(backend 模式动态菜单依赖 menus 表必须有数据)。 + /// + public static async Task SeedMenusAsync(PlatformDbContext db) + { + // 幂等:按 Id 补种缺失的菜单。已存在的(含用户改名/调序)不动。 + // 这样升级后新增菜单项能在已有数据库里自动出现,而不是「库非空就整体跳过」。 + var existingIds = await db.Menus.Select(m => m.Id).ToHashSetAsync(); + var toAdd = BuildSeedMenus().Where(m => !existingIds.Contains(m.Id)).ToList(); + if (toAdd.Count == 0) return; + await db.Menus.AddRangeAsync(toAdd); + await db.SaveChangesAsync(); + } + + /// + /// 构建全量菜单种子数据。对应前端 backend 模式动态菜单。 + /// Authority 为空表示所有已认证用户可见;后续可按角色名(逗号分隔)收紧。 + /// Guid 采用固定命名空间,便于幂等补种与排查。 + /// + private static List BuildSeedMenus() + { + var menus = new List(); + + // ── Dashboard (order=-1):数据看板作为首页,不再 redirect 到 analytics ── + var dashboard = Menu("40000000-0000-0000-0000-000000000001", "Dashboard", "/dashboard", + "page.dashboard.title", icon: "lucide:layout-dashboard", order: -1); + menus.Add(dashboard); + menus.Add(Leaf("40000000-0000-0000-0000-000000000002", "Analytics", "/analytics", + "page.dashboard.analytics", "/dashboard/analytics/index", dashboard.Id, order: 1, affixTab: true)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000003", "Workspace", "/workspace", + "page.dashboard.workspace", "/dashboard/workspace/index", dashboard.Id, order: 2)); + // 数据看板(新首页大屏):放 dashboard 容器下作为默认页 + menus.Add(Leaf("40000000-0000-0000-0000-000000000004", "DashboardOverview", "/dashboard", + "数据看板", "/dashboard/index", dashboard.Id, order: 0, affixTab: true)); + + // ── AI 对话 (order=3) ── + var aiChat = Menu("40000000-0000-0000-0000-000000000010", "AiChat", "/ai-chat", + "AI 对话", icon: "lucide:bot", order: 3); + menus.Add(aiChat); + menus.Add(Leaf("40000000-0000-0000-0000-000000000011", "AiChatMain", "/ai-chat", + "AI 对话", "/ai-chat/index", aiChat.Id)); + + // ── 知识库 (order=4) ── + var kb = Menu("40000000-0000-0000-0000-000000000020", "KnowledgeBase", "/knowledge-base", + "知识库", icon: "lucide:database", order: 4); + menus.Add(kb); + menus.Add(Leaf("40000000-0000-0000-0000-000000000021", "KnowledgeBaseList", "/knowledge-base", + "知识库管理", "/knowledge-base/index", kb.Id)); + + // ── BuildFlow (order=5) ── + var buildflow = Menu("40000000-0000-0000-0000-000000000030", "BuildFlow", "/buildflow", + "BuildFlow", icon: "lucide:blocks", order: 5); + menus.Add(buildflow); + menus.Add(Leaf("40000000-0000-0000-0000-000000000031", "BuildFlowConsole", "/buildflow", + "控制台", "/buildflow/index", buildflow.Id, icon: "lucide:settings-2")); + + // ── 即时通讯 (order=5) ── + var chat = Menu("40000000-0000-0000-0000-000000000040", "Chat", "/chat", + "即时通讯", icon: "lucide:message-square", order: 5); + menus.Add(chat); + menus.Add(Leaf("40000000-0000-0000-0000-000000000041", "ChatMain", "/chat", + "聊天", "/im/index", chat.Id)); + + // ── 流程管理 (order=6) ── + var workflow = Menu("40000000-0000-0000-0000-000000000050", "Workflow", "/workflow", + "流程管理", icon: "lucide:git-branch", order: 6); + menus.Add(workflow); + menus.Add(Leaf("40000000-0000-0000-0000-000000000051", "WorkflowStart", "/workflow/start", + "发起流程", "/workflow/start/index", workflow.Id, icon: "lucide:play-circle", order: 1)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000052", "WorkflowDefinitions", "/workflow/definitions", + "流程定义", "/workflow/definitions/index", workflow.Id, icon: "lucide:file-text", order: 2)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000053", "WorkflowTasks", "/workflow/tasks", + "我的任务", "/workflow/tasks/index", workflow.Id, icon: "lucide:clock", order: 3)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000054", "WorkflowInstances", "/workflow/instances", + "流程实例", "/workflow/instances/index", workflow.Id, icon: "lucide:layers", order: 4)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000055", "WorkflowMonitor", "/workflow/monitor", + "流程监控", "/workflow/monitor/index", workflow.Id, icon: "lucide:activity", order: 5)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000056", "WorkflowForms", "/workflow/forms", + "表单管理", "/workflow/forms/index", workflow.Id, icon: "lucide:file-edit", order: 6)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000057", "WorkflowFormComponents", "/workflow/form-components", + "组件管理", "/workflow/form-components/index", workflow.Id, icon: "lucide:components", order: 7)); + // 辅助页(hideInMenu) + menus.Add(Leaf("40000000-0000-0000-0000-000000000058", "WorkflowFormEditor", "/workflow/forms/editor/:id", + "表单设计器", "/workflow/forms/designer/index", workflow.Id, hideInMenu: true, order: 8)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000059", "WorkflowFormVersions", "/workflow/forms/versions/:id", + "版本对比", "/workflow/forms/versions/index", workflow.Id, hideInMenu: true, order: 9)); + menus.Add(Leaf("40000000-0000-0000-0000-00000000005a", "WorkflowInstanceDetail", "/workflow/instances/detail/:id", + "实例详情", "/workflow/instances/detail/index", workflow.Id, hideInMenu: true, order: 10)); + menus.Add(Leaf("40000000-0000-0000-0000-00000000005b", "WorkflowDesigner", "/workflow/designer/:id", + "流程设计器", "/workflow/designer/index", workflow.Id, hideInMenu: true, order: 11)); + + // ── 文件管理 (order=10) ── + var fileMgmt = Menu("40000000-0000-0000-0000-000000000080", "FileManagement", "/file-management", + "page.fileManagement.title", icon: "lucide:folder-open", order: 10); + menus.Add(fileMgmt); + menus.Add(Leaf("40000000-0000-0000-0000-000000000081", "FileManager", "/file-manager", + "page.fileManagement.files", "/file-management/files/index", fileMgmt.Id, icon: "lucide:hard-drive")); + + // ── 系统管理 (order=9):权限/组织架构/审计日志 ── + var system = Menu("40000000-0000-0000-0000-000000000095", "System", "/system", + "系统管理", icon: "lucide:settings", order: 9); + menus.Add(system); + menus.Add(Leaf("40000000-0000-0000-0000-000000000096", "Permissions", "/system/permissions", + "权限管理", "/system/permissions/index", system.Id, icon: "lucide:shield-check", order: 1)); + // 权限矩阵:辅助页,不在菜单显示,由权限页内 Tab 跳转 + menus.Add(Leaf("40000000-0000-0000-0000-000000000097", "PermissionsMatrix", "/system/permissions/matrix", + "权限矩阵", "/system/permissions/matrix", system.Id, hideInMenu: true, order: 2)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000098", "Organization", "/system/organization", + "组织架构", "/system/organization/index", system.Id, icon: "lucide:users", order: 3)); + menus.Add(Leaf("40000000-0000-0000-0000-000000000099", "AuditLogs", "/system/audit-logs", + "审计日志", "/system/audit-logs/index", system.Id, icon: "lucide:scroll-text", order: 4)); + + // ── 个人中心(顶级,hideInMenu)── + menus.Add(Leaf("40000000-0000-0000-0000-000000000090", "Profile", "/profile", + "个人中心", "/_core/profile/index", null, icon: "lucide:user", hideInMenu: true)); + + return menus; + } + + /// 构建顶级(容器型)菜单。 + private static Menu Menu(string id, string name, string path, string title, + string? icon = null, int order = 0, string? redirect = null) + => new() + { + Id = Guid.Parse(id), + Name = name, + Path = path, + Redirect = redirect, + Title = title, + Icon = icon, + Order = order, + // IAuditable / ISoftDelete / IHasOperatorIP 由 AuditInterceptor 在 SaveChanges 时填充, + // 但种子数据走 AddRangeAsync 后立即 SaveChangesAsync,拦截器仍会触发,确保非空。 + }; + + /// 构建叶子(页面型)菜单。 + private static Menu Leaf(string id, string name, string path, string title, + string component, Guid? parentId, string? icon = null, + int order = 0, bool hideInMenu = false, bool? affixTab = null) + => new() + { + Id = Guid.Parse(id), + Name = name, + Path = path, + Component = component, + ParentId = parentId, + Title = title, + Icon = icon, + Order = order, + HideInMenu = hideInMenu, + AffixTab = affixTab, + }; +} diff --git a/src/modules/platform/Infrastructure/Platform.Infrastructure.csproj b/src/modules/platform/Infrastructure/Platform.Infrastructure.csproj new file mode 100644 index 0000000..1c6b8ff --- /dev/null +++ b/src/modules/platform/Infrastructure/Platform.Infrastructure.csproj @@ -0,0 +1,22 @@ + + + net10.0 + enable + enable + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/src/modules/platform/Infrastructure/PlatformDbContextDesignTimeFactory.cs b/src/modules/platform/Infrastructure/PlatformDbContextDesignTimeFactory.cs new file mode 100644 index 0000000..34562f7 --- /dev/null +++ b/src/modules/platform/Infrastructure/PlatformDbContextDesignTimeFactory.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using BuildFlow.Platform.Infrastructure.Persistence; + +namespace BuildFlow.Platform.Infrastructure; + +/// 设计时工厂(供 dotnet ef migrations 生成迁移用)。 +public class PlatformDbContextDesignTimeFactory : IDesignTimeDbContextFactory +{ + public PlatformDbContext CreateDbContext(string[] args) + { + var options = new DbContextOptionsBuilder() + .UseNpgsql("Host=localhost;Port=15432;Database=platform;Username=rag;Password=dev") + .UseSnakeCaseNamingConvention() + .Options; + return new PlatformDbContext(options); + } +} diff --git a/src/modules/platform/Infrastructure/PlatformInfrastructureModule.cs b/src/modules/platform/Infrastructure/PlatformInfrastructureModule.cs new file mode 100644 index 0000000..ebb9b0f --- /dev/null +++ b/src/modules/platform/Infrastructure/PlatformInfrastructureModule.cs @@ -0,0 +1,26 @@ +using BuildFlow.Platform.Domain; +using BuildFlow.Platform.Infrastructure.Persistence; +using BuildFlow.Shared.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace BuildFlow.Platform.Infrastructure; + +/// 平台基础能力基础设施层模块。注册 PlatformDbContext(连 platform 库)。 +[DependsOn(typeof(PlatformDomainModule))] +public class PlatformInfrastructureModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var services = context.Services; + var config = services.GetConfiguration(); + + var conn = $"{config.GetConnectionString("Default")};Database={config["DatabaseNames:Platform"]}"; + services.AddDbContext((sp, options) => + options.UseNpgsql(conn) + .AddInterceptors(sp.GetRequiredService())); + services.AddScoped(); + } +} diff --git a/src/modules/rag/Application/RAGApplicationModule.cs b/src/modules/rag/Application/RAGApplicationModule.cs index 276ff95..317c99a 100644 --- a/src/modules/rag/Application/RAGApplicationModule.cs +++ b/src/modules/rag/Application/RAGApplicationModule.cs @@ -1,7 +1,6 @@ using FluentValidation; using MediatR; using Microsoft.Extensions.DependencyInjection; -using BuildFlow.Application.Auth.Commands; using BuildFlow.Application.Common; using BuildFlow.Infrastructure; using Volo.Abp.Modularity; @@ -17,7 +16,7 @@ public class RAGApplicationModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { var services = context.Services; - var assembly = typeof(RegisterCommand).Assembly; + var assembly = typeof(RAGApplicationModule).Assembly; services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(assembly)); services.AddValidatorsFromAssembly(assembly); diff --git a/src/modules/rag/Domain/Entities/Conversation.cs b/src/modules/rag/Domain/Entities/Conversation.cs index 6e92807..9531487 100644 --- a/src/modules/rag/Domain/Entities/Conversation.cs +++ b/src/modules/rag/Domain/Entities/Conversation.cs @@ -23,8 +23,7 @@ public class Conversation : BaseEntity, IFullAudit // IHasOperatorIP public string OperatorIP { get; set; } = default!; - // Navigation - public User User { get; set; } = default!; + // Navigation(User 实体已拆到 platform 模块,这里只保留 UserId 外键) public KnowledgeBase? KnowledgeBase { get; set; } public ICollection Messages { get; set; } = []; } diff --git a/src/modules/rag/Infrastructure/Persistence/Configurations/ConversationConfiguration.cs b/src/modules/rag/Infrastructure/Persistence/Configurations/ConversationConfiguration.cs index 51f01fb..fc380f1 100644 --- a/src/modules/rag/Infrastructure/Persistence/Configurations/ConversationConfiguration.cs +++ b/src/modules/rag/Infrastructure/Persistence/Configurations/ConversationConfiguration.cs @@ -31,10 +31,7 @@ public class ConversationConfiguration : IEntityTypeConfiguration // IHasOperatorIP builder.Property(c => c.OperatorIP).HasMaxLength(50).HasComment("操作者IP地址"); - builder.HasOne(c => c.User) - .WithMany() - .HasForeignKey(c => c.UserId) - .OnDelete(DeleteBehavior.Cascade); + // User 导航已移除(跨模块),UserId 作为外键保留,不做 FK 约束(跨库无法约束) builder.HasMany(c => c.Messages) .WithOne(m => m.Conversation) diff --git a/src/modules/rag/Infrastructure/Persistence/RagDbContext.cs b/src/modules/rag/Infrastructure/Persistence/RagDbContext.cs index ee81ea9..b50e89b 100644 --- a/src/modules/rag/Infrastructure/Persistence/RagDbContext.cs +++ b/src/modules/rag/Infrastructure/Persistence/RagDbContext.cs @@ -6,36 +6,24 @@ using BuildFlow.Domain.Entities; namespace BuildFlow.Infrastructure.Persistence; /// -/// RAG 系统统一 DbContext。聚合用户/角色/权限、会话/消息、知识库/文档/分块等全部聚合根。 -/// 关键扩展:启用 PostgreSQL 的 pgvector 扩展(向量列类型),并为所有 ISoftDelete 实体 -/// 自动注入全局查询过滤器,使业务层默认看不见已软删记录。 +/// RAG 业务 DbContext。只管 RAG 专属表(知识库/文档/分块/会话/消息)。 +/// 平台表(User/Role/Permission/Menu/Notification)已拆到 PlatformDbContext。 /// public class RagDbContext : DbContext { public RagDbContext(DbContextOptions options) : base(options) { } - public DbSet Users => Set(); - public DbSet Roles => Set(); - public DbSet Permissions => Set(); - public DbSet UserRoles => Set(); - public DbSet RolePermissions => Set(); - public DbSet RefreshTokens => Set(); - public DbSet Menus => Set(); public DbSet Conversations => Set(); public DbSet ChatMessages => Set(); public DbSet KnowledgeBases => Set(); public DbSet Documents => Set(); public DbSet DocumentChunks => Set(); - public DbSet Notifications => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasPostgresExtension("vector"); modelBuilder.ApplyConfigurationsFromAssembly(typeof(RagDbContext).Assembly); - // 为所有实现 ISoftDelete 的实体自动注册全局查询过滤器:e => !e.IsDeleted - // 查询时自动排除已软删除的记录,无需每个查询手动过滤 - // 如需查询已删除记录,使用 IgnoreQueryFilters() foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType)) diff --git a/src/modules/rag/Infrastructure/Persistence/SeedData.cs b/src/modules/rag/Infrastructure/Persistence/SeedData.cs index fbd03e8..86c8c94 100644 --- a/src/modules/rag/Infrastructure/Persistence/SeedData.cs +++ b/src/modules/rag/Infrastructure/Persistence/SeedData.cs @@ -1,374 +1,10 @@ -using BuildFlow.Domain.Entities; -using Microsoft.EntityFrameworkCore; - namespace BuildFlow.Infrastructure.Persistence; /// -/// SeedData类型。 +/// RAG 模块种子数据。平台种子(权限/角色/用户/菜单)已迁移到 platform 模块。 +/// RAG 业务种子(知识库示例等)按需在此添加。 /// public static class SeedData { - public static async Task SeedAsync(RagDbContext db) - { - // 权限 - await SeedPermissionsAsync(db); - - // 角色 - if (!await db.Roles.AnyAsync()) - { - var superAdminRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000001"), Name = "SuperAdmin", Description = "超级管理员" }; - var adminRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000002"), Name = "Admin", Description = "管理员" }; - var userRole = new Role { Id = Guid.Parse("20000000-0000-0000-0000-000000000003"), Name = "User", Description = "普通用户" }; - await db.Roles.AddRangeAsync(superAdminRole, adminRole, userRole); - await db.SaveChangesAsync(); - } - - // 角色-权限关联 - if (!await db.RolePermissions.AnyAsync()) - { - var superAdminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000001"); - var adminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000002"); - var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); - - var allPerms = await db.Permissions.ToListAsync(); - await db.RolePermissions.AddRangeAsync(allPerms.Select(p => new RolePermission { RoleId = superAdminRoleId, PermissionId = p.Id })); - await db.RolePermissions.AddRangeAsync(allPerms.Where(p => p.Code is "user:read" or "user:create" or "user:update" or "role:read" or "permission:read").Select(p => new RolePermission { RoleId = adminRoleId, PermissionId = p.Id })); - await db.RolePermissions.AddRangeAsync(allPerms.Where(p => p.Code is "user:read" or "role:read" or "permission:read").Select(p => new RolePermission { RoleId = userRoleId, PermissionId = p.Id })); - await db.SaveChangesAsync(); - } - await EnsureDefaultRolePermissionsAsync(db); - - // 管理员用户 - var adminUserId = Guid.Parse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"); - var existingAdmin = await db.Users.FirstOrDefaultAsync(u => u.Id == adminUserId); - if (existingAdmin == null) - { - existingAdmin = await db.Users.FirstOrDefaultAsync(u => u.Username == "admin"); - if (existingAdmin == null) - { - existingAdmin = new User - { - Id = adminUserId, - Username = "admin", - Email = "admin@rag.local", - PasswordHash = BCrypt.Net.BCrypt.HashPassword("admin123") - }; - await db.Users.AddAsync(existingAdmin); - await db.SaveChangesAsync(); - } - } - - // 确保管理员有 SuperAdmin 角色 - var superAdminId = Guid.Parse("20000000-0000-0000-0000-000000000001"); - var hasRole = await db.UserRoles.AnyAsync(ur => ur.UserId == existingAdmin.Id && ur.RoleId == superAdminId); - if (!hasRole) - { - await db.UserRoles.AddAsync(new UserRole { UserId = existingAdmin.Id, RoleId = superAdminId }); - await db.SaveChangesAsync(); - } - - // IM 演示用户:ID 与 im-system SeedData 保持一致,供通讯录和 Auth gRPC 查询使用 - await SeedImDemoUsersAsync(db); - - // ===== 菜单种子数据 ===== - await SeedMenusAsync(db); - } - - private static async Task SeedPermissionsAsync(RagDbContext db) - { - var permissionSeeds = new[] - { - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000001"), "查看用户", "user:read", "UserManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000002"), "创建用户", "user:create", "UserManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000003"), "更新用户", "user:update", "UserManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000004"), "删除用户", "user:delete", "UserManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000005"), "分配角色", "user:assign-role", "UserManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000006"), "查看角色", "role:read", "RoleManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000007"), "创建角色", "role:create", "RoleManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000008"), "更新角色", "role:update", "RoleManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000009"), "删除角色", "role:delete", "RoleManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000010"), "分配权限", "role:assign-permission", "RoleManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000011"), "查看权限", "permission:read", "SystemManagement"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000018"), "发布通知", "notification:publish", "NotificationCenter"), - new SeedPermission(Guid.Parse("10000000-0000-0000-0000-000000000019"), "查看通知", "notification:read", "NotificationCenter"), - }; - - var existingCodes = await db.Permissions.Select(p => p.Code).ToListAsync(); - var permissions = permissionSeeds - .Where(p => !existingCodes.Contains(p.Code)) - .Select(p => new Permission { Id = p.Id, Name = p.Name, Code = p.Code, Group = p.Group }) - .ToList(); - - if (permissions.Count == 0) return; - - await db.Permissions.AddRangeAsync(permissions); - await db.SaveChangesAsync(); - } - - private static async Task EnsureDefaultRolePermissionsAsync(RagDbContext db) - { - var superAdminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000001"); - var adminRoleId = Guid.Parse("20000000-0000-0000-0000-000000000002"); - var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); - - var allPermissionCodes = await db.Permissions.Select(p => p.Code).ToListAsync(); - await EnsureRolePermissionsAsync(db, superAdminRoleId, allPermissionCodes); - await EnsureRolePermissionsAsync(db, adminRoleId, [ - "user:read", - "user:create", - "user:update", - "role:read", - "permission:read" - ]); - await EnsureRolePermissionsAsync(db, userRoleId, [ - "user:read", - "role:read", - "permission:read" - ]); - } - - private static async Task EnsureRolePermissionsAsync(RagDbContext db, Guid roleId, IReadOnlyCollection permissionCodes) - { - var roleExists = await db.Roles.AnyAsync(r => r.Id == roleId); - if (!roleExists) return; - - var codes = permissionCodes.ToArray(); - var permissionIds = await db.Permissions - .Where(p => codes.Contains(p.Code)) - .Select(p => p.Id) - .ToListAsync(); - - var existingPermissionIds = await db.RolePermissions - .Where(rp => rp.RoleId == roleId && permissionIds.Contains(rp.PermissionId)) - .Select(rp => rp.PermissionId) - .ToListAsync(); - - var missingRolePermissions = permissionIds - .Except(existingPermissionIds) - .Select(permissionId => new RolePermission { RoleId = roleId, PermissionId = permissionId }) - .ToList(); - - if (missingRolePermissions.Count == 0) return; - - await db.RolePermissions.AddRangeAsync(missingRolePermissions); - await db.SaveChangesAsync(); - } - - private static async Task SeedImDemoUsersAsync(RagDbContext db) - { - var userRoleId = Guid.Parse("20000000-0000-0000-0000-000000000003"); - var users = new[] - { - new SeedUser( - Guid.Parse("30000000-0000-0000-0000-000000000002"), - "zhangsan", - "zhangsan@rag.local"), - new SeedUser( - Guid.Parse("30000000-0000-0000-0000-000000000003"), - "lisi", - "lisi@rag.local"), - new SeedUser( - Guid.Parse("30000000-0000-0000-0000-000000000004"), - "wangwu", - "wangwu@rag.local"), - }; - - foreach (var seedUser in users) - { - var exists = await db.Users.AnyAsync(u => - u.Id == seedUser.Id || - u.Username == seedUser.Username || - u.Email == seedUser.Email); - if (exists) continue; - - await db.Users.AddAsync(new User - { - Id = seedUser.Id, - Username = seedUser.Username, - Email = seedUser.Email, - PasswordHash = BCrypt.Net.BCrypt.HashPassword("user123"), - IsActive = true - }); - } - - await db.SaveChangesAsync(); - - foreach (var seedUser in users) - { - var userExists = await db.Users.AnyAsync(u => u.Id == seedUser.Id); - if (!userExists) continue; - - var hasUserRole = await db.UserRoles.AnyAsync(ur => - ur.UserId == seedUser.Id && ur.RoleId == userRoleId); - if (hasUserRole) continue; - - await db.UserRoles.AddAsync(new UserRole - { - UserId = seedUser.Id, - RoleId = userRoleId - }); - } - - await db.SaveChangesAsync(); - } - - /// 权限种子记录(Id 固定 Guid,保证幂等重跑不重复插入)。 - private sealed record SeedPermission(Guid Id, string Name, string Code, string Group); - - /// 用户种子记录(Id 固定,密码在主流程中 BCrypt 哈希后写入)。 - private sealed record SeedUser(Guid Id, string Username, string Email); - - /// - /// 全量菜单种子入口。幂等:menus 表非空时跳过。供 BuildFlow Host 启动时独立调用, - /// 不依赖用户种子是否执行(backend 模式动态菜单依赖 menus 表必须有数据)。 - /// - public static async Task SeedMenusAsync(RagDbContext db) - { - // 幂等:按 Id 补种缺失的菜单。已存在的(含用户改名/调序)不动。 - // 这样升级后新增菜单项能在已有数据库里自动出现,而不是「库非空就整体跳过」。 - var existingIds = await db.Menus.Select(m => m.Id).ToHashSetAsync(); - var toAdd = BuildSeedMenus().Where(m => !existingIds.Contains(m.Id)).ToList(); - if (toAdd.Count == 0) return; - await db.Menus.AddRangeAsync(toAdd); - await db.SaveChangesAsync(); - } - - /// - /// 构建全量菜单种子数据。对应前端 backend 模式动态菜单。 - /// Authority 为空表示所有已认证用户可见;后续可按角色名(逗号分隔)收紧。 - /// Guid 采用固定命名空间,便于幂等补种与排查。 - /// - private static List BuildSeedMenus() - { - var menus = new List(); - - // ── Dashboard (order=-1):数据看板作为首页,不再 redirect 到 analytics ── - var dashboard = Menu("40000000-0000-0000-0000-000000000001", "Dashboard", "/dashboard", - "page.dashboard.title", icon: "lucide:layout-dashboard", order: -1); - menus.Add(dashboard); - menus.Add(Leaf("40000000-0000-0000-0000-000000000002", "Analytics", "/analytics", - "page.dashboard.analytics", "/dashboard/analytics/index", dashboard.Id, order: 1, affixTab: true)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000003", "Workspace", "/workspace", - "page.dashboard.workspace", "/dashboard/workspace/index", dashboard.Id, order: 2)); - // 数据看板(新首页大屏):放 dashboard 容器下作为默认页 - menus.Add(Leaf("40000000-0000-0000-0000-000000000004", "DashboardOverview", "/dashboard", - "数据看板", "/dashboard/index", dashboard.Id, order: 0, affixTab: true)); - - // ── AI 对话 (order=3) ── - var aiChat = Menu("40000000-0000-0000-0000-000000000010", "AiChat", "/ai-chat", - "AI 对话", icon: "lucide:bot", order: 3); - menus.Add(aiChat); - menus.Add(Leaf("40000000-0000-0000-0000-000000000011", "AiChatMain", "/ai-chat", - "AI 对话", "/ai-chat/index", aiChat.Id)); - - // ── 知识库 (order=4) ── - var kb = Menu("40000000-0000-0000-0000-000000000020", "KnowledgeBase", "/knowledge-base", - "知识库", icon: "lucide:database", order: 4); - menus.Add(kb); - menus.Add(Leaf("40000000-0000-0000-0000-000000000021", "KnowledgeBaseList", "/knowledge-base", - "知识库管理", "/knowledge-base/index", kb.Id)); - - // ── BuildFlow (order=5) ── - var buildflow = Menu("40000000-0000-0000-0000-000000000030", "BuildFlow", "/buildflow", - "BuildFlow", icon: "lucide:blocks", order: 5); - menus.Add(buildflow); - menus.Add(Leaf("40000000-0000-0000-0000-000000000031", "BuildFlowConsole", "/buildflow", - "控制台", "/buildflow/index", buildflow.Id, icon: "lucide:settings-2")); - - // ── 即时通讯 (order=5) ── - var chat = Menu("40000000-0000-0000-0000-000000000040", "Chat", "/chat", - "即时通讯", icon: "lucide:message-square", order: 5); - menus.Add(chat); - menus.Add(Leaf("40000000-0000-0000-0000-000000000041", "ChatMain", "/chat", - "聊天", "/im/index", chat.Id)); - - // ── 流程管理 (order=6) ── - var workflow = Menu("40000000-0000-0000-0000-000000000050", "Workflow", "/workflow", - "流程管理", icon: "lucide:git-branch", order: 6); - menus.Add(workflow); - menus.Add(Leaf("40000000-0000-0000-0000-000000000051", "WorkflowStart", "/workflow/start", - "发起流程", "/workflow/start/index", workflow.Id, icon: "lucide:play-circle", order: 1)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000052", "WorkflowDefinitions", "/workflow/definitions", - "流程定义", "/workflow/definitions/index", workflow.Id, icon: "lucide:file-text", order: 2)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000053", "WorkflowTasks", "/workflow/tasks", - "我的任务", "/workflow/tasks/index", workflow.Id, icon: "lucide:clock", order: 3)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000054", "WorkflowInstances", "/workflow/instances", - "流程实例", "/workflow/instances/index", workflow.Id, icon: "lucide:layers", order: 4)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000055", "WorkflowMonitor", "/workflow/monitor", - "流程监控", "/workflow/monitor/index", workflow.Id, icon: "lucide:activity", order: 5)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000056", "WorkflowForms", "/workflow/forms", - "表单管理", "/workflow/forms/index", workflow.Id, icon: "lucide:file-edit", order: 6)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000057", "WorkflowFormComponents", "/workflow/form-components", - "组件管理", "/workflow/form-components/index", workflow.Id, icon: "lucide:components", order: 7)); - // 辅助页(hideInMenu) - menus.Add(Leaf("40000000-0000-0000-0000-000000000058", "WorkflowFormEditor", "/workflow/forms/editor/:id", - "表单设计器", "/workflow/forms/designer/index", workflow.Id, hideInMenu: true, order: 8)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000059", "WorkflowFormVersions", "/workflow/forms/versions/:id", - "版本对比", "/workflow/forms/versions/index", workflow.Id, hideInMenu: true, order: 9)); - menus.Add(Leaf("40000000-0000-0000-0000-00000000005a", "WorkflowInstanceDetail", "/workflow/instances/detail/:id", - "实例详情", "/workflow/instances/detail/index", workflow.Id, hideInMenu: true, order: 10)); - menus.Add(Leaf("40000000-0000-0000-0000-00000000005b", "WorkflowDesigner", "/workflow/designer/:id", - "流程设计器", "/workflow/designer/index", workflow.Id, hideInMenu: true, order: 11)); - - // ── 文件管理 (order=10) ── - var fileMgmt = Menu("40000000-0000-0000-0000-000000000080", "FileManagement", "/file-management", - "page.fileManagement.title", icon: "lucide:folder-open", order: 10); - menus.Add(fileMgmt); - menus.Add(Leaf("40000000-0000-0000-0000-000000000081", "FileManager", "/file-manager", - "page.fileManagement.files", "/file-management/files/index", fileMgmt.Id, icon: "lucide:hard-drive")); - - // ── 系统管理 (order=9):权限/组织架构/审计日志 ── - var system = Menu("40000000-0000-0000-0000-000000000095", "System", "/system", - "系统管理", icon: "lucide:settings", order: 9); - menus.Add(system); - menus.Add(Leaf("40000000-0000-0000-0000-000000000096", "Permissions", "/system/permissions", - "权限管理", "/system/permissions/index", system.Id, icon: "lucide:shield-check", order: 1)); - // 权限矩阵:辅助页,不在菜单显示,由权限页内 Tab 跳转 - menus.Add(Leaf("40000000-0000-0000-0000-000000000097", "PermissionsMatrix", "/system/permissions/matrix", - "权限矩阵", "/system/permissions/matrix", system.Id, hideInMenu: true, order: 2)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000098", "Organization", "/system/organization", - "组织架构", "/system/organization/index", system.Id, icon: "lucide:users", order: 3)); - menus.Add(Leaf("40000000-0000-0000-0000-000000000099", "AuditLogs", "/system/audit-logs", - "审计日志", "/system/audit-logs/index", system.Id, icon: "lucide:scroll-text", order: 4)); - - // ── 个人中心(顶级,hideInMenu)── - menus.Add(Leaf("40000000-0000-0000-0000-000000000090", "Profile", "/profile", - "个人中心", "/_core/profile/index", null, icon: "lucide:user", hideInMenu: true)); - - return menus; - } - - /// 构建顶级(容器型)菜单。 - private static Menu Menu(string id, string name, string path, string title, - string? icon = null, int order = 0, string? redirect = null) - => new() - { - Id = Guid.Parse(id), - Name = name, - Path = path, - Redirect = redirect, - Title = title, - Icon = icon, - Order = order, - // IAuditable / ISoftDelete / IHasOperatorIP 由 AuditInterceptor 在 SaveChanges 时填充, - // 但种子数据走 AddRangeAsync 后立即 SaveChangesAsync,拦截器仍会触发,确保非空。 - }; - - /// 构建叶子(页面型)菜单。 - private static Menu Leaf(string id, string name, string path, string title, - string component, Guid? parentId, string? icon = null, - int order = 0, bool hideInMenu = false, bool? affixTab = null) - => new() - { - Id = Guid.Parse(id), - Name = name, - Path = path, - Component = component, - ParentId = parentId, - Title = title, - Icon = icon, - Order = order, - HideInMenu = hideInMenu, - AffixTab = affixTab, - }; + // 平台种子已迁移到 BuildFlow.Platform.Infrastructure.Persistence.SeedData } diff --git a/tests/BuildFlow.File.Tests/DownloadFileQueryHandlerTests.cs b/tests/BuildFlow.File.Tests/DownloadFileQueryHandlerTests.cs index 7506364..c504fa1 100644 --- a/tests/BuildFlow.File.Tests/DownloadFileQueryHandlerTests.cs +++ b/tests/BuildFlow.File.Tests/DownloadFileQueryHandlerTests.cs @@ -38,17 +38,18 @@ public class DownloadFileQueryHandlerTests new DownloadFileQuery("test-bucket", "test-key"), CancellationToken.None); - // Assert + // Assert — 返回的是 S3 原始流,透传给调用方;ContentType 取 S3 返回的真实类型 result.Should().NotBeNull(); - result.Data.Should().NotBeNullOrEmpty(); - result.ContentType.Should().Be("application/octet-stream"); + result.Stream.Should().NotBeNull(); + result.ContentType.Should().Be("text/plain"); + result.FileName.Should().Be("test-key"); s3Mock.Verify(x => x.DownloadFileAsync("test-bucket", "test-key", It.IsAny()), Times.Once); } [Fact] - public async Task DownloadFile_S3ReturnsEmpty_ReturnsEmptyArray() + public async Task DownloadFile_S3ReturnsEmpty_ReturnsReadableEmptyStream() { - // Arrange — DownloadFileQueryHandler 不校验空内容,直接返回空 byte[] + // Arrange — DownloadFileQueryHandler 透传 S3 流,空内容对应空 MemoryStream var s3Mock = new Mock(); var emptyStream = new MemoryStream(); s3Mock @@ -62,8 +63,8 @@ public class DownloadFileQueryHandlerTests new DownloadFileQuery("test-bucket", "test-key"), CancellationToken.None); - // Assert — Handler 不做空内容校验,返回空数组 - result.Data.Should().NotBeNull(); - result.Data.Should().BeEmpty(); + // Assert — Handler 透传空流,长度为 0 + result.Stream.Should().NotBeNull(); + result.Stream.Length.Should().Be(0); } } diff --git a/tests/BuildFlow.Rag.Tests/Auth/LoginCommandHandlerTests.cs b/tests/BuildFlow.Rag.Tests/Auth/LoginCommandHandlerTests.cs deleted file mode 100644 index 20f8f1e..0000000 --- a/tests/BuildFlow.Rag.Tests/Auth/LoginCommandHandlerTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using BuildFlow.Application.Auth.Commands; -using BuildFlow.Application.Auth.Handlers; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Shared.Domain.Exceptions; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Xunit; - -namespace BuildFlow.Rag.Tests.Auth; - -/// -/// 登录命令处理器单元测试。覆盖正常登录、密码错误、用户不存在、账号停用四个核心场景。 -/// 使用 EF Core InMemory 作为测试数据库,手动填充审计字段(生产由 AuditInterceptor 自动填充)。 -/// -public class LoginCommandHandlerTests -{ - /// 创建 InMemory 数据库 + 种子用户。 - private static async Task<(RagDbContext db, LoginCommandHandler handler)> SetupAsync( - string username = "admin", - string password = "admin123", - bool isActive = true) - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"login-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - - var user = new User - { - Id = Guid.NewGuid(), - Username = username, - Email = $"{username}@rag.local", - PasswordHash = BCrypt.Net.BCrypt.HashPassword(password), - IsActive = isActive, - CreatedBy = "system", - UpdatedBy = "system", - OperatorIP = "127.0.0.1", - }; - db.Users.Add(user); - await db.SaveChangesAsync(); - - // IConfiguration 用最小配置(JWT 签名密钥 + issuer/audience) - var config = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["Jwt:SigningKey"] = "REDACTED_JWT_SIGNING_KEY_MIN_32_CHARS!!", - ["Jwt:Issuer"] = "http://localhost:5211", - ["Jwt:Audience"] = "rag-frontend", - }) - .Build(); - - var handler = new LoginCommandHandler(db, config); - return (db, handler); - } - - [Fact(Skip = "LoginCommandHandler 内部创建 RefreshToken 需要审计字段,InMemory 无 AuditInterceptor,集成测试覆盖")] - public async Task Login_WithValidCredentials_ReturnsTokenResponse() - { - // Arrange - var (db, handler) = await SetupAsync("admin", "admin123"); - - // Act - var result = await handler.Handle(new LoginCommand("admin", "admin123"), CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.AccessToken.Should().NotBeNullOrEmpty(); - result.RefreshToken.Should().NotBeNullOrEmpty(); - result.ExpiresAt.Should().BeAfter(DateTime.UtcNow); - } - - [Fact] - public async Task Login_WithWrongPassword_ThrowsBusinessException() - { - // Arrange - var (db, handler) = await SetupAsync("admin", "admin123"); - - // Act - var act = () => handler.Handle(new LoginCommand("admin", "wrongpassword"), CancellationToken.None); - - // Assert - await act.Should().ThrowAsync() - .WithMessage("用户名或密码错误"); - } - - [Fact] - public async Task Login_WithNonExistentUser_ThrowsBusinessException() - { - // Arrange - var (db, handler) = await SetupAsync("admin", "admin123"); - - // Act - var act = () => handler.Handle(new LoginCommand("ghost", "anypassword"), CancellationToken.None); - - // Assert - await act.Should().ThrowAsync() - .WithMessage("用户名或密码错误"); - } - - [Fact] - public async Task Login_WithInactiveUser_ThrowsBusinessException() - { - // Arrange - var (db, handler) = await SetupAsync("inactive", "pass123", isActive: false); - - // Act - var act = () => handler.Handle(new LoginCommand("inactive", "pass123"), CancellationToken.None); - - // Assert - await act.Should().ThrowAsync() - .WithMessage("用户已被停用"); - } -} diff --git a/tests/BuildFlow.Rag.Tests/Notifications/PublishNotificationCommandTests.cs b/tests/BuildFlow.Rag.Tests/Notifications/PublishNotificationCommandTests.cs deleted file mode 100644 index 106ae19..0000000 --- a/tests/BuildFlow.Rag.Tests/Notifications/PublishNotificationCommandTests.cs +++ /dev/null @@ -1,129 +0,0 @@ -using BuildFlow.Application.Notifications; -using BuildFlow.Application.Notifications.Commands; -using BuildFlow.Application.Notifications.Handlers; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Xunit; - -namespace BuildFlow.Rag.Tests.Notifications; - -/// -/// 验证消息中心 Fanout 扇出策略:一条业务消息为每个接收人生成一条独立记录, -/// 支持按用户 ID 精确指定与按角色展开,去重后落库。 -/// -public class PublishNotificationCommandTests -{ - private static async Task<(RagDbContext db, PublishNotificationCommandHandler handler, List<(Guid id, string role)> users)> - SetupAsync(string? roleName = "Admin") - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"notifications-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - var dispatcher = new NoOpDispatcher(); - var handler = new PublishNotificationCommandHandler(db, dispatcher); - - // 构造角色 + 用户(用于角色展开测试)。审计字段在测试中手动填充(生产由 AuditInterceptor 填充) - var role = Audit(new Role { Id = Guid.NewGuid(), Name = roleName ?? "Admin" }); - var user1 = Audit(new User { Id = Guid.NewGuid(), Username = "u1", Email = "u1@e.com", PasswordHash = "x" }); - var user2 = Audit(new User { Id = Guid.NewGuid(), Username = "u2", Email = "u2@e.com", PasswordHash = "x" }); - db.Roles.Add(role); - db.Users.AddRange(user1, user2); - db.UserRoles.AddRange( - Audit(new UserRole { UserId = user1.Id, RoleId = role.Id }), - Audit(new UserRole { UserId = user2.Id, RoleId = role.Id })); - await db.SaveChangesAsync(); - - return (db, handler, new() { (user1.Id, roleName ?? "Admin"), (user2.Id, roleName ?? "Admin") }); - } - - /// 填充 IAuditable / IHasOperatorIP 审计字段(测试中绕过 AuditInterceptor)。 - private static T Audit(T entity) where T : BuildFlow.Shared.Domain.Common.IAuditable - { - var now = DateTime.UtcNow; - entity.CreatedBy = "test"; - entity.CreatedAt = now; - entity.UpdatedBy = "test"; - entity.UpdatedAt = now; - // IFullAudit 实体还要求 OperatorIP 非空 - if (entity is BuildFlow.Shared.Domain.Common.IHasOperatorIP hasIp) - hasIp.OperatorIP = "127.0.0.1"; - return entity; - } - - [Fact] - public async Task Publish_with_explicit_user_ids_creates_one_record_per_recipient() - { - var (db, handler, users) = await SetupAsync(); - var recipientIds = users.Select(u => u.id).ToList(); - - var count = await handler.Handle(new PublishNotificationCommand( - Type: "system", Title: "你好", Content: "测试通知", Source: "test", - RelatedId: null, RelatedType: null, - RecipientUserIds: recipientIds, RecipientRoles: null), default); - - count.Should().Be(2); - var notifications = await db.Notifications.ToListAsync(); - notifications.Should().HaveCount(2); - notifications.Select(n => n.RecipientUserId).Should().BeEquivalentTo(recipientIds); - notifications.All(n => !n.IsRead && n.Title == "你好").Should().BeTrue(); - } - - [Fact] - public async Task Publish_by_role_expands_to_all_users_with_that_role() - { - var (db, handler, users) = await SetupAsync(roleName: "Admin"); - - var count = await handler.Handle(new PublishNotificationCommand( - Type: "task-arrived", Title: "新待办", Content: "请处理", Source: "workflow", - RelatedId: "inst-123", RelatedType: "workflow-instance", - RecipientUserIds: null, RecipientRoles: new List { "Admin" }), default); - - count.Should().Be(2); - var notifications = await db.Notifications.ToListAsync(); - notifications.Should().HaveCount(2); - notifications.All(n => n.Source == "workflow" && n.RelatedId == "inst-123").Should().BeTrue(); - } - - [Fact] - public async Task Publish_dedupes_when_user_both_in_ids_and_role() - { - var (db, handler, users) = await SetupAsync(roleName: "Admin"); - var firstUser = users[0].id; - - // 该用户既在 RecipientUserIds 又拥有 Admin 角色 → 只落一条 - var count = await handler.Handle(new PublishNotificationCommand( - Type: "system", Title: "去重测试", Content: "只一条", Source: "test", - RelatedId: null, RelatedType: null, - RecipientUserIds: new List { firstUser }, - RecipientRoles: new List { "Admin" }), default); - - // 两个 Admin 用户都该收到 = 2 条(去重发生在同一用户内) - count.Should().Be(2); - var notifications = await db.Notifications.Where(n => n.RecipientUserId == firstUser).ToListAsync(); - notifications.Should().HaveCount(1); - } - - [Fact] - public async Task Publish_with_no_recipients_returns_zero() - { - var (db, handler, _) = await SetupAsync(); - - var count = await handler.Handle(new PublishNotificationCommand( - Type: "system", Title: "无人接收", Content: "x", Source: "test", - RelatedId: null, RelatedType: null, - RecipientUserIds: null, RecipientRoles: null), default); - - count.Should().Be(0); - (await db.Notifications.ToListAsync()).Should().BeEmpty(); - } - - /// 空操作分发器:测试中不依赖 SignalR,只验证落库。 - private sealed class NoOpDispatcher : INotificationDispatcher - { - public Task DispatchAsync(IReadOnlyList notifications, CancellationToken ct = default) - => Task.CompletedTask; - } -} diff --git a/tests/BuildFlow.Rag.Tests/Roles/GetRoleListQueryHandlerTests.cs b/tests/BuildFlow.Rag.Tests/Roles/GetRoleListQueryHandlerTests.cs deleted file mode 100644 index 30f8392..0000000 --- a/tests/BuildFlow.Rag.Tests/Roles/GetRoleListQueryHandlerTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -using BuildFlow.Application.Roles.Handlers; -using BuildFlow.Application.Roles.Queries; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Xunit; - -namespace BuildFlow.Rag.Tests.Roles; - -/// -/// 角色列表查询处理器单元测试。覆盖正常查询和空表场景。 -/// -public class GetRoleListQueryHandlerTests -{ - private static async Task<(RagDbContext db, GetRoleListQueryHandler handler)> SetupAsync() - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"rolelist-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - - db.Roles.AddRange( - new Role { Id = Guid.NewGuid(), Name = "SuperAdmin", CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" }, - new Role { Id = Guid.NewGuid(), Name = "Admin", CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" }, - new Role { Id = Guid.NewGuid(), Name = "User", CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" } - ); - await db.SaveChangesAsync(); - - var handler = new GetRoleListQueryHandler(db); - return (db, handler); - } - - [Fact] - public async Task GetRoleList_ReturnsAllRoles() - { - // Arrange - var (db, handler) = await SetupAsync(); - - // Act - var result = await handler.Handle(new GetRoleListQuery(), CancellationToken.None); - - // Assert - result.Should().HaveCount(3); - result.Select(r => r.Name).Should().Contain(new[] { "SuperAdmin", "Admin", "User" }); - } - - [Fact] - public async Task GetRoleList_OnEmptyDatabase_ReturnsEmptyList() - { - // Arrange - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"rolelist-empty-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - var handler = new GetRoleListQueryHandler(db); - - // Act - var result = await handler.Handle(new GetRoleListQuery(), CancellationToken.None); - - // Assert - result.Should().BeEmpty(); - } -} diff --git a/tests/BuildFlow.Rag.Tests/Users/CreateUserCommandHandlerTests.cs b/tests/BuildFlow.Rag.Tests/Users/CreateUserCommandHandlerTests.cs deleted file mode 100644 index 5516b09..0000000 --- a/tests/BuildFlow.Rag.Tests/Users/CreateUserCommandHandlerTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -using BuildFlow.Application.Users.Commands; -using BuildFlow.Application.Users.Handlers; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using BuildFlow.Shared.Domain.Exceptions; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Xunit; - -namespace BuildFlow.Rag.Tests.Users; - -/// -/// 创建用户命令处理器单元测试。覆盖正常创建、用户名重复两个核心场景。 -/// -public class CreateUserCommandHandlerTests -{ - /// 创建 InMemory 数据库 + Handler。预种 "User" 角色(CreateUserCommandHandler 内部依赖)。 - private static async Task<(RagDbContext db, CreateUserCommandHandler handler)> SetupAsync() - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"users-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - - // 预种默认 "User" 角色(CreateUserCommandHandler 内部 FirstAsync(r => r.Name == "User") 依赖) - db.Roles.Add(new Role - { - Id = Guid.NewGuid(), - Name = "User", - CreatedBy = "system", - UpdatedBy = "system", - OperatorIP = "127.0.0.1", - }); - await db.SaveChangesAsync(); - - var handler = new CreateUserCommandHandler(db); - return (db, handler); - } - - [Fact(Skip = "Handler 内部创建 User 需 AuditInterceptor 填审计字段,集成测试覆盖")] - public async Task CreateUser_WithValidInput_ReturnsUserDto() - { - // Arrange - var (db, handler) = await SetupAsync(); - - // Act - var result = await handler.Handle( - new CreateUserCommand("testuser", "test@rag.local", "Pass123!"), - CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.Username.Should().Be("testuser"); - result.Email.Should().Be("test@rag.local"); - (await db.Users.CountAsync()).Should().Be(1); - } - - [Fact] - public async Task CreateUser_WithDuplicateUsername_ThrowsBusinessException() - { - // Arrange - var (db, handler) = await SetupAsync(); - db.Users.Add(new User - { - Id = Guid.NewGuid(), - Username = "existing", - Email = "existing@rag.local", - PasswordHash = "x", - CreatedBy = "system", - UpdatedBy = "system", - OperatorIP = "127.0.0.1", - }); - await db.SaveChangesAsync(); - - // Act - var act = () => handler.Handle( - new CreateUserCommand("existing", "new@rag.local", "Pass123!"), - CancellationToken.None); - - // Assert - await act.Should().ThrowAsync() - .WithMessage("*已存在*"); - } -} diff --git a/tests/BuildFlow.Rag.Tests/Users/GetUserListQueryHandlerTests.cs b/tests/BuildFlow.Rag.Tests/Users/GetUserListQueryHandlerTests.cs deleted file mode 100644 index d1c9305..0000000 --- a/tests/BuildFlow.Rag.Tests/Users/GetUserListQueryHandlerTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -using BuildFlow.Application.Users.Handlers; -using BuildFlow.Application.Users.Queries; -using BuildFlow.Domain.Entities; -using BuildFlow.Infrastructure.Persistence; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Xunit; - -namespace BuildFlow.Rag.Tests.Users; - -/// -/// 用户列表查询处理器单元测试。覆盖无搜索、关键字搜索、空表三个场景。 -/// -public class GetUserListQueryHandlerTests -{ - /// 创建 InMemory 数据库 + 种子用户。 - private static async Task<(RagDbContext db, GetUserListQueryHandler handler)> SetupAsync() - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"userlist-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - - db.Users.AddRange( - new User { Id = Guid.NewGuid(), Username = "zhangsan", Email = "z@e.com", PasswordHash = "x", IsActive = true, CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" }, - new User { Id = Guid.NewGuid(), Username = "lisi", Email = "l@e.com", PasswordHash = "x", IsActive = true, CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" }, - new User { Id = Guid.NewGuid(), Username = "wangwu", Email = "w@e.com", PasswordHash = "x", IsActive = false, CreatedBy = "s", UpdatedBy = "s", OperatorIP = "0" } - ); - await db.SaveChangesAsync(); - - var handler = new GetUserListQueryHandler(db); - return (db, handler); - } - - [Fact] - public async Task GetUserList_WithoutSearch_ReturnsAllUsers() - { - // Arrange - var (db, handler) = await SetupAsync(); - - // Act - var result = await handler.Handle(new GetUserListQuery(null), CancellationToken.None); - - // Assert - result.Should().HaveCount(3); - } - - [Fact] - public async Task GetUserList_WithSearch_ReturnsFilteredUsers() - { - // Arrange - var (db, handler) = await SetupAsync(); - - // Act - var result = await handler.Handle(new GetUserListQuery("zhang"), CancellationToken.None); - - // Assert - result.Should().HaveCount(1); - result[0].Username.Should().Be("zhangsan"); - } - - [Fact] - public async Task GetUserList_OnEmptyDatabase_ReturnsEmptyList() - { - // Arrange - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"userlist-empty-{Guid.NewGuid()}") - .Options; - var db = new RagDbContext(options); - var handler = new GetUserListQueryHandler(db); - - // Act - var result = await handler.Handle(new GetUserListQuery(null), CancellationToken.None); - - // Assert - result.Should().BeEmpty(); - } -}