From d662ecdb6dbba0414a2326c4ca72a5feefff414e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=97=AD?= <1619917346@qq.com> Date: Tue, 5 Aug 2025 15:06:15 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E7=8E=AF=E5=A2=83=E6=90=AD=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/UniversalAdminSystem.sln | 48 ++ .../Attributes/RequirePermissionAttribute.cs | 61 ++ .../Attributes/RequireRoleAttribute.cs | 46 ++ .../Controllers/AuthenticationController.cs | 97 ++++ .../Controllers/FileController.cs | 142 +++++ .../Controllers/K2ModelController.cs | 93 +++ .../Controllers/LogController.cs | 71 +++ .../Controllers/PermissionController.cs | 163 ++++++ .../Controllers/RoleController.cs | 225 ++++++++ .../Controllers/SystemSettingsController.cs | 63 +++ .../Controllers/UserManagementController.cs | 176 ++++++ .../Controllers/UserRoleController.cs | 122 ++++ .../K2ModelTests.http | 81 +++ .../K2Model_README.md | 133 +++++ .../Middleware/JwtAuthenticationMiddleware.cs | 126 +++++ .../PermissionRules.json | 10 + .../src/UniversalAdminSystem.Api/Program.cs | 98 ++++ .../Properties/launchSettings.json | 41 ++ .../SystemPermissions.json | 89 +++ .../UniversalAdminSystem.Api.csproj | 23 + .../UniversalAdminSystem.Api.http | 11 + .../UniversalAdminSystem.Api/appsettings.json | 23 + .../Authentication/DTOs/CredentialDto.cs | 3 + .../Authentication/DTOs/LoginDto.cs | 3 + .../Authentication/DTOs/LoginResultDto.cs | 5 + .../Authentication/DTOs/RegisterDto.cs | 3 + .../Authentication/DTOs/RegisterResultDto.cs | 3 + .../Authentication/DTOs/TokenDto.cs | 26 + .../Interfaces/IJwtTokenService.cs | 10 + .../Interfaces/ILoginAppService.cs | 8 + .../Interfaces/IRegisterAppService.cs | 8 + .../Authentication/Service/LoginAppService.cs | 40 ++ .../Service/RegisterAppService.cs | 91 +++ .../Common/Exceptions/BusinessException.cs | 43 ++ .../Common/Results/Result.cs | 54 ++ .../Common/interfaces/IUnitOfWork.cs | 9 + .../FileStorage/DTOs/FileDto.cs | 40 ++ .../FileStorage/Interfaces/IFileAppService.cs | 23 + .../FileStorage/Services/FileAppService.cs | 84 +++ .../LogManagement/DTOs/LogEntryDto.cs | 21 + .../Interfaces/ILogManagementAppService.cs | 52 ++ .../Services/LogManagementAppService.cs | 182 ++++++ .../DTOs/AssignPermissionDto.cs | 11 + .../DTOs/PermissionCreateDto.cs | 15 + .../DTOs/PermissionDto.cs | 13 + .../PermissionManagement/DTOs/RoleDto.cs | 46 ++ .../Interfaces/IPermissionCheckService.cs | 46 ++ .../IPermissionManagementAppService.cs | 37 ++ .../Interfaces/IRoleManagementAppService.cs | 68 +++ .../Services/PermissionCheckService.cs | 349 ++++++++++++ .../PermissionManagementAppService.cs | 141 +++++ .../Services/RoleManagementAppService.cs | 301 ++++++++++ .../SystemSettings/DTOs/SystemSettingDto.cs | 23 + .../Interfaces/ISystemSettingAppService.cs | 37 ++ .../Services/SystemSettingAppService.cs | 154 +++++ .../UniversalAdminSystem.Application.csproj | 13 + .../UserManagement/Dtos/UserCreateDto.cs | 18 + .../UserManagement/Dtos/UserDetailDto.cs | 26 + .../UserManagement/Dtos/UserDto.cs | 24 + .../UserManagement/Dtos/UserUpdateDto.cs | 8 + .../Interface/IPasswordHelper.cs | 11 + .../Interface/IUserManagementAppService.cs | 77 +++ .../Service/UserManagementAppService.cs | 386 +++++++++++++ .../UserPermissionIntegrationService.cs | 142 +++++ .../Core/AggregateRoot.cs | 8 + .../Core/DomainEvent.cs | 23 + .../Core/Events/PermissionEvents.cs | 78 +++ .../Core/Events/RoleEvents.cs | 50 ++ .../Core/Events/UserEvents.cs | 66 +++ .../Core/Exceptions/DomainException.cs} | 0 ...6\345\237\237\345\274\202\345\270\270.txt" | 0 .../Core/Interfaces/IRepository.cs | 40 ++ .../Core/ValueObjects/PermissionId.cs | 22 + .../Core/ValueObjects/RoleId.cs | 19 + .../Core/ValueObjects/UserId.cs | 14 + .../Core/ValueObjects/UserInfoId.cs | 50 ++ ...0\345\200\274\345\257\271\350\261\241.txt" | 0 .../DomainServices.cs | 24 + .../FileStorage/Aggregates/File.cs | 85 +++ .../IRepository/IFileRepository.cs | 14 + .../Interface/IFileDomainService.cs | 6 + .../FileStorage/Services/FileDomainService.cs | 19 + .../ValueObjects/FileAccessLevel.cs | 8 + .../FileStorage/ValueObjects/FileId.cs | 11 + .../FileStorage/ValueObjects/FileName.cs | 16 + .../FileStorage/ValueObjects/FilePath.cs | 16 + .../FileStorage/ValueObjects/FileSize.cs | 17 + .../FileStorage/ValueObjects/FileType.cs | 17 + .../LogManagement/Aggregates/LogEntry.cs | 51 ++ .../IRepository/ILogEntryRepository.cs | 12 + .../Aggregate/Permission.cs | 220 ++++++++ .../PermissionManagement/Aggregate/Role.cs | 186 ++++++ .../Exceptions/PermissionDomainException.cs | 8 + .../IRepository/IPermissionRepository.cs | 55 ++ .../IRepository/IRoleRepository.cs | 52 ++ .../IAssignPermissionDomainService.cs | 42 ++ .../Services/AssignPermissionDomainService.cs | 87 +++ .../Services/ResourceActionValidator.cs | 52 ++ .../ValueObjects/PermissionAction.cs | 64 +++ .../ValueObjects/PermissionCode.cs | 89 +++ .../ValueObjects/PermissionName.cs | 54 ++ .../ValueObjects/PermissionResource.cs | 66 +++ .../ValueObjects/PermissionType.cs | 34 ++ .../ValueObjects/RoleDescription.cs | 14 + .../ValueObjects/RoleName.cs | 18 + ...6\344\270\212\344\270\213\346\226\207.txt" | 0 .../Aggregates/SystemSetting.cs | 36 ++ .../IRepository/ISystemSettingRepository.cs | 10 + .../ValueObjects/SettingDescription.cs | 14 + .../SystemSettings/ValueObjects/SettingKey.cs | 16 + .../ValueObjects/SettingValue.cs | 16 + ...6\344\270\212\344\270\213\346\226\207.txt" | 0 .../UniversalAdminSystem.Domian.csproj | 9 + .../UserManagement/Aggregates/User.cs | 123 ++++ .../UserManagement/Entities/UserInfo.cs | 77 +++ .../IRepository/IUserInfoRepository.cs | 11 + .../IRepository/IUserRepository.cs | 39 ++ .../UserManagement/ValueObj/UserAccount.cs | 16 + .../UserManagement/ValueObj/UserEmail.cs | 16 + .../UserManagement/ValueObj/UserGender.cs | 7 + .../UserManagement/ValueObj/UsersStatus.cs | 9 + ...6\344\270\212\344\270\213\346\226\207.txt" | 0 .../Auth/JwtSettings.cs | 9 + .../Auth/JwtTokenBuilder.cs | 49 ++ .../Auth/JwtTokenService.cs | 88 +++ ...4\350\257\201\345\256\236\347\216\260.txt" | 0 .../Configs/K2Config.cs | 61 ++ .../Configs/SystemPermissionConfig.cs | 30 + .../DependencyInject/AddApplicationService.cs | 97 ++++ .../DependencyInject/AddDomainService.cs | 26 + .../AddInfrastructureService.cs | 131 +++++ .../ServiceCollectionExtensions.cs | 15 + .../FileStorage/IFileStorageService.cs | 11 + .../FileStorage/LocalFileStorageService.cs | 82 +++ .../UniversalAdminSystemDbContext.cs | 227 ++++++++ .../Repositories/BaseRepository.cs | 70 +++ .../Repositories/FileRepository.cs | 36 ++ .../Repositories/LogEntryRepository.cs | 42 ++ .../Repositories/PermissionRepository.cs | 109 ++++ .../Repositories/RoleRepository.cs | 78 +++ .../Repositories/SystemSettingRepository.cs | 27 + .../Repositories/UserInfoRepository.cs | 21 + .../Repositories/UserRepository.cs | 69 +++ .../Persistence/Transaction/UnitOfWork.cs | 40 ++ .../Services/K2ConfigService.cs | 19 + .../Services/K2ModelService.cs | 86 +++ .../Services/PasswordHelper.cs | 62 ++ .../Services/PermissionCacheService.cs | 105 ++++ .../PermissionInitializationService.cs | 106 ++++ .../Services/PermissionRuleConfigService.cs | 112 ++++ .../Services/PermissionRuleFileService.cs | 155 +++++ .../Services/SystemInitializationService.cs | 529 ++++++++++++++++++ .../Services/SystemPermissionConfigLoader.cs | 83 +++ ...UniversalAdminSystem.Infrastructure.csproj | 34 ++ 154 files changed, 9311 insertions(+) create mode 100644 backend/UniversalAdminSystem.sln create mode 100644 backend/src/UniversalAdminSystem.Api/Attributes/RequirePermissionAttribute.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Attributes/RequireRoleAttribute.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/AuthenticationController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/K2ModelController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/LogController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/PermissionController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/RoleController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/SystemSettingsController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/UserManagementController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/UserRoleController.cs create mode 100644 backend/src/UniversalAdminSystem.Api/K2ModelTests.http create mode 100644 backend/src/UniversalAdminSystem.Api/K2Model_README.md create mode 100644 backend/src/UniversalAdminSystem.Api/Middleware/JwtAuthenticationMiddleware.cs create mode 100644 backend/src/UniversalAdminSystem.Api/PermissionRules.json create mode 100644 backend/src/UniversalAdminSystem.Api/Program.cs create mode 100644 backend/src/UniversalAdminSystem.Api/Properties/launchSettings.json create mode 100644 backend/src/UniversalAdminSystem.Api/SystemPermissions.json create mode 100644 backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.csproj create mode 100644 backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http create mode 100644 backend/src/UniversalAdminSystem.Api/appsettings.json create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/CredentialDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginResultDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterResultDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/DTOs/TokenDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IJwtTokenService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/ILoginAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IRegisterAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/Service/LoginAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Authentication/Service/RegisterAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Common/Exceptions/BusinessException.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Common/Results/Result.cs create mode 100644 backend/src/UniversalAdminSystem.Application/Common/interfaces/IUnitOfWork.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/LogManagement/DTOs/LogEntryDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/LogManagement/Interfaces/ILogManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/LogManagement/Services/LogManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/AssignPermissionDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionCreateDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/RoleDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionCheckService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IRoleManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionCheckService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/RoleManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/SystemSettings/DTOs/SystemSettingDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/SystemSettings/Interfaces/ISystemSettingAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/SystemSettings/Services/SystemSettingAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserCreateDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDetailDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserUpdateDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IPasswordHelper.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IUserManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserManagementAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserPermissionIntegrationService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/AggregateRoot.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/DomainEvent.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/Events/PermissionEvents.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/Events/RoleEvents.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/Events/UserEvents.cs rename backend/{txt.txt => src/UniversalAdminSystem.Domian/Core/Exceptions/DomainException.cs} (100%) create mode 100644 "backend/src/UniversalAdminSystem.Domian/Core/Exceptions/\351\242\206\345\237\237\345\274\202\345\270\270.txt" create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/Interfaces/IRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/PermissionId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/RoleId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserInfoId.cs create mode 100644 "backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/\351\200\232\347\224\250\345\200\274\345\257\271\350\261\241.txt" create mode 100644 backend/src/UniversalAdminSystem.Domian/DomainServices.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/IRepository/IFileRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileName.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FilePath.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileSize.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/LogManagement/Aggregates/LogEntry.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/LogManagement/IRepository/ILogEntryRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Permission.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Role.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Exceptions/PermissionDomainException.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IPermissionRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IRoleRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Interfaces/IAssignPermissionDomainService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/AssignPermissionDomainService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/ResourceActionValidator.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionCode.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionName.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionResource.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionType.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleDescription.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleName.cs create mode 100644 "backend/src/UniversalAdminSystem.Domian/PermissionManagement/\346\235\203\351\231\220\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" create mode 100644 backend/src/UniversalAdminSystem.Domian/SystemSettings/Aggregates/SystemSetting.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/SystemSettings/IRepository/ISystemSettingRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingDescription.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingKey.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingValue.cs create mode 100644 "backend/src/UniversalAdminSystem.Domian/SystemSettings/\347\263\273\347\273\237\350\256\276\347\275\256\344\270\212\344\270\213\346\226\207.txt" create mode 100644 backend/src/UniversalAdminSystem.Domian/UniversalAdminSystem.Domian.csproj create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/Aggregates/User.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/Entities/UserInfo.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserInfoRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserAccount.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserEmail.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserGender.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UsersStatus.cs create mode 100644 "backend/src/UniversalAdminSystem.Domian/UserManagement/\347\224\250\346\210\267\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenService.cs create mode 100644 "backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Configs/SystemPermissionConfig.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddDomainService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/BaseRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/FileRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/LogEntryRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/PermissionRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/RoleRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/SystemSettingRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserInfoRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Transaction/UnitOfWork.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/PasswordHelper.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionCacheService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionInitializationService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleConfigService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleFileService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/SystemPermissionConfigLoader.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj diff --git a/backend/UniversalAdminSystem.sln b/backend/UniversalAdminSystem.sln new file mode 100644 index 0000000..c65262f --- /dev/null +++ b/backend/UniversalAdminSystem.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9032CBF4-BB53-4E64-9978-E90FEB71CC7C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalAdminSystem.Api", "src\UniversalAdminSystem.Api\UniversalAdminSystem.Api.csproj", "{2AD39004-1226-419A-946B-72964C31CFF2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalAdminSystem.Application", "src\UniversalAdminSystem.Application\UniversalAdminSystem.Application.csproj", "{1B84D865-ECF7-4F61-A45C-81641308405F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalAdminSystem.Domian", "src\UniversalAdminSystem.Domian\UniversalAdminSystem.Domian.csproj", "{6430BA75-BA96-4EED-9F4E-D7DB30C583A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalAdminSystem.Infrastructure", "src\UniversalAdminSystem.Infrastructure\UniversalAdminSystem.Infrastructure.csproj", "{D497ABDA-6594-4364-9BE7-90AB35E2B194}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2AD39004-1226-419A-946B-72964C31CFF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AD39004-1226-419A-946B-72964C31CFF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AD39004-1226-419A-946B-72964C31CFF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AD39004-1226-419A-946B-72964C31CFF2}.Release|Any CPU.Build.0 = Release|Any CPU + {1B84D865-ECF7-4F61-A45C-81641308405F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B84D865-ECF7-4F61-A45C-81641308405F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B84D865-ECF7-4F61-A45C-81641308405F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B84D865-ECF7-4F61-A45C-81641308405F}.Release|Any CPU.Build.0 = Release|Any CPU + {6430BA75-BA96-4EED-9F4E-D7DB30C583A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6430BA75-BA96-4EED-9F4E-D7DB30C583A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6430BA75-BA96-4EED-9F4E-D7DB30C583A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6430BA75-BA96-4EED-9F4E-D7DB30C583A3}.Release|Any CPU.Build.0 = Release|Any CPU + {D497ABDA-6594-4364-9BE7-90AB35E2B194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D497ABDA-6594-4364-9BE7-90AB35E2B194}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D497ABDA-6594-4364-9BE7-90AB35E2B194}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D497ABDA-6594-4364-9BE7-90AB35E2B194}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2AD39004-1226-419A-946B-72964C31CFF2} = {9032CBF4-BB53-4E64-9978-E90FEB71CC7C} + {1B84D865-ECF7-4F61-A45C-81641308405F} = {9032CBF4-BB53-4E64-9978-E90FEB71CC7C} + {6430BA75-BA96-4EED-9F4E-D7DB30C583A3} = {9032CBF4-BB53-4E64-9978-E90FEB71CC7C} + {D497ABDA-6594-4364-9BE7-90AB35E2B194} = {9032CBF4-BB53-4E64-9978-E90FEB71CC7C} + EndGlobalSection +EndGlobal diff --git a/backend/src/UniversalAdminSystem.Api/Attributes/RequirePermissionAttribute.cs b/backend/src/UniversalAdminSystem.Api/Attributes/RequirePermissionAttribute.cs new file mode 100644 index 0000000..5a56330 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Attributes/RequirePermissionAttribute.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Security.Claims; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; + +namespace UniversalAdminSystem.Api.Attributes; + +/// +/// 权限验证特性 +/// 用于标记需要特定权限的API接口 +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] //指定该特性可用于 类(Controller) 或 方法(Action) 级别。 +public class RequirePermissionAttribute : Attribute, IAsyncAuthorizationFilter +{ + private readonly string _permissionCode; + + public RequirePermissionAttribute(string permissionCode) + { + _permissionCode = permissionCode; + } + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + try + { + + // 获取用户ID + var userId = context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + System.Console.WriteLine($"userId: {userId}" ?? "null"); + if (string.IsNullOrEmpty(userId)) + { + Console.WriteLine($"userId: {context.HttpContext.User.Identity?.Name}"); + context.Result = new UnauthorizedResult(); + return; + } + + // 获取权限检查服务 + var service = context.HttpContext.RequestServices.GetService(); + if (service == null) + { + context.Result = new StatusCodeResult(500); + return; + } + + // 检查用户是否有指定权限 + var hasPermission = await service.CheckUserPermissionAsync(Guid.Parse(userId), _permissionCode); + System.Console.WriteLine($"hasPermission: {hasPermission}"); + if (!hasPermission) + { + context.Result = new StatusCodeResult(401); + return; + } + } + catch (Exception ex) + { + // 记录错误并返回500 + Console.WriteLine($"权限验证错误: {ex.Message}"); + context.Result = new StatusCodeResult(500); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Attributes/RequireRoleAttribute.cs b/backend/src/UniversalAdminSystem.Api/Attributes/RequireRoleAttribute.cs new file mode 100644 index 0000000..66a24bd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Attributes/RequireRoleAttribute.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Security.Claims; + +namespace UniversalAdminSystem.Api.Attributes; + +/// +/// 角色验证特性 +/// 用于标记需要特定角色的API接口 +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public class RequireRoleAttribute : Attribute, IAsyncAuthorizationFilter +{ + private readonly string _roleName; + + public RequireRoleAttribute(string roleName) + { + _roleName = roleName; + } + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + try + { + // 获取用户角色 + var userRole = context.HttpContext.User.FindFirst(ClaimTypes.Role)?.Value; + if (string.IsNullOrEmpty(userRole)) + { + context.Result = new UnauthorizedResult(); + return; + } + + // 检查用户角色是否匹配 + if (userRole != _roleName) + { + context.Result = new ForbidResult(); + return; + } + } + catch (Exception ex) + { + Console.WriteLine($"角色验证错误: {ex.Message}"); + context.Result = new StatusCodeResult(500); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/AuthenticationController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/AuthenticationController.cs new file mode 100644 index 0000000..dd1e8cd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/AuthenticationController.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.Authentication.DTOs; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Api.Controllers; + +[ApiController] +[Route("api/auth")] +public class AuthenticationController : ControllerBase +{ + private readonly ILoginAppService _loginAppService; + private readonly IRegisterAppService _registerAppService; + private readonly IJwtTokenService _jwtTokenService; + + public AuthenticationController( + ILoginAppService loginAppService, + IRegisterAppService registerAppService, + IJwtTokenService jwtTokenService) + { + _loginAppService = loginAppService; + _registerAppService = registerAppService; + _jwtTokenService = jwtTokenService; + } + + [HttpPost("login")] + public async Task Login([FromBody] LoginDto loginDto) + { + try + { + if (string.IsNullOrEmpty(loginDto.Account) || string.IsNullOrEmpty(loginDto.Password)) + { + return BadRequest(Result.Failure("账号和密码不能为空")); + } + + var result = await _loginAppService.LoginAsync(loginDto); + return Ok(Result.Success(result)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + [HttpPost("register")] + public async Task Register([FromBody] RegisterDto registerDto) + { + try + { + if (string.IsNullOrEmpty(registerDto.Account) || + string.IsNullOrEmpty(registerDto.Password) || + string.IsNullOrEmpty(registerDto.Email)) + { + return BadRequest(Result.Failure("账号、密码和邮箱不能为空")); + } + + var result = await _registerAppService.RegisterAsync(registerDto); + return Ok(Result.Success(result)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + [HttpPost("refresh-token")] + public IActionResult RefreshToken([FromBody] RefreshTokenDto refreshTokenDto) + { + try + { + if (string.IsNullOrEmpty(refreshTokenDto.Token)) + { + return BadRequest(Result.Failure("Token不能为空")); + } + + var newToken = _jwtTokenService.RefreshToken( + refreshTokenDto.Token, + out string userId, + out string roleId, + out UserStatus status); + + var result = new RefreshTokenResultDto(newToken, userId, roleId); + return Ok(Result.Success(result)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + [HttpPost("logout")] + public IActionResult Logout() + { + return Ok(Result.Success("登出成功")); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs new file mode 100644 index 0000000..98f6832 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs @@ -0,0 +1,142 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.FileStorage.DTOs; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Infrastructure.FileStorage; +using UniversalAdminSystem.Application.Common.Interfaces; + +namespace UniversalAdminSystem.Api.Controllers; + +[ApiController] +[Route("api/files")] +public class FileController : ControllerBase +{ + private readonly IFileAppService _fileAppService; + private readonly IFileStorageService _fileStorageService; + private readonly IUnitOfWork _unitOfWork; + + public FileController(IFileAppService fileAppService, IFileStorageService fileStorageService, IUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + _fileAppService = fileAppService; + _fileStorageService = fileStorageService; + } + + [HttpPost("upload")] + [RequirePermission("file:Create")] // 临时注释掉权限检查 + public async Task Upload(IFormFile file, Guid? parentId = null) + { + if (file == null || file.Length == 0) + { + return BadRequest(Result.Failure("请选择要上传的文件")); + } + + try + { + await _unitOfWork.BeginTransactionAsync(); + var filePath = await _fileStorageService.UploadAsync(file, file.OpenReadStream()); + var fileUploadDto = new FileUploadDto( + file.FileName, + filePath, + file.Length, + file.ContentType, + parentId.HasValue ? (FileId)parentId.Value : Guid.Empty); + + var res = await _fileAppService.UploadAsync(fileUploadDto); + await _unitOfWork.CommitAsync(); + return Ok(Result.Success(res)); + + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + return BadRequest(Result.Failure(ex.Message)); + } + } + + [HttpGet("test-permission")] + [RequirePermission("file:Create")] + public IActionResult TestPermission() + { + return Ok(Result.Success("权限检查通过")); + } + + // [HttpGet("download/{fileId}")] + // [RequirePermission("file:download")] + // public async Task Download(Guid fileId) + // { + // var result = await _fileAppService.DownloadAsync((FileId)fileId); + // return result.IsSuccess ? Ok(result) : BadRequest(result); + // } + + [HttpGet("list")] + [RequirePermission("file:Read")] + public async Task List() + { + var result = await _fileAppService.GetList(); + return Ok(result); + } + + // [HttpPost("folder")] + // [RequirePermission("file:create")] + // public async Task CreateFolder([FromBody] string name, [FromQuery] Guid? parentId) + // { + // var result = await _fileAppService.CreateFolderAsync(name, parentId.HasValue ? (FileId)parentId.Value : null); + // return result.IsSuccess ? Ok(result) : BadRequest(result); + // } + + // [HttpDelete("{fileId}")] + // [RequirePermission("file:delete")] + // public async Task Delete(Guid fileId) + // { + // var result = await _fileAppService.DeleteAsync((FileId)fileId); + // return result.IsSuccess ? Ok(result) : BadRequest(result); + // } + + // [HttpGet("{fileId}")] + // [RequirePermission("file:read")] + // public async Task GetFileById(Guid fileId) + // { + // var result = await _fileAppService.GetFileByIdAsync((FileId)fileId); + // return result.IsSuccess ? Ok(result) : BadRequest(result); + // } + + [HttpGet("{id}")] + [RequirePermission("file:Read")] + public async Task Download(Guid id) + { + try + { + + var entity = await _fileAppService.GetFileById(id); + var file = await _fileStorageService.DownloadAsync(entity.Name); + return new FileStreamResult(file, entity.Type); + } + catch (System.Exception) + { + return BadRequest(Result.Failure("")); + } + } + + [HttpDelete("{id}")] + [RequirePermission("file:Delete")] + public async Task RemoveFile(Guid id) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + var entity = await _fileAppService.GetFileById(id); + await _fileStorageService.DeleteAsync(entity.Name); + await _fileAppService.RemoveFile(entity.Id); + await _unitOfWork.CommitAsync(); + return Ok(Result.Success()); + } + catch (System.Exception) + { + await _unitOfWork.RollbackAsync(); + return BadRequest(Result.Failure("操作错误")); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/K2ModelController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/K2ModelController.cs new file mode 100644 index 0000000..0138549 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/K2ModelController.cs @@ -0,0 +1,93 @@ +// using Microsoft.AspNetCore.Mvc; +// using UniversalAdminSystem.Infrastructure.Services; + +// namespace UniversalAdminSystem.Api.Controllers; + +// [ApiController] +// [Route("api/[controller]")] +// public class K2ModelController : ControllerBase +// { +// private readonly K2ModelService _k2ModelService; +// private readonly ILogger _logger; + +// public K2ModelController(K2ModelService k2ModelService, ILogger logger) +// { +// _k2ModelService = k2ModelService; +// _logger = logger; +// } + +// /// +// /// 发送简单文本请求到K2模型 +// /// +// /// 请求模型 +// /// 模型响应 +// [HttpPost("chat")] +// public async Task SendChatRequest([FromBody] K2ChatRequest request) +// { +// try +// {2 +// _logger.LogInformation("收到K2模型聊天请求: {Prompt}", request.Prompt); + +// var response = await _k2ModelService.SendSimpleRequestAsync( +// request.Prompt, +// request.Model ?? "qwen-turbo"); + +// return Ok(new { response = response }); +// } +// catch (Exception ex) +// { +// _logger.LogError(ex, "K2模型请求失败"); +// return StatusCode(500, new { error = "K2模型请求失败", message = ex.Message }); +// } +// } + +// /// +// /// 发送多轮对话请求到K2模型 +// /// +// /// 多轮对话请求 +// /// 模型响应 +// [HttpPost("conversation")] +// public async Task SendConversationRequest([FromBody] K2ConversationRequest request) +// { +// try +// { +// _logger.LogInformation("收到K2模型多轮对话请求,消息数量: {MessageCount}", request.Messages.Count); + +// var response = await _k2ModelService.SendChatRequestAsync( +// request.Messages, +// request.Model ?? "qwen-turbo", +// request.Temperature ?? 0.7f, +// request.MaxTokens ?? 1000); + +// return Ok(response); +// } +// catch (Exception ex) +// { +// _logger.LogError(ex, "K2模型多轮对话请求失败"); +// return StatusCode(500, new { error = "K2模型请求失败", message = ex.Message }); +// } +// } + +// /// +// /// 获取K2模型配置信息 +// /// +// /// 配置信息 +// [HttpGet("config")] +// public IActionResult GetConfig([FromServices] K2ConfigService configService) +// { +// try +// { +// var config = configService.GetK2Config(); +// return Ok(new +// { +// baseUrl = config.BaseUrl, +// hasApiKey = !string.IsNullOrEmpty(config.ApiKey) +// }); +// } +// catch (Exception ex) +// { +// _logger.LogError(ex, "获取K2配置失败"); +// return StatusCode(500, new { error = "获取配置失败", message = ex.Message }); +// } +// } +// } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/LogController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/LogController.cs new file mode 100644 index 0000000..62ca15e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/LogController.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.LogManagement.DTOs; +using UniversalAdminSystem.Application.LogManagement.Interfaces; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Api.Controllers; + +[ApiController] +[Route("api/logs")] +public class LogController : ControllerBase +{ + private readonly ILogManagementAppService _logService; + public LogController(ILogManagementAppService logService) => _logService = logService; + + [HttpGet] + //[RequirePermission("log:read")] + public async Task GetAllLogsAsync() + { + var result = await _logService.GetAllLogsAsync(); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("{id}")] + //[RequirePermission("log:read")] + public async Task GetLogByIdAsync(Guid id) + { + var result = await _logService.GetLogByIdAsync(id); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpPost] + //[RequirePermission("log:create")] + public async Task CreateLogAsync([FromBody] LogCreateDto dto) + { + var result = await _logService.CreateLogAsync(dto); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("level/{level}")] + //[RequirePermission("log:read")] + public async Task GetLogsByLevelAsync(string level) + { + var result = await _logService.GetLogsByLevelAsync(level); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("user/{userId}")] + //[RequirePermission("log:read")] + public async Task GetLogsByUserAsync(Guid userId) + { + var result = await _logService.GetLogsByUserAsync(userId); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("date")] + //[RequirePermission("log:read")] + public async Task GetLogsByDateRangeAsync([FromQuery] DateTime start, [FromQuery] DateTime end) + { + var result = await _logService.GetLogsByDateRangeAsync(start, end); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("source/{source}")] + //[RequirePermission("log:read")] + public async Task GetLogsBySourceAsync(string source) + { + var result = await _logService.GetLogsBySourceAsync(source); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/PermissionController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/PermissionController.cs new file mode 100644 index 0000000..d4bea70 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/PermissionController.cs @@ -0,0 +1,163 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.PermissionManagement.DTOs; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Infrastructure.Services; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Api.Controllers; + +/// +/// 权限管理控制器 +/// +[ApiController] +[Route("api/permissions")] +public class PermissionController : ControllerBase +{ + private readonly IPermissionManagementAppService _permissionAppService; + private readonly PermissionRuleConfigService _permissionRuleConfigService; + + public PermissionController(IPermissionManagementAppService permissionAppService, PermissionRuleConfigService permissionRuleConfigService) + { + _permissionAppService = permissionAppService; + _permissionRuleConfigService = permissionRuleConfigService; + } + + /// + /// 获取所有权限列表 + /// + [HttpGet] + [RequirePermission("permission:Read")] + public async Task GetAllPermissionsAsync() + { + try + { + var permissions = await _permissionAppService.GetAllPermissionAsync(); + return Ok(Result>.Success(permissions)); + } + catch (Exception ex) + { + return BadRequest(Result>.Failure(ex.Message)); + } + } + + /// + /// 创建新权限 + /// + [HttpPost("create")] + [RequirePermission("permission:Create")] + public async Task CreatePermissionAsync([FromBody] PermissionCreateDto createDto) + { + try + { + var permission = await _permissionAppService.CreatePermissionAsync(createDto); + return Ok(Result.Success(permission)); + } + catch (InvalidOperationException ex) + { + // 检查是否是重复权限错误 + if (ex.Message.Contains("已存在")) + { + return Conflict(Result.Failure(ex.Message)); + } + return BadRequest(Result.Failure(ex.Message)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure($"创建权限失败: {ex.Message}")); + } + } + + /// + /// 删除权限 + /// + [HttpDelete("{permissionId}")] + [RequirePermission("permission:Delete")] + public async Task DeletePermissionAsync(Guid permissionId) + { + try + { + await _permissionAppService.RemovePermissionAsync(permissionId); + return Ok(Result.Success("权限删除成功")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + + // 已废弃(请使用Role Controller的分配api) + // /// + // /// 为角色分配权限 + // /// + // [HttpPost("assign")] + // [RequirePermission("permission:Update")] + // public async Task AssignPermissionToRoleAsync([FromBody] AssignPermissionDto assignDto) + // { + // try + // { + // await _permissionAppService.AssignPermissionToRoleAsync(assignDto); + // return Ok(Result.Success("权限分配成功")); + // } + // catch (Exception ex) + // { + // return BadRequest(Result.Failure(ex.Message)); + // } + // } + + /// + /// 获取权限规则配置 + /// + [HttpGet("rules")] + [RequirePermission("permission:Read")] + public IActionResult GetPermissionRulesAsync() + { + try + { + var rules = _permissionRuleConfigService.GetCurrentConfig(); + return Ok(Result>>.Success(rules)); + } + catch (Exception ex) + { + return BadRequest(Result>>.Failure(ex.Message)); + } + } + + /// + /// 更新权限规则配置 + /// + [HttpPut("rules")] + [RequirePermission("permission:Update")] + public IActionResult UpdatePermissionRulesAsync([FromBody] Dictionary> newRules) + { + try + { + // TODO: 这里应该校验当前用户是否为超级管理员 + _permissionRuleConfigService.UpdateConfig(newRules); + return Ok(Result.Success("权限规则更新成功")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 刷新权限规则配置 + /// + [HttpPost("rules/refresh")] + [RequirePermission("permission:Update")] + public IActionResult RefreshPermissionRulesAsync() + { + try + { + _permissionRuleConfigService.RefreshConfig(); + return Ok(Result.Success("权限规则刷新成功")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/RoleController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/RoleController.cs new file mode 100644 index 0000000..b975d81 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/RoleController.cs @@ -0,0 +1,225 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.PermissionManagement.DTOs; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Api.Controllers; + +/// +/// 角色管理控制器 +/// 提供角色的创建、查询、权限分配等操作 +/// +[ApiController] +[Route("api/roles")] +public class RoleController : ControllerBase +{ + private readonly IRoleManagementAppService _roleManagementAppService; + private readonly IPermissionManagementAppService _permissionAppService; + + public RoleController( + IRoleManagementAppService roleManagementAppService, + IPermissionManagementAppService permissionAppService) + { + _roleManagementAppService = roleManagementAppService; + _permissionAppService = permissionAppService; + } + + /// + /// 获取所有角色列表 + /// + [HttpGet] + [RequirePermission("role:Read")] + public async Task GetAllRolesAsync() + { + try + { + var roles = await _roleManagementAppService.GetAllRolesAsync(); + return Ok(Result>.Success(roles)); + } + catch (Exception ex) + { + return BadRequest(Result>.Failure(ex.Message)); + } + } + + /// + /// 根据ID获取角色 + /// + [HttpGet("{roleId}")] + [RequirePermission("role:Read")] + public async Task GetRoleByIdAsync(Guid roleId) + { + try + { + var role = await _roleManagementAppService.GetRoleByIdAsync(roleId); + if (role == null) + { + return NotFound(Result.Failure("角色不存在")); + } + return Ok(Result.Success(role)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 创建新角色 + /// + [HttpPost] + [RequirePermission("role:Create")] + public async Task CreateRoleAsync([FromBody] RoleCreateDto createDto) + { + try + { + var role = await _roleManagementAppService.CreateRoleAsync(createDto); + return Ok(Result.Success(role)); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 更新角色信息 + /// + [HttpPut("{roleId}")] + [RequirePermission("role:Update")] + public async Task UpdateRoleAsync(Guid roleId, [FromBody] RoleUpdateDto updateDto) + { + try + { + var role = await _roleManagementAppService.UpdateRoleAsync(roleId, updateDto); + return Ok(Result.Success(role)); + } + catch (KeyNotFoundException) + { + return NotFound(Result.Failure("角色不存在")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 删除角色 + /// + [HttpDelete("{roleId}")] + [RequirePermission("role:Delete")] + public async Task DeleteRoleAsync(Guid roleId) + { + try + { + await _roleManagementAppService.DeleteRoleAsync(roleId); + return Ok(Result.Success("角色删除成功")); + } + catch (KeyNotFoundException) + { + return NotFound(Result.Failure("角色不存在")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 为角色分配权限 + /// + [HttpPost("{roleId}/permissions")] + [RequirePermission("role:Update")] + public async Task AssignPermissionsToRoleAsync( + Guid roleId, + [FromBody] List permissionCodes) + { + try + { + // 将权限编码转换为权限ID(这里需要根据实际需求调整) + + foreach (var permissionCode in permissionCodes) + { + var assignDto = new AssignPermissionDto(permissionCode, roleId); + await _permissionAppService.AssignPermissionToRoleAsync(assignDto); + } + return Ok(Result.Success("角色权限分配成功")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 为角色分配权限(使用权限ID) + /// + [HttpPost("{roleId}/permissions/ids")] + [RequirePermission("role:Update")] + public async Task AssignPermissionsToRoleByIdsAsync( + Guid roleId, + [FromBody] List permissionIds) + { + try + { + await _roleManagementAppService.AssignPermissionsToRoleAsync(roleId, permissionIds); + return Ok(Result.Success("角色权限分配成功")); + } + catch (KeyNotFoundException) + { + return NotFound(Result.Failure("角色不存在")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 移除角色的权限 + /// + [HttpDelete("{roleId}/permissions")] + [RequirePermission("role:Update")] + public async Task RemovePermissionsFromRoleAsync( + Guid roleId, + [FromBody] List permissionIds) + { + try + { + await _roleManagementAppService.RemovePermissionsFromRoleAsync(roleId, permissionIds); + return Ok(Result.Success("角色权限移除成功")); + } + catch (KeyNotFoundException) + { + return NotFound(Result.Failure("角色不存在")); + } + catch (Exception ex) + { + return BadRequest(Result.Failure(ex.Message)); + } + } + + /// + /// 获取角色的所有权限 + /// + [HttpGet("{roleId}/permissions")] + [RequirePermission("role:Read")] + public async Task GetRolePermissionsAsync(Guid roleId) + { + try + { + var permissions = await _roleManagementAppService.GetRolePermissionsAsync(roleId); + return Ok(Result>.Success(permissions)); + } + catch (KeyNotFoundException) + { + return NotFound(Result>.Failure("角色不存在")); + } + catch (Exception ex) + { + return BadRequest(Result>.Failure(ex.Message)); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/SystemSettingsController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/SystemSettingsController.cs new file mode 100644 index 0000000..57b42c9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/SystemSettingsController.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.SystemSettings.DTOs; +using UniversalAdminSystem.Application.SystemSettings.Interfaces; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Api.Controllers; + +[ApiController] +[Route("api/system-settings")] +public class SystemSettingsController : ControllerBase +{ + private readonly ISystemSettingAppService _settingService; + public SystemSettingsController(ISystemSettingAppService settingService) => _settingService = settingService; + + [HttpGet] + //[RequirePermission("systemsetting:read")] + public async Task GetAllSettingsAsync() + { + var result = await _settingService.GetAllSettingsAsync(); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("key/{key}")] + //[RequirePermission("systemsetting:read")] + public async Task GetSettingByKeyAsync(string key) + { + var result = await _settingService.GetSettingByKeyAsync(key); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpGet("group/{group}")] + //[RequirePermission("systemsetting:read")] + public async Task GetSettingsByGroupAsync(string group) + { + var result = await _settingService.GetSettingsByGroupAsync(group); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpPost] + //[RequirePermission("systemsetting:create")] + public async Task CreateSettingAsync([FromBody] SystemSettingCreateDto dto) + { + var result = await _settingService.CreateSettingAsync(dto); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpPut("{id}")] + //[RequirePermission("systemsetting:update")] + public async Task UpdateSetting(Guid id, [FromBody] SystemSettingUpdateDto updateDto) + { + var result = await _settingService.UpdateSettingAsync(id, updateDto); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } + + [HttpDelete("{id}")] + //[RequirePermission("systemsetting:delete")] + public async Task DeleteSettingAsync(Guid id) + { + var result = await _settingService.DeleteSettingAsync(id); + return result.IsSuccess ? Ok(result) : BadRequest(result); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/UserManagementController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/UserManagementController.cs new file mode 100644 index 0000000..b4b75f7 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/UserManagementController.cs @@ -0,0 +1,176 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Application.UserManagement.Dtos; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Api.Attributes; + +namespace UniversalAdminSystem.Api.Controllers; + +/// +/// 用户管理控制器 +/// +[ApiController] +[Route("api/user")] +public class UserManagementController : ControllerBase +{ + private readonly IUserManagementAppService _userRepoService; + + public UserManagementController(IUserManagementAppService userService) + { + _userRepoService = userService; + } + + [HttpGet] + [RequirePermission("user:Read")] + public async Task GetUsersAsync() + { + var result = await _userRepoService.GetUsersAsync(); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + [HttpPost("create")] + [RequirePermission("user:Create")] + public async Task CreateUserAsync([FromBody] UserCreateDto userCreate) + { + var result = await _userRepoService.CreateUserAsync(userCreate); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + [HttpDelete("{id}")] + [RequirePermission("user:Delete")] + public async Task DeleteUserAsync(Guid id) + { + var result = await _userRepoService.DeleteUserAsync(id); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + [HttpGet("{id}")] + [RequirePermission("user:Read")] + public async Task GetUserByIdAsync(Guid id) + { + var result = await _userRepoService.GetUserByIdAsync(id); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + [HttpPut("{id}")] + [RequirePermission("user:Update")] + public async Task UpdateUserAsync(Guid id, [FromBody] UserUpdateDto updateDto) + { + var result = await _userRepoService.UpdateUserAsync(id, updateDto); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + // 已废弃(批量获取角色) + // [HttpPost("{id}/role")] + // [RequirePermission("user:Update")] + // public async Task AssignRoleAsync(Guid id, [FromBody] List roleIds) + // { + // var result = await _userRepoService.AssignRoleAsync(id, roleIds); + + // if (result.IsSuccess) + // { + // return Ok(result); + // } + // else + // { + // return BadRequest(result); + // } + // } + + /// + /// 移除用户角色 + /// + [HttpDelete("{id}/roles/{roleId}")] + [RequirePermission("user:Update")] + public async Task RemoveRoleAsync(Guid id, Guid roleId) + { + var result = await _userRepoService.RemoveRoleAsync(id, roleId); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + /// + /// 获取用户的所有权限 + /// + [HttpGet("{id}/permissions")] + [RequirePermission("user:Read")] + public async Task GetUserPermissionsAsync(Guid id) + { + var result = await _userRepoService.GetUserPermissionsAsync(id); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } + + /// + /// 检查用户是否有指定权限 + /// + [HttpPost("{id}/permissions/check")] + [RequirePermission("user:Read")] + public async Task CheckUserPermissionAsync(Guid id, [FromBody] string permissionCode) + { + var result = await _userRepoService.CheckUserPermissionAsync(id, permissionCode); + + if (result.IsSuccess) + { + return Ok(result); + } + else + { + return BadRequest(result); + } + } +} diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/UserRoleController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/UserRoleController.cs new file mode 100644 index 0000000..935fd99 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/UserRoleController.cs @@ -0,0 +1,122 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Api.Attributes; + +namespace UniversalAdminSystem.Api.Controllers; + +/// +/// 用户角色管理控制器 +/// 提供用户角色分配和管理功能 +/// +[ApiController] +[Route("api/users")] +public class UserRoleController : ControllerBase +{ + private readonly IPermissionCheckService _permissionCheckService; + + public UserRoleController(IPermissionCheckService permissionCheckService) + { + _permissionCheckService = permissionCheckService; + } + + /// + /// 获取用户的所有权限 + /// + [HttpGet("{userId}/permissions")] + [RequirePermission("user:Read")] + public async Task GetUserPermissionsAsync(Guid userId) + { + try + { + var permissions = await _permissionCheckService.GetUserPermissionsAsync(userId); + var result = Result>.Success(permissions); + return Ok(result); + } + catch (Exception ex) + { + var result = Result>.Failure($"获取用户权限失败: {ex.Message}"); + return BadRequest(result); + } + } + + /// + /// 获取用户的所有角色 + /// + [HttpGet("{userId}/roles")] + [RequirePermission("user:Read")] + public async Task GetUserRolesAsync(Guid userId) + { + try + { + var roles = await _permissionCheckService.GetUserRoleAsync(userId); + var result = Result>.Success(roles); + return Ok(result); + } + catch (Exception ex) + { + var result = Result>.Failure($"获取用户角色失败: {ex.Message}"); + return BadRequest(result); + } + } + + /// + /// 检查用户是否有指定权限 + /// + [HttpPost("{userId}/permissions/check")] + [RequirePermission("user:Read")] + public async Task CheckUserPermissionAsync(Guid userId, [FromBody] string permissionCode) + { + try + { + var hasPermission = await _permissionCheckService.CheckUserPermissionAsync(userId, permissionCode); + var result = Result.Success(hasPermission); + return Ok(result); + } + catch (Exception ex) + { + var result = Result.Failure($"检查用户权限失败: {ex.Message}"); + return BadRequest(result); + } + } + + /// + /// 检查用户是否有任意一个指定权限 + /// + [HttpPost("{userId}/permissions/check-any")] + [RequirePermission("user:Read")] + public async Task CheckUserAnyPermissionAsync(Guid userId, [FromBody] List permissionCodes) + { + try + { + var hasPermission = await _permissionCheckService.CheckUserAnyPermissionAsync(userId, permissionCodes); + var result = Result.Success(hasPermission); + return Ok(result); + } + catch (Exception ex) + { + var result = Result.Failure($"检查用户权限失败: {ex.Message}"); + return BadRequest(result); + } + } + + /// + /// 检查用户是否有所有指定权限 + /// + [HttpPost("{userId}/permissions/check-all")] + [RequirePermission("user:Read")] + public async Task CheckUserAllPermissionsAsync(Guid userId, [FromBody] List permissionCodes) + { + try + { + var hasPermission = await _permissionCheckService.CheckUserAllPermissionsAsync(userId, permissionCodes); + var result = Result.Success(hasPermission); + return Ok(result); + } + catch (Exception ex) + { + var result = Result.Failure($"检查用户权限失败: {ex.Message}"); + return BadRequest(result); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/K2ModelTests.http b/backend/src/UniversalAdminSystem.Api/K2ModelTests.http new file mode 100644 index 0000000..26e23ce --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/K2ModelTests.http @@ -0,0 +1,81 @@ +@baseUrl = https://localhost:7001 + +### 获取K2模型配置 +GET {{baseUrl}}/api/K2Model/config +Content-Type: application/json + +### + +### 发送简单聊天请求 +POST {{baseUrl}}/api/K2Model/chat +Content-Type: application/json + +{ + "prompt": "你好,请介绍一下你自己", + "model": "qwen-turbo" +} + +### + +### 发送多轮对话请求 +POST {{baseUrl}}/api/K2Model/conversation +Content-Type: application/json + +{ + "messages": [ + { + "role": "system", + "content": "你是一个有用的AI助手" + }, + { + "role": "user", + "content": "请帮我写一个简单的C#函数来计算两个数的和" + } + ], + "model": "qwen-turbo", + "temperature": 0.7, + "maxTokens": 1000 +} + +### + +### 测试复杂对话 +POST {{baseUrl}}/api/K2Model/conversation +Content-Type: application/json + +{ + "messages": [ + { + "role": "system", + "content": "你是一个专业的软件工程师,擅长C#和.NET开发" + }, + { + "role": "user", + "content": "请解释一下依赖注入模式在ASP.NET Core中的应用" + } + ], + "model": "qwen-turbo", + "temperature": 0.5, + "maxTokens": 1500 +} + +### +POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions +Content-Type: application/json +Authorization: Bearer sk-a31163f6b59e44dcbdb87c668482ce96 + +{ + "model": "Moonshot-Kimi-K2-Instruct", + "messages": [ + { + "role": "user", + "content": "你好,请介绍一下你自己" + } + ], + "temperature": 0.7, + "max_tokens": 1000, + "stream": true, + "stream_options": { + "include_usage": true + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/K2Model_README.md b/backend/src/UniversalAdminSystem.Api/K2Model_README.md new file mode 100644 index 0000000..f3d93bc --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/K2Model_README.md @@ -0,0 +1,133 @@ +# K2模型集成指南 + +## 概述 + +本项目已集成K2模型(基于阿里云DashScope的兼容模式),提供了完整的AI对话功能。 + +## 配置说明 + +### 1. 配置文件设置 + +在 `appsettings.json` 中配置K2模型参数: + +```json +{ + "K2": { + "BaseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1", + "ApiKey": "your-api-key-here" + } +} +``` + +### 2. 配置参数说明 + +- **BaseUrl**: K2模型的API基础URL +- **ApiKey**: 阿里云DashScope的API密钥 + +## API接口 + +### 1. 获取配置信息 + +``` +GET /api/K2Model/config +``` + +返回配置信息(不包含敏感数据)。 + +### 2. 简单聊天请求 + +``` +POST /api/K2Model/chat +``` + +请求体: +```json +{ + "prompt": "你好,请介绍一下你自己", + "model": "qwen-turbo" +} +``` + +### 3. 多轮对话请求 + +``` +POST /api/K2Model/conversation +``` + +请求体: +```json +{ + "messages": [ + { + "role": "system", + "content": "你是一个有用的AI助手" + }, + { + "role": "user", + "content": "请帮我写一个简单的C#函数" + } + ], + "model": "qwen-turbo", + "temperature": 0.7, + "maxTokens": 1000 +} +``` + +## 使用示例 + +### 1. 简单对话 + +```csharp +// 在控制器中注入服务 +private readonly K2ModelService _k2ModelService; + +// 发送简单请求 +var response = await _k2ModelService.SendSimpleRequestAsync("你好"); +``` + +### 2. 多轮对话 + +```csharp +var messages = new List +{ + new K2Message { Role = "system", Content = "你是一个专业的程序员" }, + new K2Message { Role = "user", Content = "请解释一下依赖注入" } +}; + +var response = await _k2ModelService.SendChatRequestAsync(messages); +``` + +## 测试方法 + +1. 启动应用程序 +2. 使用提供的 `K2ModelTests.http` 文件进行API测试 +3. 在Swagger UI中查看和测试API接口 + +## 注意事项 + +1. **API密钥安全**: 确保API密钥的安全性,不要在客户端代码中暴露 +2. **错误处理**: 服务已包含完整的错误处理和日志记录 +3. **性能优化**: 使用HttpClient工厂模式,支持连接池和重试机制 +4. **配置验证**: 启动时会验证配置的有效性 + +## 支持的模型 + +- `qwen-turbo`: 通义千问Turbo模型 +- `qwen-plus`: 通义千问Plus模型 +- `qwen-max`: 通义千问Max模型 + +## 故障排除 + +1. **401错误**: 检查API密钥是否正确 +2. **404错误**: 检查BaseUrl配置是否正确 +3. **网络错误**: 检查网络连接和防火墙设置 + +## 扩展功能 + +可以根据需要扩展以下功能: + +1. 流式响应支持 +2. 模型参数缓存 +3. 请求限流 +4. 响应内容过滤 +5. 多模型支持 \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Middleware/JwtAuthenticationMiddleware.cs b/backend/src/UniversalAdminSystem.Api/Middleware/JwtAuthenticationMiddleware.cs new file mode 100644 index 0000000..c528bfa --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Middleware/JwtAuthenticationMiddleware.cs @@ -0,0 +1,126 @@ +using System.Security.Claims; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Api.Middleware; + +public class JwtAuthenticationMiddleware +{ + private readonly RequestDelegate _next; + private readonly IJwtTokenService _jwtService; + private readonly ILogger _logger; + + public JwtAuthenticationMiddleware( + RequestDelegate next, + IJwtTokenService jwtService, + ILogger logger) + { + _next = next; + _jwtService = jwtService; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + var token = ExtractTokenFromHeader(context); + Console.WriteLine($"提取到的Token: {(token != null ? "存在" : "不存在")}"); + if (!string.IsNullOrEmpty(token)) + { + Console.WriteLine($"Token内容: {token.Substring(0, Math.Min(50, token.Length))}..."); + var userInfo = await AuthenticateTokenAsync(token); + if (userInfo.HasValue) + { + Console.WriteLine($"Token解析结果 - 用户ID: {userInfo.Value.UserId}, 角色ID: {userInfo.Value.RoleId}, 状态: {userInfo.Value.status}"); + SetUserClaims(context, userInfo.Value); + _logger.LogInformation("用户 {UserId} 认证成功", userInfo.Value.UserId); + } + else + { + Console.WriteLine("Token解析失败"); + } + } + else + { + Console.WriteLine("Token不存在"); + } + } + catch (Exception ex) + { + _logger.LogWarning("JWT认证中间件异常: {Message}", ex.Message); + } + + await _next(context); + } + + private string? ExtractTokenFromHeader(HttpContext context) + { + var authHeader = context.Request.Headers["Authorization"].FirstOrDefault(); + Console.WriteLine($"Authorization头: {authHeader ?? "null"}"); + + if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine("Authorization头为空或不以Bearer开头"); + return null; + } + + var token = authHeader.Substring("Bearer ".Length).Trim(); + Console.WriteLine($"提取的Token长度: {token.Length}"); + Console.WriteLine($"Token前50个字符: {token.Substring(0, Math.Min(50, token.Length))}"); + + // 检查token格式 + if (string.IsNullOrEmpty(token)) + { + Console.WriteLine("Token为空"); + return null; + } + + // JWT应该包含两个点,格式为: header.payload.signature + var parts = token.Split('.'); + if (parts.Length != 3) + { + Console.WriteLine($"Token格式错误,包含{parts.Length}个部分,应该是3个部分"); + return null; + } + + Console.WriteLine("Token格式检查通过"); + return token; + } + + private async Task<(string UserId, string RoleId, UserStatus status)?> AuthenticateTokenAsync(string token) + { + try + { + var (userId, roleId, status) = _jwtService.ParseToken(token); + Console.WriteLine($"Token解析结果 - 用户ID: {userId}, 角色ID: {roleId}, 状态: {status}"); + return (userId, roleId, status); + } + catch (Exception ex) + { + _logger.LogWarning("Token解析失败: {Message}", ex.Message); + return null; + } + } + + private void SetUserClaims(HttpContext context, (string UserId, string RoleId, UserStatus status) userInfo) + { + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, userInfo.UserId), + new Claim(ClaimTypes.Role, userInfo.RoleId), + new Claim(ClaimTypes.StateOrProvince, userInfo.status.ToString()), + }; + + var identity = new ClaimsIdentity(claims, "jwt"); + context.User = new ClaimsPrincipal(identity); + } +} + +public static class JwtAuthenticationMiddlewareExtensions +{ + public static IApplicationBuilder UseJwtAuthentication(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/PermissionRules.json b/backend/src/UniversalAdminSystem.Api/PermissionRules.json new file mode 100644 index 0000000..234271e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/PermissionRules.json @@ -0,0 +1,10 @@ +{ + "Read": ["data", "file", "user", "role", "permission", "config", "system", "report"], + "Create": ["data", "user", "file", "role", "permission"], + "Update": ["data", "user", "config", "role", "permission"], + "Delete": ["data", "user", "file", "role", "permission"], + "Manage": ["system", "user"], + "Execute": ["job", "script"], + "Import": ["data", "file"], + "Export": ["data", "file", "report"] +} diff --git a/backend/src/UniversalAdminSystem.Api/Program.cs b/backend/src/UniversalAdminSystem.Api/Program.cs new file mode 100644 index 0000000..d580a5b --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Program.cs @@ -0,0 +1,98 @@ +using UniversalAdminSystem.Infrastructure.DependencyInject; +using UniversalAdminSystem.Infrastructure.Services; +using UniversalAdminSystem.Api.Middleware; +using Microsoft.OpenApi.Models; +using UniversalAdminSystem.Infrastructure.Configs; + +var builder = WebApplication.CreateBuilder(args); + +// 添加CORS服务 +builder.Services.AddCors(options => +{ + // 开发环境策略 - 允许所有来源 + options.AddPolicy("AllowAll", policy => + { + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); + + // 生产环境策略 - 指定允许的来源 + options.AddPolicy("AllowSpecific", policy => + { + policy.WithOrigins( + "http://localhost:5173", + "http://localhost:3000", // React开发服务器 + "http://localhost:8080", // Vue开发服务器 + "http://localhost:4200", // Angular开发服务器 + "https://mikuslittlenest.cn", // 生产域名 + "http://www.mikuslittlenest.cn" + ) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + }); +}); + +builder.Services.Configure(builder.Configuration.GetSection("K2")); + +// Add services to the container. +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Universal Admin System API", Version = "v1" }); + + // 添加JWT认证配置 + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }); +}); + +// 在应用启动时初始化权限规则配置 +var configPath = Path.Combine(Directory.GetCurrentDirectory(), "PermissionRules.json"); +var permissionRuleConfigService = new PermissionRuleConfigService(configPath); +permissionRuleConfigService.Initialize(); + +// 注册为单例服务,供后续使用 +builder.Services.AddSingleton(permissionRuleConfigService); +builder.Services.AddAllServiceRegistrations(builder.Configuration); +builder.Services.AddControllers(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseCors("AllowSpecific"); + +// 添加JWT认证中间件 +Console.WriteLine("正在注册JWT认证中间件..."); +app.UseJwtAuthentication(); +Console.WriteLine("JWT认证中间件注册完成"); + +app.MapControllers(); +app.Run(); diff --git a/backend/src/UniversalAdminSystem.Api/Properties/launchSettings.json b/backend/src/UniversalAdminSystem.Api/Properties/launchSettings.json new file mode 100644 index 0000000..db61601 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:56387", + "sslPort": 44340 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7242;http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/backend/src/UniversalAdminSystem.Api/SystemPermissions.json b/backend/src/UniversalAdminSystem.Api/SystemPermissions.json new file mode 100644 index 0000000..7aa7b88 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/SystemPermissions.json @@ -0,0 +1,89 @@ +{ + "SystemPermissions": [ + { + "Resource": "file", + "Action": "Create", + "Name": "文件上传" + }, + { + "Resource": "file", + "Action": "Read", + "Name": "文件下载" + }, + { + "Resource": "file", + "Action": "Delete", + "Name": "文件删除" + }, + { + "Resource": "user", + "Action": "Create", + "Name": "创建用户" + }, + { + "Resource": "user", + "Action": "Read", + "Name": "查看用户" + }, + { + "Resource": "user", + "Action": "Update", + "Name": "更新用户" + }, + { + "Resource": "user", + "Action": "Delete", + "Name": "删除用户" + }, + { + "Resource": "role", + "Action": "Create", + "Name": "创建角色" + }, + { + "Resource": "role", + "Action": "Read", + "Name": "查看角色" + }, + { + "Resource": "role", + "Action": "Update", + "Name": "更新角色" + }, + { + "Resource": "role", + "Action": "Delete", + "Name": "删除角色" + }, + { + "Resource": "permission", + "Action": "Create", + "Name": "创建权限" + }, + { + "Resource": "permission", + "Action": "Read", + "Name": "查看权限" + }, + { + "Resource": "permission", + "Action": "Update", + "Name": "更新权限" + }, + { + "Resource": "permission", + "Action": "Delete", + "Name": "删除权限" + }, + { + "Resource": "config", + "Action": "Update", + "Name": "更新系统配置" + }, + { + "Resource": "system", + "Action": "Manage", + "Name": "管理安全策略" + } + ] +} diff --git a/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.csproj b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.csproj new file mode 100644 index 0000000..5e7a731 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http new file mode 100644 index 0000000..cb7770f --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http @@ -0,0 +1,11 @@ +@url = http://localhost:5101 + +POST {{url}}/api/auth/login +Content-Type: application/json + +{ + "account": "manager", + "password": "manager123" +} + +### diff --git a/backend/src/UniversalAdminSystem.Api/appsettings.json b/backend/src/UniversalAdminSystem.Api/appsettings.json new file mode 100644 index 0000000..5a0c1dd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/appsettings.json @@ -0,0 +1,23 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "UniversalAdminSystem.Infrastructure.Services.SystemPermissionConfigLoader": "Debug" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "pgSql": "server=127.0.0.1;port=5432;uid=postgres;password=031028@yue;database=universal_admin" + }, + "Jwt": { + "Key": "YourSuperSecretKey1232347509872093oiqewupori", + "Issuer": "UniversalAdminSystem", + "Audience": "api-web-admin", + "ExpireHours": 2 + }, + "K2": { + "BaseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1/", + "ApiKey": "sk-a31163f6b59e44dcbdb87c668482ce96" + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/CredentialDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/CredentialDto.cs new file mode 100644 index 0000000..bd00463 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/CredentialDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +public record CredentialDto(string Message, string? Account = null, string? Token = null); diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginDto.cs new file mode 100644 index 0000000..14bb597 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +public record LoginDto(string Account, string Password); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginResultDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginResultDto.cs new file mode 100644 index 0000000..742c9c1 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/LoginResultDto.cs @@ -0,0 +1,5 @@ +using UniversalAdminSystem.Domian.UserManagement.Entities; + +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +public record LoginResultDto(string Token, string UserId, string RoleId, string UserName,UserInfo UserInfo); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterDto.cs new file mode 100644 index 0000000..8006203 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +public record RegisterDto(string Account, string Password, string Email); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterResultDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterResultDto.cs new file mode 100644 index 0000000..00c7d44 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/RegisterResultDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +public record RegisterResultDto(string UserId, string Account, string Message); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/TokenDto.cs b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/TokenDto.cs new file mode 100644 index 0000000..e91dd4d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/DTOs/TokenDto.cs @@ -0,0 +1,26 @@ +namespace UniversalAdminSystem.Application.Authentication.DTOs; + +/// +/// 刷新Token请求DTO +/// +public record RefreshTokenDto(string Token); + +/// +/// 刷新Token结果DTO +/// +public record RefreshTokenResultDto(string Token, string UserId, string RoleId); + +/// +/// 验证Token请求DTO +/// +public record ValidateTokenDto(string Token); + +/// +/// Token验证结果DTO +/// +public record TokenValidationResultDto(bool IsValid, string UserId, string RoleId); + +/// +/// 登出响应DTO +/// +public record LogoutResultDto(string Message); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IJwtTokenService.cs b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IJwtTokenService.cs new file mode 100644 index 0000000..b9413a3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IJwtTokenService.cs @@ -0,0 +1,10 @@ +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Application.Authentication.Interfaces; + +public interface IJwtTokenService +{ + string GenerateToken(string userId, string roleId, UserStatus status); + (string userId, string roleId, UserStatus status) ParseToken(string token); + string RefreshToken(string oldToken, out string userId, out string roleId, out UserStatus status); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/ILoginAppService.cs b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/ILoginAppService.cs new file mode 100644 index 0000000..80adb82 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/ILoginAppService.cs @@ -0,0 +1,8 @@ +using UniversalAdminSystem.Application.Authentication.DTOs; + +namespace UniversalAdminSystem.Application.Authentication.Interfaces; + +public interface ILoginAppService +{ + Task LoginAsync(LoginDto dto); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IRegisterAppService.cs b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IRegisterAppService.cs new file mode 100644 index 0000000..7070f3a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/Interfaces/IRegisterAppService.cs @@ -0,0 +1,8 @@ +using UniversalAdminSystem.Application.Authentication.DTOs; + +namespace UniversalAdminSystem.Application.Authentication.Interfaces; + +public interface IRegisterAppService +{ + Task RegisterAsync(RegisterDto dto); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/Service/LoginAppService.cs b/backend/src/UniversalAdminSystem.Application/Authentication/Service/LoginAppService.cs new file mode 100644 index 0000000..70edb56 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/Service/LoginAppService.cs @@ -0,0 +1,40 @@ +using UniversalAdminSystem.Application.Authentication.DTOs; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Application.Authentication.Service; + +public class LoginAppService : ILoginAppService +{ + private readonly IUserRepository _userRepository; + private readonly IJwtTokenService _jwtService; + private readonly IPasswordHelper _passwordHelper; + private readonly IUserInfoRepository _userInfoRepository; + + public LoginAppService( + IUserRepository userRepository, + IJwtTokenService jwtService, + IPasswordHelper passwordHelper, + IUserInfoRepository userInfoRepository) + { + _userRepository = userRepository; + _jwtService = jwtService; + _passwordHelper = passwordHelper; + _userInfoRepository = userInfoRepository; + } + + public async Task LoginAsync(LoginDto dto) + { + + var user = await _userRepository.GetUserByAccountAsync(dto.Account) ?? throw new Exception("账号不存在"); + var userinfo = await _userInfoRepository.GetByGuidAsync(user.UserInfoId ?? throw new Exception("用户信息未绑定")) ?? throw new Exception("用户信息不存在"); + // 使用密码验证服务进行安全比对 + if (!_passwordHelper.VerifyPasswordWithSeparateSalt(dto.Password, user.Password, user.Salt)) throw new Exception("密码错误"); + // 生成JWT令牌 + var token = _jwtService.GenerateToken(user.UserId.Value.ToString(), user.RoleId?.Value.ToString() ?? "", user.Status); + // 返回登录结果 + return new LoginResultDto(token, user.UserId.Value.ToString(), user.RoleId?.Value.ToString() ?? "", user.Account.Value.ToString(),userinfo); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Authentication/Service/RegisterAppService.cs b/backend/src/UniversalAdminSystem.Application/Authentication/Service/RegisterAppService.cs new file mode 100644 index 0000000..5577a52 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Authentication/Service/RegisterAppService.cs @@ -0,0 +1,91 @@ +using UniversalAdminSystem.Application.Authentication.DTOs; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; + +namespace UniversalAdminSystem.Application.Authentication.Service; + +public class RegisterAppService : IRegisterAppService +{ + private readonly IUserRepository _userRepository; + private readonly IPasswordHelper _passwordHelper; + private readonly IUnitOfWork _unitOfWork; + + private readonly IRoleRepository _RoleRepo; + + public RegisterAppService( + IUserRepository userRepository, + IPasswordHelper passwordHelper, + IUnitOfWork unitOfWork, + IRoleRepository roleRepository) + { + _userRepository = userRepository; + _passwordHelper = passwordHelper; + _unitOfWork = unitOfWork; + _RoleRepo = roleRepository; + } + + public async Task RegisterAsync(RegisterDto dto) + { + try + { + // 开始事务 + await _unitOfWork.BeginTransactionAsync(); + + // 检查账号是否已存在 + var existingUser = await _userRepository.GetUserByAccountAsync(dto.Account); + if (existingUser != null) + { + throw new Exception("账号已存在"); + } + + // 检查邮箱是否已存在 + var existingUserByEmail = await _userRepository.GetUserByEmailAsync(dto.Email); + if (existingUserByEmail != null) + { + throw new Exception("邮箱已被注册"); + } + + // 密码加密 + var (hashedPassword, salt) = _passwordHelper.HashPasswordWithSeparateSalt(dto.Password); + + // 创建用户信息 + var userInfo = UserInfo.CreateUserInfo(); + await _userRepository.AddUserInfoAsync(userInfo); + var Role = await _RoleRepo.GetByNameAsync("普通用户") ?? throw new Exception("用户创建失败"); + // 创建用户 + var user = User.CreateUser( + userInfo.UserInfoId, + dto.Account, + hashedPassword, + dto.Email, + salt, + UserStatus.Normal, + Role.RoleId.Value + + ); + + await _userRepository.AddAsync(user); + + // 提交事务 + await _unitOfWork.CommitAsync(); + + return new RegisterResultDto( + user.UserId.Value.ToString(), + user.Account.Value.ToString(), + "注册成功" + ); + } + catch (Exception) + { + // 回滚事务 + await _unitOfWork.RollbackAsync(); + throw; + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Common/Exceptions/BusinessException.cs b/backend/src/UniversalAdminSystem.Application/Common/Exceptions/BusinessException.cs new file mode 100644 index 0000000..ef09cbe --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Common/Exceptions/BusinessException.cs @@ -0,0 +1,43 @@ +namespace UniversalAdminSystem.Application.Common.Exceptions; + +/// +/// 业务异常基类 +/// +public class BusinessException : Exception +{ + public string ErrorCode { get; } + + public BusinessException(string message, string errorCode = "BUSINESS_ERROR") : base(message) + { + ErrorCode = errorCode; + } + + public BusinessException(string message, Exception innerException, string errorCode = "BUSINESS_ERROR") : base(message, innerException) + { + ErrorCode = errorCode; + } +} + +/// +/// 菜单相关业务异常 +/// +public class MenuBusinessException : BusinessException +{ + public MenuBusinessException(string message, string errorCode = "MENU_ERROR") : base(message, errorCode) { } +} + +/// +/// 未找到异常 +/// +public class NotFoundException : BusinessException +{ + public NotFoundException(string message) : base(message, "NOT_FOUND") { } +} + +/// +/// 验证异常 +/// +public class ValidationException : BusinessException +{ + public ValidationException(string message) : base(message, "VALIDATION_ERROR") { } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Common/Results/Result.cs b/backend/src/UniversalAdminSystem.Application/Common/Results/Result.cs new file mode 100644 index 0000000..b725103 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Common/Results/Result.cs @@ -0,0 +1,54 @@ +namespace UniversalAdminSystem.Application.Common.Results; + +/// +/// 统一响应结果基类 +/// +public class Result +{ + public bool IsSuccess { get; } + public string Message { get; } + public string[] Errors { get; } + + protected Result(bool isSuccess, string message, string[]? errors = null) + { + IsSuccess = isSuccess; + Message = message; + Errors = errors ?? Array.Empty(); + } + + public static Result Success(string message = "操作成功") + { + return new Result(true, message); + } + + public static Result Failure(string message, string[]? errors = null) + { + return new Result(false, message, errors); + } +} + +/// +/// 带数据的响应结果 +/// +public class Result : Result +{ + public T? Data { get; } + + protected Result(bool isSuccess, string message, T? data, string[]? errors = null) + : base(isSuccess, message, errors) + { + Data = data; + } + + public static Result Success(T data, string message = "操作成功") + { + return new Result(true, message, data); + } + + public static new Result Failure(string message, string[]? errors = null) + { + return new Result(false, message, default, errors); + } + + public static implicit operator Result(T data) => Success(data); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Common/interfaces/IUnitOfWork.cs b/backend/src/UniversalAdminSystem.Application/Common/interfaces/IUnitOfWork.cs new file mode 100644 index 0000000..c4f6fab --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Common/interfaces/IUnitOfWork.cs @@ -0,0 +1,9 @@ +namespace UniversalAdminSystem.Application.Common.Interfaces; + +public interface IUnitOfWork +{ + Task BeginTransactionAsync(); + Task CommitAsync(); + Task RollbackAsync(); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs new file mode 100644 index 0000000..79c8621 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs @@ -0,0 +1,40 @@ +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Application.FileStorage.DTOs; + +public record FileDto( + Guid Id, + string Name, + string Path, + long Size, + string Type, + Guid OwnerId, + DateTime UploadTime, + bool IsFolder, + Guid? ParentId, + string AccessLevel +); + +public record FileUploadDto( + string Name, + string Path, + long Size, + string Type, + Guid? ParentId = null +); + +public record FileDownloadDto( + Guid Id, + string Name, + string Path, + string Type +); + +public record FileUploadResultDto( + Guid Id, + string Name, + string Path, + string Type +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs new file mode 100644 index 0000000..ef8a47b --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs @@ -0,0 +1,23 @@ +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Application.FileStorage.DTOs; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Application.FileStorage.Interfaces; + +public interface IFileAppService +{ + Task UploadAsync(FileUploadDto dto); + + Task> GetList(); + // Task> DownloadAsync(FileId fileId); + // Task>> ListAsync(FileId? parentId); + // Task> CreateFolderAsync(string name, FileId? parentId); + // Task DeleteAsync(FileId fileId); + // Task> GetFileByIdAsync(FileId fileId); + + Task GetFileById(Guid id); + + Task RemoveFile(Guid id); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs new file mode 100644 index 0000000..f4ae062 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs @@ -0,0 +1,84 @@ +using UniversalAdminSystem.Application.FileStorage.DTOs; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.IRepository; +using UniversalAdminSystem.Domian.FileStorage.Services; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using File = UniversalAdminSystem.Domian.FileStorage.Aggregates.File; +using UniversalAdminSystem.Domian.FileStorage.Interface; + +namespace UniversalAdminSystem.Application.FileStorage.Services; + +public class FileAppService : IFileAppService +{ + private readonly IFileRepository _fileRepository; + + public FileAppService(IFileRepository fileRepository) + { + _fileRepository = fileRepository; + } + + public async Task UploadAsync(FileUploadDto dto) + { + try + { + var file = File.Create( + FileName.Create(dto.Name), + FilePath.Create(dto.Path), + FileSize.Create(dto.Size), + FileType.Create(dto.Type), + (UserId)Guid.NewGuid(), + false, + dto.ParentId.HasValue ? (FileId)dto.ParentId.Value : null); + await _fileRepository.AddAsync(file); + return new FileUploadResultDto( + file.Id.Value, + file.Name.Value, + file.Path.Value, + file.Type.Value + ); + } + catch (Exception ex) + { + throw new InvalidOperationException($"上传文件失败: {ex.Message}"); + } + } + + public async Task> GetList() + { + var files = await _fileRepository.GetAllAsync(); + return files.Select(file => new FileUploadResultDto( + file.Id, + file.Name, + file.Path, + file.Type + )); + } + + public async Task GetFileById(Guid id) + { + var file = await _fileRepository.GetByGuidAsync(id); + if (file == null) + { + throw new FileNotFoundException(); + } + return new FileDownloadDto(file.Id, file.Name, file.Path, file.Type); + } + + public async Task RemoveFile(Guid id) + { + try + { + await _fileRepository.RemoveAsync(id); + } + catch (System.Exception) + { + + throw new Exception("删除异常"); + } + + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/LogManagement/DTOs/LogEntryDto.cs b/backend/src/UniversalAdminSystem.Application/LogManagement/DTOs/LogEntryDto.cs new file mode 100644 index 0000000..0883fcd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/LogManagement/DTOs/LogEntryDto.cs @@ -0,0 +1,21 @@ +namespace UniversalAdminSystem.Application.LogManagement.DTOs; + +public record LogEntryDto( + Guid Id, + string Level, + string Message, + string Source, + Guid? UserId, + DateTime Timestamp, + string? Context, + string? Exception +); + +public record LogCreateDto( + string Level, + string Message, + string Source, + Guid? UserId = null, + string? Context = null, + string? Exception = null +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/LogManagement/Interfaces/ILogManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/LogManagement/Interfaces/ILogManagementAppService.cs new file mode 100644 index 0000000..61b7d96 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/LogManagement/Interfaces/ILogManagementAppService.cs @@ -0,0 +1,52 @@ +using UniversalAdminSystem.Application.LogManagement.DTOs; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Application.LogManagement.Interfaces; + +public interface ILogManagementAppService +{ + /// + /// 获取所有日志 + /// + Task>> GetAllLogsAsync(); + + /// + /// 根据ID获取日志 + /// + Task> GetLogByIdAsync(Guid id); + + /// + /// 创建日志 + /// + Task> CreateLogAsync(LogCreateDto createDto); + + /// + /// 根据级别获取日志 + /// + Task>> GetLogsByLevelAsync(string level); + + /// + /// 根据用户获取日志 + /// + Task>> GetLogsByUserAsync(Guid userId); + + /// + /// 根据日期范围获取日志 + /// + Task>> GetLogsByDateRangeAsync(DateTime start, DateTime end); + + /// + /// 根据来源获取日志 + /// + Task>> GetLogsBySourceAsync(string source); + + /// + /// 删除日志 + /// + Task DeleteLogAsync(Guid id); + + /// + /// 清空所有日志 + /// + Task ClearLogsAsync(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/LogManagement/Services/LogManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/LogManagement/Services/LogManagementAppService.cs new file mode 100644 index 0000000..0b81223 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/LogManagement/Services/LogManagementAppService.cs @@ -0,0 +1,182 @@ +using UniversalAdminSystem.Application.LogManagement.DTOs; +using UniversalAdminSystem.Application.LogManagement.Interfaces; +using UniversalAdminSystem.Domian.LogManagement.Aggregates; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Application.Common.Interfaces; + +namespace UniversalAdminSystem.Application.LogManagement.Services; + +public class LogManagementAppService : ILogManagementAppService +{ + private readonly IRepository _logRepository; + private readonly IUnitOfWork _unitOfWork; + + public LogManagementAppService( + IRepository logRepository, + IUnitOfWork unitOfWork) + { + _logRepository = logRepository; + _unitOfWork = unitOfWork; + } + + public async Task>> GetAllLogsAsync() + { + try + { + var logs = await _logRepository.GetAllAsync(); + var logDtos = logs.Select(MapToDto); + return Result>.Success(logDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取日志列表失败: {ex.Message}"); + } + } + + public async Task> GetLogByIdAsync(Guid id) + { + try + { + var log = await _logRepository.GetByGuidAsync(id); + if (log == null) + { + return Result.Success(null); + } + + return Result.Success(MapToDto(log)); + } + catch (Exception ex) + { + return Result.Failure($"获取日志详情失败: {ex.Message}"); + } + } + + public async Task> CreateLogAsync(LogCreateDto createDto) + { + try + { + var log = LogEntry.Create( + createDto.Level, + createDto.Message, + createDto.Source, + createDto.UserId.HasValue ? (UserId)createDto.UserId.Value : null, + createDto.Context, + createDto.Exception + ); + + await _logRepository.AddAsync(log); + await _unitOfWork.SaveChangesAsync(); + return Result.Success(MapToDto(log)); + } + catch (Exception ex) + { + return Result.Failure($"创建日志失败: {ex.Message}"); + } + } + + public async Task>> GetLogsByLevelAsync(string level) + { + try + { + var logs = await _logRepository.GetAllAsync(); + var filteredLogs = logs.Where(l => l.Level == level).Select(MapToDto); + return Result>.Success(filteredLogs); + } + catch (Exception ex) + { + return Result>.Failure($"获取日志失败: {ex.Message}"); + } + } + + public async Task>> GetLogsByUserAsync(Guid userId) + { + try + { + var logs = await _logRepository.GetAllAsync(); + var userLogs = logs.Where(l => l.UserId != null && l.UserId.Value == userId); + + var logDtos = userLogs.Select(MapToDto); + return Result>.Success(logDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取用户日志失败: {ex.Message}"); + } + } + + public async Task>> GetLogsByDateRangeAsync(DateTime start, DateTime end) + { + try + { + var logs = await _logRepository.GetAllAsync(); + var dateRangeLogs = logs.Where(l => l.Timestamp >= start && l.Timestamp <= end); + + var logDtos = dateRangeLogs.Select(MapToDto); + return Result>.Success(logDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取日志失败: {ex.Message}"); + } + } + + public async Task>> GetLogsBySourceAsync(string source) + { + try + { + var logs = await _logRepository.GetAllAsync(); + var sourceLogs = logs.Where(l => l.Source == source); + + var logDtos = sourceLogs.Select(MapToDto); + return Result>.Success(logDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取日志失败: {ex.Message}"); + } + } + + public async Task DeleteLogAsync(Guid id) + { + try + { + var log = await _logRepository.GetByGuidAsync(id); + if (log == null) + { + return Result.Failure("日志不存在"); + } + + await _logRepository.RemoveAsync(id); + await _unitOfWork.SaveChangesAsync(); + return Result.Success("日志删除成功"); + } + catch (Exception ex) + { + return Result.Failure($"删除日志失败: {ex.Message}"); + } + } + + public async Task ClearLogsAsync() + { + try + { + var logs = await _logRepository.GetAllAsync(); + foreach (var log in logs) + { + await _logRepository.RemoveAsync(log.Id); + } + await _unitOfWork.SaveChangesAsync(); + return Result.Success("日志清空成功"); + } + catch (Exception ex) + { + return Result.Failure($"清空日志失败: {ex.Message}"); + } + } + + private static LogEntryDto MapToDto(LogEntry l) => new( + l.Id, l.Level, l.Message, l.Source, l.UserId?.Value, l.Timestamp, l.Context, l.Exception + ); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/AssignPermissionDto.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/AssignPermissionDto.cs new file mode 100644 index 0000000..a875638 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/AssignPermissionDto.cs @@ -0,0 +1,11 @@ +namespace UniversalAdminSystem.Application.PermissionManagement.DTOs; + +/// +/// 权限分配数据传输对象 +/// +/// 权限编码 +/// 角色ID +public record AssignPermissionDto( + string PermissionCode, + Guid RoleId +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionCreateDto.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionCreateDto.cs new file mode 100644 index 0000000..79124ba --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionCreateDto.cs @@ -0,0 +1,15 @@ +namespace UniversalAdminSystem.Application.PermissionManagement.DTOs; + +/// +/// 权限创建数据传输对象 +/// +/// 权限名称 +/// 权限作用资源 +/// 权限操作类型值 +/// 权限描述 +public record PermissionCreateDto( + string Name, + string Resource, + int ActionValue, + string? Description = null +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionDto.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionDto.cs new file mode 100644 index 0000000..3c8cf10 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/PermissionDto.cs @@ -0,0 +1,13 @@ +namespace UniversalAdminSystem.Application.PermissionManagement.DTOs; + +/// +/// 权限数据传输对象 +/// +/// 权限名称 +/// 权限编码 +/// 权限描述 +public record PermissionDto( + string Name, + string Code, + string Description +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/RoleDto.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/RoleDto.cs new file mode 100644 index 0000000..a72356d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/DTOs/RoleDto.cs @@ -0,0 +1,46 @@ +namespace UniversalAdminSystem.Application.PermissionManagement.DTOs; + +/// +/// 角色数据传输对象 +/// +/// 角色ID +/// 角色名称 +/// 角色描述 +/// 是否为系统角色 +/// 是否为超级管理员 +/// 创建时间 +/// 更新时间 +/// 权限数量 +public record RoleDto( + Guid RoleId, + string Name, + string? Description, + bool IsSystem, + bool IsSupper, + DateTime CreateTime, + DateTime UpdateTime, + int PermissionCount = 0 +); + +/// +/// 角色创建数据传输对象 +/// +/// 角色名称 +/// 角色描述 +/// 是否为系统角色 +/// 是否为超级管理员 +public record RoleCreateDto( + string Name, + string? Description = null, + bool IsSupper = false +); + +/// +/// 角色更新数据传输对象 +/// +/// 角色名称 +/// 角色描述 +public record RoleUpdateDto( + string Name, + string? Description = null +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionCheckService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionCheckService.cs new file mode 100644 index 0000000..f355206 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionCheckService.cs @@ -0,0 +1,46 @@ +namespace UniversalAdminSystem.Application.PermissionManagement.Interfaces; + +/// +/// 权限检查服务接口 +/// 提供用户权限验证功能 +/// +public interface IPermissionCheckService +{ + /// + /// 检查用户是否有指定权限 + /// + /// 用户ID + /// 权限编码 + /// 是否有权限 + Task CheckUserPermissionAsync(Guid userId, string permissionCode); + + /// + /// 检查用户是否有任意一个指定权限 + /// + /// 用户ID + /// 权限编码列表 + /// 是否有权限 + Task CheckUserAnyPermissionAsync(Guid userId, IEnumerable permissionCodes); + + /// + /// 检查用户是否有所有指定权限 + /// + /// 用户ID + /// 权限编码列表 + /// 是否有权限 + Task CheckUserAllPermissionsAsync(Guid userId, IEnumerable permissionCodes); + + /// + /// 获取用户的所有权限 + /// + /// 用户ID + /// 权限编码列表 + Task> GetUserPermissionsAsync(Guid userId); + + /// + /// 获取用户的所有角色 + /// + /// 用户ID + /// 角色名称列表 + Task> GetUserRoleAsync(Guid userId); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionManagementAppService.cs new file mode 100644 index 0000000..9d3caa5 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IPermissionManagementAppService.cs @@ -0,0 +1,37 @@ +using UniversalAdminSystem.Application.PermissionManagement.DTOs; + +namespace UniversalAdminSystem.Application.PermissionManagement.Interfaces; + +/// +/// 权限管理应用服务接口 +/// 提供权限的创建、查询、分配等业务操作 +/// +public interface IPermissionManagementAppService +{ + /// + /// 获取所有权限列表 + /// + /// 权限列表 + Task> GetAllPermissionAsync(); + + /// + /// 创建新权限 + /// + /// 权限创建数据传输对象 + /// 创建的权限信息 + Task CreatePermissionAsync(PermissionCreateDto createDto); + + /// + /// 删除权限 + /// + /// 权限ID + /// 删除操作结果 + Task RemovePermissionAsync(Guid permissionId); + + /// + /// 为角色分配权限 + /// + /// 权限分配数据传输对象 + /// 分配操作结果 + Task AssignPermissionToRoleAsync(AssignPermissionDto assignDto); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IRoleManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IRoleManagementAppService.cs new file mode 100644 index 0000000..6667c1e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Interfaces/IRoleManagementAppService.cs @@ -0,0 +1,68 @@ +using UniversalAdminSystem.Application.PermissionManagement.DTOs; + +namespace UniversalAdminSystem.Application.PermissionManagement.Interfaces; + +/// +/// 角色管理应用服务接口 +/// 提供角色的创建、查询、更新、删除等业务操作 +/// +public interface IRoleManagementAppService +{ + /// + /// 获取所有角色列表 + /// + /// 角色列表 + Task> GetAllRolesAsync(); + + /// + /// 根据ID获取角色 + /// + /// 角色ID + /// 角色信息 + Task GetRoleByIdAsync(Guid roleId); + + /// + /// 创建新角色 + /// + /// 角色创建数据传输对象 + /// 创建的角色信息 + Task CreateRoleAsync(RoleCreateDto createDto); + + /// + /// 更新角色信息 + /// + /// 角色ID + /// 角色更新数据传输对象 + /// 更新后的角色信息 + Task UpdateRoleAsync(Guid roleId, RoleUpdateDto updateDto); + + /// + /// 删除角色 + /// + /// 角色ID + /// 删除操作结果 + Task DeleteRoleAsync(Guid roleId); + + /// + /// 为角色分配权限 + /// + /// 角色ID + /// 权限ID列表 + /// 分配操作结果 + Task AssignPermissionsToRoleAsync(Guid roleId, IEnumerable permissionIds); + + /// + /// 移除角色的权限 + /// + /// 角色ID + /// 权限ID列表 + /// 移除操作结果 + Task RemovePermissionsFromRoleAsync(Guid roleId, IEnumerable permissionIds); + + /// + /// 获取角色的所有权限 + /// + /// 角色ID + /// 权限列表 + Task> GetRolePermissionsAsync(Guid roleId); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionCheckService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionCheckService.cs new file mode 100644 index 0000000..022244f --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionCheckService.cs @@ -0,0 +1,349 @@ +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Application.PermissionManagement.Services; + +/// +/// 权限检查服务实现 +/// 提供用户权限验证的具体逻辑 +/// +public class PermissionCheckService : IPermissionCheckService +{ + private readonly IUserRepository _userRepository; + private readonly IRoleRepository _roleRepository; + private readonly IPermissionRepository _permissionRepository; + + public PermissionCheckService( + IUserRepository userRepository, + IRoleRepository roleRepository, + IPermissionRepository permissionRepository) + { + _userRepository = userRepository; + _roleRepository = roleRepository; + _permissionRepository = permissionRepository; + } + + public async Task CheckUserPermissionAsync(Guid userId, string permissionCode) + { + try + { + var user = await _userRepository.GetByGuidAsync(userId); + if (user == null || user.RoleId == null) return false; + var role = await _roleRepository.GetByGuidAsync(user.RoleId.Value); + if (role == null) return false; + var permission = await _permissionRepository.GetByCodeAsync(PermissionCode.Create(permissionCode)); + if (permission == null) return false; + + Console.WriteLine($"检查权限: {permissionCode}"); + Console.WriteLine($"角色权限数量: {role.Permissions.Count}"); + + // 打印角色拥有的所有权限 + foreach (var perm in role.Permissions) + { + Console.WriteLine($"角色权限: {perm.Code} (类型: {perm.Code.GetType().Name})"); + } + + // 修复比较逻辑:使用值对象比较 + var hasPermission = role.Permissions.Any(p => p.Code == PermissionCode.Create(permissionCode)); + Console.WriteLine($"权限检查结果: {hasPermission}"); + + return hasPermission; + } + catch (Exception ex) + { + Console.WriteLine($"权限检查错误: {ex.Message}"); + return false; + } + } + + /// + /// 检查用户是否有任意一个指定权限 + /// + public async Task CheckUserAnyPermissionAsync(Guid userId, IEnumerable permissionCodes) + { + foreach (var permissionCode in permissionCodes) + { + if (await CheckUserPermissionAsync(userId, permissionCode)) return true; + } + return false; + } + + /// + /// 检查用户是否有所有指定权限 + /// + public async Task CheckUserAllPermissionsAsync(Guid userId, IEnumerable permissionCodes) + { + foreach (var permissionCode in permissionCodes) + { + if (!await CheckUserPermissionAsync(userId, permissionCode)) return false; + } + return true; + } + + /// + /// 获取用户的所有权限编码 + /// + public async Task> GetUserPermissionsAsync(Guid userId) + { + try + { + if (userId == Guid.Empty) throw new ArgumentException("用户ID为空", nameof(userId)); + var user = await _userRepository.GetByGuidAsync(userId) ?? throw new InvalidOperationException("用户不存在"); + if (user.RoleId == null) return Enumerable.Empty(); + var role = await _roleRepository.GetByGuidAsync(user.RoleId.Value) ?? throw new InvalidDataException("用户角色不存在"); + return role.Permissions.Select(p => p.Code.Value); + } + catch (Exception ex) + { + Console.WriteLine($"获取用户权限错误: {ex.Message}"); + return Enumerable.Empty(); + } + } + + /// + /// 获取用户的所有角色 + /// + public async Task> GetUserRoleAsync(Guid userId) + { + try + { + var user = await _userRepository.GetByGuidAsync(userId); + if (user == null) return Enumerable.Empty(); + if (user.RoleId == null) return Enumerable.Empty(); + var role = await _roleRepository.GetByGuidAsync(user.RoleId.Value); + if (role == null) return Enumerable.Empty(); + return new[] { role.Name.Value }; + } + catch (Exception ex) + { + Console.WriteLine($"获取用户角色错误: {ex.Message}"); + return Enumerable.Empty(); + } + } + + /// + /// 检查用户是否有菜单访问权限 + /// + public async Task CheckMenuPermissionAsync(Guid userId, string? menuPermissionCode) + { + // 如果菜单没有设置权限要求,则所有用户都可以访问 + if (string.IsNullOrEmpty(menuPermissionCode)) + { + return true; + } + + return await CheckUserPermissionAsync(userId, menuPermissionCode); + } + + /// + /// 检查用户是否有文件访问权限 + /// + public async Task CheckFilePermissionAsync(Guid userId, string fileAccessLevel, Guid? fileOwnerId = null) + { + try + { + // 超级管理员可以访问所有文件 + if (await IsSuperAdminAsync(userId)) + { + return true; + } + + // 管理员可以访问除超级管理员文件外的所有文件 + if (await IsAdminAsync(userId)) + { + if (fileOwnerId.HasValue) + { + var fileOwner = await _userRepository.GetByGuidAsync(fileOwnerId.Value); + return fileOwner == null || !await IsSuperAdminAsync(fileOwnerId.Value); + } + return true; + } + + // 普通用户只能访问自己的文件或公开文件 + switch (fileAccessLevel.ToLower()) + { + case "public": + return true; + case "private": + return fileOwnerId.HasValue && fileOwnerId.Value == userId; + case "restricted": + return false; + default: + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"文件权限检查错误: {ex.Message}"); + return false; + } + } + + /// + /// 检查用户是否有日志查看权限 + /// + public async Task CheckLogPermissionAsync(Guid userId, string logLevel) + { + try + { + // 超级管理员可以查看所有日志 + if (await IsSuperAdminAsync(userId)) + { + return true; + } + + // 管理员可以查看除系统级日志外的所有日志 + if (await IsAdminAsync(userId)) + { + return logLevel.ToLower() != "system"; + } + + // 普通用户只能查看基本日志 + return logLevel.ToLower() == "basic" || logLevel.ToLower() == "info"; + } + catch (Exception ex) + { + Console.WriteLine($"日志权限检查错误: {ex.Message}"); + return false; + } + } + + /// + /// 检查用户是否有系统设置管理权限 + /// + public async Task CheckSystemSettingPermissionAsync(Guid userId, string settingKey) + { + try + { + // 超级管理员可以管理所有设置 + if (await IsSuperAdminAsync(userId)) + { + return true; + } + + // 管理员可以管理除系统核心设置外的所有设置 + if (await IsAdminAsync(userId)) + { + var restrictedSettings = new[] { "system.core", "security", "database" }; + return !restrictedSettings.Any(s => settingKey.StartsWith(s)); + } + + // 普通用户只能查看基本设置 + var basicSettings = new[] { "ui", "theme", "language" }; + return basicSettings.Any(s => settingKey.StartsWith(s)); + } + catch (Exception ex) + { + Console.WriteLine($"系统设置权限检查错误: {ex.Message}"); + return false; + } + } + + + /// + /// 检查用户是否为超级管理员 + /// + public async Task IsSuperAdminAsync(Guid userId) + { + try + { + var permissions = await GetUserPermissionsAsync(userId); + return permissions.Any(p => p.Contains("super") || p.Contains("system")); + } + catch (Exception ex) + { + Console.WriteLine($"超级管理员检查错误: {ex.Message}"); + return false; + } + } + + /// + /// 检查用户是否为管理员 + /// + public async Task IsAdminAsync(Guid userId) + { + try + { + var user = await _userRepository.GetByGuidAsync(userId); + if (user == null || user.RoleId == null) return false; + var role = await _roleRepository.GetByGuidAsync(user.RoleId.Value); + if (role == null) return false; + return role.IsSupper; + } + catch (Exception ex) + { + Console.WriteLine($"管理员检查错误: {ex.Message}"); + return false; + } + } + + /// + /// 获取用户可访问的菜单权限列表 + /// + public async Task> GetUserMenuPermissionsAsync(Guid userId) + { + try + { + var allPermissions = await GetUserPermissionsAsync(userId); + return allPermissions.Where(p => p.StartsWith("menu:")); + } + catch (Exception ex) + { + Console.WriteLine($"获取菜单权限错误: {ex.Message}"); + return Enumerable.Empty(); + } + } + + /// + /// 获取用户可访问的文件权限列表 + /// + public async Task> GetUserFilePermissionsAsync(Guid userId) + { + try + { + var allPermissions = await GetUserPermissionsAsync(userId); + return allPermissions.Where(p => p.StartsWith("file:")); + } + catch (Exception ex) + { + Console.WriteLine($"获取文件权限错误: {ex.Message}"); + return Enumerable.Empty(); + } + } + + /// + /// 获取用户可访问的日志权限列表 + /// + public async Task> GetUserLogPermissionsAsync(Guid userId) + { + try + { + var allPermissions = await GetUserPermissionsAsync(userId); + return allPermissions.Where(p => p.StartsWith("log:")); + } + catch (Exception ex) + { + Console.WriteLine($"获取日志权限错误: {ex.Message}"); + return Enumerable.Empty(); + } + } + + /// + /// 获取用户可访问的系统设置权限列表 + /// + public async Task> GetUserSystemSettingPermissionsAsync(Guid userId) + { + try + { + var allPermissions = await GetUserPermissionsAsync(userId); + return allPermissions.Where(p => p.StartsWith("setting:")); + } + catch (Exception ex) + { + Console.WriteLine($"获取系统设置权限错误: {ex.Message}"); + return Enumerable.Empty(); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionManagementAppService.cs new file mode 100644 index 0000000..f6b7ee2 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/PermissionManagementAppService.cs @@ -0,0 +1,141 @@ +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Application.PermissionManagement.DTOs; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Application.PermissionManagement.Services; + +/// +/// 权限管理应用服务实现 +/// 提供权限的创建、查询、分配等业务操作的具体实现 +/// +public class PermissionManagementAppService : IPermissionManagementAppService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IPermissionRepository _permissionRepository; + private readonly IRoleRepository _roleRepository; + private readonly IAssignPermissionDomainService _assignPermissionDomainService; + + public PermissionManagementAppService( + IUnitOfWork unitOfWork, + IPermissionRepository permissionRepository, + IRoleRepository roleRepository, + IAssignPermissionDomainService assignPermissionDomainService + ) + { + _unitOfWork = unitOfWork; + _permissionRepository = permissionRepository; + _roleRepository = roleRepository; + _assignPermissionDomainService = assignPermissionDomainService; + } + + /// + /// 为角色分配权限 + /// + public async Task AssignPermissionToRoleAsync(AssignPermissionDto assignDto) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + var permission = await _permissionRepository.GetByCodeAsync((PermissionCode)assignDto.PermissionCode) + ?? throw new KeyNotFoundException("未找到对应的权限"); + var role = await _roleRepository.GetByGuidAsync(assignDto.RoleId) + ?? throw new KeyNotFoundException("未找到对应的角色"); + var state = _assignPermissionDomainService.AssignPermission(permission, role); + if (state) + { + await _roleRepository.Update(role); + + } + await _unitOfWork.CommitAsync(); + } + catch (KeyNotFoundException) + { + await _unitOfWork.RollbackAsync(); + throw; + } + } + + /// + /// 创建新权限 + /// + public async Task CreatePermissionAsync(PermissionCreateDto createDto) + { + try + { + if (!Enum.IsDefined(typeof(PermissionAction), createDto.ActionValue)) + { + throw new InvalidOperationException($"无效的操作类型值: {createDto.ActionValue}"); + } + PermissionAction enumValue = (PermissionAction)createDto.ActionValue; + // 检查权限代码是否已存在 + var existingPermission = await _permissionRepository.GetByCodeAsync(PermissionCode.Create($"{createDto.Resource}:{enumValue}")); + if (existingPermission != null) + { + throw new InvalidOperationException($"权限代码 '{createDto.Resource}:{enumValue}' 已存在,无法创建重复权限"); + } + + // 开启事务 + await _unitOfWork.BeginTransactionAsync(); + + // 创建权限并保存 + var permission = Permission.CreateStandardPermission( + createDto.Name, + createDto.Resource, + createDto.ActionValue, + createDto.Description + ); + + try + { + var savedPermission = await _permissionRepository.AddAsync(permission); + await _unitOfWork.CommitAsync(); + return new PermissionDto( + savedPermission.Name, + savedPermission.Code, + savedPermission.Description! + ); + } + catch (System.Exception ex) + { + throw new InvalidOperationException(ex.Message); + } + } + catch (System.Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException(ex.Message); + } + } + + /// + /// 获取所有权限列表 + /// + public async Task> GetAllPermissionAsync() + { + var permissions = await _permissionRepository.GetAllAsync(); + var permissionList = permissions.Select(p => new PermissionDto(p.Name, p.Code, p.Description!)); + return permissionList; + } + + /// + /// 删除权限 + /// + public async Task RemovePermissionAsync(Guid permissionId) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + await _permissionRepository.RemoveAsync(permissionId); + await _unitOfWork.CommitAsync(); + } + catch (System.Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new System.Exception(ex.Message); + } + } +} diff --git a/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/RoleManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/RoleManagementAppService.cs new file mode 100644 index 0000000..2603384 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/PermissionManagement/Services/RoleManagementAppService.cs @@ -0,0 +1,301 @@ +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Application.PermissionManagement.DTOs; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Application.PermissionManagement.Services; + +/// +/// 角色管理应用服务实现 +/// 提供角色的创建、查询、更新、删除等业务操作的具体实现 +/// +public class RoleManagementAppService : IRoleManagementAppService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IRoleRepository _roleRepository; + private readonly IPermissionRepository _permissionRepository; + + public RoleManagementAppService( + IUnitOfWork unitOfWork, + IRoleRepository roleRepository, + IPermissionRepository permissionRepository) + { + _unitOfWork = unitOfWork; + _roleRepository = roleRepository; + _permissionRepository = permissionRepository; + } + + /// + /// 获取所有角色列表 + /// + public async Task> GetAllRolesAsync() + { + try + { + var roles = await _roleRepository.GetAllRolesWithPermissionsAsync(); + return roles.Select(r => new RoleDto( + r.RoleId, + r.Name, + r.Description ?? string.Empty, + r.IsSystem, + r.IsSupper, + r.CreateTime, + r.UpdateTime, + r.PermissionCount + )); + } + catch (Exception ex) + { + throw new InvalidOperationException($"获取角色列表失败: {ex.Message}"); + } + } + + /// + /// 根据ID获取角色 + /// + public async Task GetRoleByIdAsync(Guid roleId) + { + try + { + var role = await _roleRepository.GetRoleWithPermissionsAsync(roleId); + if (role == null) return null; + + return new RoleDto( + role.RoleId, + role.Name, + role.Description ?? string.Empty, + role.IsSystem, + role.IsSupper, + role.CreateTime, + role.UpdateTime, + role.PermissionCount + ); + } + catch (Exception ex) + { + throw new InvalidOperationException($"获取角色失败: {ex.Message}"); + } + } + + /// + /// 创建新角色 + /// + public async Task CreateRoleAsync(RoleCreateDto createDto) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + // 检查角色名称是否已存在 + if (await _roleRepository.ExistsAsync(createDto.Name)) + { + throw new InvalidOperationException($"角色名称 '{createDto.Name}' 已存在"); + } + + // 创建角色 + var role = Role.Create( + createDto.Name, + createDto.Description, + false, + createDto.IsSupper + ); + + var savedRole = await _roleRepository.AddAsync(role); + await _unitOfWork.CommitAsync(); + + return new RoleDto( + savedRole.RoleId, + savedRole.Name, + savedRole.Description, + savedRole.IsSystem, + savedRole.IsSupper, + savedRole.CreateTime, + savedRole.UpdateTime + ); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException($"创建角色失败: {ex.Message}"); + } + } + + /// + /// 更新角色信息 + /// + public async Task UpdateRoleAsync(Guid roleId, RoleUpdateDto updateDto) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var role = await _roleRepository.GetByGuidAsync(roleId); + if (role == null) + { + throw new KeyNotFoundException($"角色ID '{roleId}' 不存在"); + } + + // 检查是否为系统角色 + if (role.IsSystem) + { + throw new InvalidOperationException("系统角色不允许修改"); + } + + // 检查角色名称是否已存在(排除当前角色) + var existingRole = await _roleRepository.GetByNameAsync(updateDto.Name); + if (existingRole != null && existingRole.RoleId != roleId) + { + throw new InvalidOperationException($"角色名称 '{updateDto.Name}' 已存在"); + } + + // 更新角色信息 + role.SetName(updateDto.Name); + role.SetDescription(updateDto.Description); + + await _roleRepository.Update(role); + await _unitOfWork.CommitAsync(); + + return new RoleDto( + role.RoleId, + role.Name, + role.Description, + role.IsSystem, + role.IsSupper, + role.CreateTime, + role.UpdateTime + ); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException($"更新角色失败: {ex.Message}"); + } + } + + /// + /// 删除角色 + /// + public async Task DeleteRoleAsync(Guid roleId) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var role = await _roleRepository.GetByGuidAsync(roleId); + if (role == null) + { + throw new KeyNotFoundException($"角色ID '{roleId}' 不存在"); + } + + // 检查是否为系统角色 + if (role.IsSystem) + { + throw new InvalidOperationException("系统角色不允许删除"); + } + + // 检查是否为超级管理员角色 + if (role.IsSupper) + { + throw new InvalidOperationException("超级管理员角色不允许删除"); + } + + await _roleRepository.RemoveAsync(roleId); + await _unitOfWork.CommitAsync(); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException($"删除角色失败: {ex.Message}"); + } + } + + /// + /// 为角色分配权限 + /// + public async Task AssignPermissionsToRoleAsync(Guid roleId, IEnumerable permissionIds) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var role = await _roleRepository.GetByGuidAsync(roleId); + if (role == null) + { + throw new KeyNotFoundException($"角色ID '{roleId}' 不存在"); + } + + // 验证权限是否存在 + var permissions = await _permissionRepository.GetByIdsAsync(permissionIds); + var validPermissionIds = permissions.Select(p => p.PermissionId).ToHashSet(); + var invalidPermissionIds = permissionIds.Except(validPermissionIds).ToList(); + + if (invalidPermissionIds.Any()) + { + throw new InvalidOperationException($"以下权限ID不存在: {string.Join(", ", invalidPermissionIds)}"); + } + + // 为角色分配权限(使用Permission实体) + role.AddPermissions(permissions); + + await _roleRepository.Update(role); + await _unitOfWork.CommitAsync(); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException($"为角色分配权限失败: {ex.Message}"); + } + } + + /// + /// 移除角色的权限 + /// + public async Task RemovePermissionsFromRoleAsync(Guid roleId, IEnumerable permissionIds) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var role = await _roleRepository.GetByGuidAsync(roleId); + if (role == null) + { + throw new KeyNotFoundException($"角色ID '{roleId}' 不存在"); + } + + // 获取要移除的权限实体 + var permissionsToRemove = await _permissionRepository.GetByIdsAsync(permissionIds); + + // 移除角色权限(使用Permission实体) + role.RemovePermissions(permissionsToRemove); + + await _roleRepository.Update(role); + await _unitOfWork.CommitAsync(); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + throw new InvalidOperationException($"移除角色权限失败: {ex.Message}"); + } + } + + /// + /// 获取角色的所有权限 + /// + public async Task> GetRolePermissionsAsync(Guid roleId) + { + var role = await _roleRepository.GetByGuidAsync(roleId); + if (role == null) + { + throw new KeyNotFoundException($"角色ID '{roleId}' 不存在"); + } + + // 直接使用角色的权限导航属性 + return role.Permissions.Select(p => new PermissionDto( + p.Name, + p.Code, + p.Description ?? string.Empty + )); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/SystemSettings/DTOs/SystemSettingDto.cs b/backend/src/UniversalAdminSystem.Application/SystemSettings/DTOs/SystemSettingDto.cs new file mode 100644 index 0000000..fc2982d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/SystemSettings/DTOs/SystemSettingDto.cs @@ -0,0 +1,23 @@ +namespace UniversalAdminSystem.Application.SystemSettings.DTOs; + +public record SystemSettingDto( + Guid Id, + string Key, + string Value, + string? Description, + string? Group, + DateTime CreateTime, + DateTime UpdateTime +); + +public record SystemSettingCreateDto( + string Key, + string Value, + string? Description = null, + string? Group = null +); + +public record SystemSettingUpdateDto( + string Value, + string? Description = null +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/SystemSettings/Interfaces/ISystemSettingAppService.cs b/backend/src/UniversalAdminSystem.Application/SystemSettings/Interfaces/ISystemSettingAppService.cs new file mode 100644 index 0000000..1fa2935 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/SystemSettings/Interfaces/ISystemSettingAppService.cs @@ -0,0 +1,37 @@ +using UniversalAdminSystem.Application.SystemSettings.DTOs; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Application.SystemSettings.Interfaces; + +public interface ISystemSettingAppService +{ + /// + /// 获取所有系统设置 + /// + Task>> GetAllSettingsAsync(); + + /// + /// 根据键获取系统设置 + /// + Task> GetSettingByKeyAsync(string key); + + /// + /// 根据组获取系统设置 + /// + Task>> GetSettingsByGroupAsync(string group); + + /// + /// 创建系统设置 + /// + Task> CreateSettingAsync(SystemSettingCreateDto createDto); + + /// + /// 更新系统设置 + /// + Task> UpdateSettingAsync(Guid id, SystemSettingUpdateDto updateDto); + + /// + /// 删除系统设置 + /// + Task DeleteSettingAsync(Guid id); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/SystemSettings/Services/SystemSettingAppService.cs b/backend/src/UniversalAdminSystem.Application/SystemSettings/Services/SystemSettingAppService.cs new file mode 100644 index 0000000..66951bc --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/SystemSettings/Services/SystemSettingAppService.cs @@ -0,0 +1,154 @@ +using UniversalAdminSystem.Application.SystemSettings.DTOs; +using UniversalAdminSystem.Application.SystemSettings.Interfaces; +using UniversalAdminSystem.Domian.SystemSettings.Aggregates; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Domian.SystemSettings.ValueObjects; +using UniversalAdminSystem.Application.Common.Interfaces; + +namespace UniversalAdminSystem.Application.SystemSettings.Services; + +public class SystemSettingAppService : ISystemSettingAppService +{ + private readonly IRepository _settingRepository; + private readonly IUnitOfWork _unitOfWork; + + public SystemSettingAppService( + IRepository settingRepository, + IUnitOfWork unitOfWork) + { + _settingRepository = settingRepository; + _unitOfWork = unitOfWork; + } + + public async Task>> GetAllSettingsAsync() + { + try + { + var settings = await _settingRepository.GetAllAsync(); + var settingDtos = settings.Select(MapToDto); + return Result>.Success(settingDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取系统设置失败: {ex.Message}"); + } + } + + public async Task> GetSettingByKeyAsync(string key) + { + try + { + var setting = (await _settingRepository.GetAllAsync()).FirstOrDefault(s => s.Key.Value == key); + if (setting == null) + { + return Result.Failure("设置不存在"); + } + + return Result.Success(MapToDto(setting)); + } + catch (Exception ex) + { + return Result.Failure($"获取系统设置失败: {ex.Message}"); + } + } + + public async Task>> GetSettingsByGroupAsync(string group) + { + try + { + var settings = await _settingRepository.GetAllAsync(); + var groupSettings = settings.Where(s => s.Key.Value.StartsWith(group)); + var settingDtos = groupSettings.Select(MapToDto); + return Result>.Success(settingDtos); + } + catch (Exception ex) + { + return Result>.Failure($"获取系统设置失败: {ex.Message}"); + } + } + + public async Task> CreateSettingAsync(SystemSettingCreateDto createDto) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var setting = SystemSetting.Create( + SettingKey.Create(createDto.Key), + SettingValue.Create(createDto.Value), + SettingDescription.Create(createDto.Description ?? "") + ); + + var savedSetting = await _settingRepository.AddAsync(setting); + await _unitOfWork.CommitAsync(); + + return Result.Success(MapToDto(savedSetting)); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"创建系统设置失败: {ex.Message}"); + } + } + + public async Task> UpdateSettingAsync(Guid id, SystemSettingUpdateDto updateDto) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var setting = await _settingRepository.GetByGuidAsync(id); + if (setting == null) + { + return Result.Failure("设置不存在"); + } + + setting.UpdateValue(SettingValue.Create(updateDto.Value)); + + await _settingRepository.Update(setting); + await _unitOfWork.CommitAsync(); + + return Result.Success(MapToDto(setting)); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"更新系统设置失败: {ex.Message}"); + } + } + + public async Task DeleteSettingAsync(Guid id) + { + try + { + await _unitOfWork.BeginTransactionAsync(); + + var setting = await _settingRepository.GetByGuidAsync(id); + if (setting == null) + { + return Result.Failure("设置不存在"); + } + + await _settingRepository.RemoveAsync(id); + await _unitOfWork.CommitAsync(); + + return Result.Success("删除成功"); + } + catch (Exception ex) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"删除系统设置失败: {ex.Message}"); + } + } + + private static SystemSettingDto MapToDto(SystemSetting s) => new( + s.Id, + s.Key.Value, + s.Value.Value, + s.Description?.Value, + s.Group, + s.CreateTime, + s.UpdateTime + ); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj b/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj new file mode 100644 index 0000000..3491141 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj @@ -0,0 +1,13 @@ + + + + + + + + net8.0 + enable + enable + + + diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserCreateDto.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserCreateDto.cs new file mode 100644 index 0000000..4c7fc36 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserCreateDto.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace UniversalAdminSystem.Application.UserManagement.Dtos; + +public record UserCreateDto( + [Required] + [MinLength(1)] + [MaxLength(20)] + string Account, + + [Required] + [MinLength(6)] + [MaxLength(20)] + string Password, + + [Required] + [EmailAddress] + string Email); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDetailDto.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDetailDto.cs new file mode 100644 index 0000000..3468760 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDetailDto.cs @@ -0,0 +1,26 @@ +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Application.UserManagement.Dtos; + + +public record UserDetailDto +{ + public Guid Id { get; set; } + public string Account { get; set; } + public string Email { get; set; } + public UserStatus UserStatus { get; set; } + + public string? RoleName { get; set; } + public UserInfo? UserInfo { get; set; } + + public UserDetailDto(Guid id, string account, string email, UserStatus userStatus, UserInfo? userInfo = null, string? rolename = null) + { + Id = id; + Account = account; + Email = email; + UserStatus = userStatus; + RoleName = rolename; + UserInfo = userInfo; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDto.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDto.cs new file mode 100644 index 0000000..f8151c8 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserDto.cs @@ -0,0 +1,24 @@ +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Application.UserManagement.Dtos; + +public record UserDto +{ + public Guid Id { get; set; } + public string Account { get; set; } + public string Email { get; set; } + public UserStatus UserStatus { get; set; } + + public string? RoleName { get; set; } + + + public UserDto(Guid id, string account, string email, UserStatus userStatus, string? rolename = null) + { + Id = id; + Account = account; + Email = email; + UserStatus = userStatus; + RoleName = rolename; + } +} + diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserUpdateDto.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserUpdateDto.cs new file mode 100644 index 0000000..b4a34fc --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Dtos/UserUpdateDto.cs @@ -0,0 +1,8 @@ +namespace UniversalAdminSystem.Application.UserManagement.Dtos; + +public record UserUpdateDto( + string Account, + string Email, + int StatusCode, + string? Password = null +); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IPasswordHelper.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IPasswordHelper.cs new file mode 100644 index 0000000..af0c2c0 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IPasswordHelper.cs @@ -0,0 +1,11 @@ +namespace UniversalAdminSystem.Application.UserManagement.Interface; + +public interface IPasswordHelper +{ + + (string hashedPassword, string salt) HashPasswordWithSeparateSalt(string password); + bool VerifyPasswordWithSeparateSalt( + string password, + string storedHashedPassword, + string storedSalt); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IUserManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IUserManagementAppService.cs new file mode 100644 index 0000000..376a00a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Interface/IUserManagementAppService.cs @@ -0,0 +1,77 @@ +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Application.UserManagement.Dtos; + +namespace UniversalAdminSystem.Application.UserManagement.Interface; + +/// +/// 用户管理应用服务接口 +/// 提供用户的基本CRUD操作和权限管理功能 +/// +public interface IUserManagementAppService +{ + /// + /// 获取所有用户列表 + /// + /// 用户列表 + Task>> GetUsersAsync(); + + /// + /// 创建新用户 + /// + /// 用户创建数据传输对象 + /// 创建的用户信息 + Task> CreateUserAsync(UserCreateDto createDto); + + /// + /// 删除用户 + /// + /// 用户ID + /// 删除操作结果 + Task DeleteUserAsync(Guid id); + + /// + /// 获取用户详情 + /// + /// 用户ID + /// 用户详情 + Task> GetUserByIdAsync(Guid userId); + + /// + /// 更新用户信息 + /// + /// 用户ID + /// 用户更新数据传输对象 + /// 更新操作结果 + Task UpdateUserAsync(Guid userId, UserUpdateDto updateDto); + + /// + /// 为用户分配角色 + /// + /// 用户ID + /// 角色ID列表 + /// 分配操作结果 + Task AssignRoleAsync(Guid userId, List roleIds); + + /// + /// 移除用户角色 + /// + /// 用户ID + /// 角色ID + /// 移除操作结果 + Task RemoveRoleAsync(Guid userId, Guid roleId); + + /// + /// 获取用户的所有权限 + /// + /// 用户ID + /// 权限编码列表 + Task>> GetUserPermissionsAsync(Guid userId); + + /// + /// 检查用户是否有指定权限 + /// + /// 用户ID + /// 权限编码 + /// 是否有权限 + Task> CheckUserPermissionAsync(Guid userId, string permissionCode); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserManagementAppService.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserManagementAppService.cs new file mode 100644 index 0000000..c6f7e1c --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserManagementAppService.cs @@ -0,0 +1,386 @@ +using UniversalAdminSystem.Application.Common.Results; +using UniversalAdminSystem.Application.UserManagement.Dtos; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Application.Common.Interfaces; + +namespace UniversalAdminSystem.Application.UserManagement.Service; + +public class UserManagementAppService : IUserManagementAppService +{ + private readonly IUserRepository _userRepo; + private readonly IUserInfoRepository _userInfoRepo; + private readonly IRoleRepository _roleRepo; + private readonly IPasswordHelper _passwordHelper; + private readonly IUnitOfWork _unitOfWork; + private readonly IPermissionCheckService _permissionCheckService; + // private readonly UserPermissionIntegrationService _userPermissionIntegration; + // private readonly ICurrentUserContext _currentUserContext; + + public UserManagementAppService( + IUserRepository user, + IUserInfoRepository userInfo, + IRoleRepository role, + IPasswordHelper password, + IUnitOfWork Wokr, + IPermissionCheckService permissionCheckService) + { + _userRepo = user; + _userInfoRepo = userInfo; + _roleRepo = role; + _passwordHelper = password; + _unitOfWork = Wokr; + _permissionCheckService = permissionCheckService; + // _userPermissionIntegration = userPermissionIntegration; + // _currentUserContext = currentUserContext; + } + + public async Task>> GetUsersAsync() + { + try + { + // 权限验证:检查当前用户是否有权限查看用户列表 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result>.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:read")) + // { + // return Result>.Failure("没有权限查看用户列表"); + // } + + var users = await _userRepo.GetAllAsync(); + var Roles = await _roleRepo.GetByNameAsync("普通用户") ?? throw new Exception("用户角色查询失败"); + var RoleId = users.Select(x => x.RoleId != null ? x.RoleId.Value : Roles.RoleId.Value); + var Role = await _roleRepo.GetByIdsAsync(RoleId); + var list = users.Select(x => new UserDto(x.UserId.Value, x.Account, x.Email, x.Status, + Role.FirstOrDefault(r => r.RoleId.Value == x.RoleId.Value).Name)); + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Failure($"获取用户列表失败: {ex.Message}"); + } + } + + public async Task> CreateUserAsync(UserCreateDto createDto) + { + try + { + // 权限验证:检查当前用户是否有权限创建用户 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:create")) + // { + // return Result.Failure("没有权限创建用户"); + // } + + await _unitOfWork.BeginTransactionAsync(); + + // 1. 创建用户信息模板并保存 + var userInfo = UserInfo.CreateUserInfo(); + var info = await _userInfoRepo.AddAsync(userInfo); + + // 2. 密码加密与盐生成 + var (hashedPassword, salt) = _passwordHelper.HashPasswordWithSeparateSalt(createDto.Password); + var Role = await _roleRepo.GetByNameAsync("普通用户") ?? throw new Exception("用户创建失败"); + // 3. 创建用户账户并保存 + var user = User.CreateUser(info.UserInfoId, createDto.Account, hashedPassword, createDto.Email, salt, UserStatus.Normal, Role.RoleId.Value); + try + { + var savedUser = await _userRepo.AddAsync(user); + await _unitOfWork.CommitAsync(); + // 4. 返回基础信息DTO + return Result.Success(new UserDto(savedUser.UserId.Value, savedUser.Account, savedUser.Email, savedUser.Status)); + } + catch (System.Exception) + { + throw new Exception("用户已存在"); + } + } + catch (System.Exception e) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"创建用户失败: {e.Message}"); + } + } + + public async Task DeleteUserAsync(Guid id) + { + try + { + var user = await _userRepo.GetByGuidAsync(id); + if (user == null) return Result.Failure("用户不存在"); + if (user.Status == UserStatus.Logout) return Result.Failure("用户已被注销"); + if (user.Status == UserStatus.Freeze) return Result.Failure("用户已被冻结"); + + // 权限验证:检查当前用户是否有权限删除目标用户 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _userPermissionIntegration.CanDeleteUserAsync(currentUserId.Value, id)) + // { + // return Result.Failure("没有权限删除该用户"); + // } + + await _unitOfWork.BeginTransactionAsync(); + await _userRepo.DeleteUserSoftAsync(id); + await _unitOfWork.CommitAsync(); + return Result.Success("用户删除成功"); + } + catch (Exception e) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"删除用户失败: {e.Message}"); + } + } + + public async Task AssignRoleAsync(Guid userId, List roleIds) + { + try + { + var user = await _userRepo.GetByGuidAsync(userId); + if (user == null) return Result.Failure("用户不存在"); + + // 权限验证:检查当前用户是否有权限为目标用户分配角色 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _userPermissionIntegration.CanAssignRolesAsync(currentUserId.Value, userId, roleIds)) + // { + // return Result.Failure("没有权限为该用户分配角色"); + // } + + foreach (var rid in roleIds) + { + user.AssignRole((RoleId)rid); + } + await _unitOfWork.SaveChangesAsync(); + return Result.Success("角色分配成功"); + } + catch (Exception ex) + { + return Result.Failure($"分配角色失败: {ex.Message}"); + } + } + + public async Task RemoveRoleAsync(Guid userId, Guid roleId) + { + try + { + var user = await _userRepo.GetByGuidAsync(userId); + if (user == null) return Result.Failure("用户不存在"); + + // 权限验证:检查当前用户是否有权限移除用户角色 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:manage")) + // { + // return Result.Failure("没有权限管理用户角色"); + // } + + user.RemoveRole((RoleId)roleId); + await _unitOfWork.SaveChangesAsync(); + return Result.Success("角色移除成功"); + } + catch (Exception ex) + { + return Result.Failure($"移除角色失败: {ex.Message}"); + } + } + + public async Task> GetUserByIdAsync(Guid userId) + { + try + { + // 权限验证:检查当前用户是否有权限查看用户详情 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:read")) + // { + // return Result.Failure("没有权限查看用户详情"); + // } + + var user = await _userRepo.GetByGuidAsync(userId); + if (user == null) return Result.Failure("用户不存在"); + UserInfo? userInfo = null; + if (user.UserInfoId != null) + { + userInfo = await _userInfoRepo.GetByGuidAsync(user.UserInfoId); + } + return Result.Success(new UserDetailDto(user.UserId.Value, user.Account, user.Email, user.Status, userInfo)); + } + catch (Exception ex) + { + return Result.Failure($"获取用户详情失败: {ex.Message}"); + } + } + + public async Task UpdateUserAsync(Guid userId, UserUpdateDto updateDto) + { + try + { + var user = await _userRepo.GetByGuidAsync(userId); + if (user == null) + { + return Result.Failure("用户不存在"); + } + if (updateDto == null) + { + return Result.Failure("为传入数据"); + } + Console.WriteLine("检查更新用户数据传输对象-------------- "); + Console.WriteLine($"检查账号: {updateDto.Account}"); + Console.WriteLine($"检查账号: {updateDto.Email}"); + Console.WriteLine($"检查账号: {updateDto.StatusCode}"); + Console.WriteLine($"检查账号: {updateDto.Password}"); + Console.WriteLine("检查结束-----------"); + + + // 权限验证:检查当前用户是否有权限更新用户信息 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:update")) + // { + // return Result.Failure("没有权限更新用户信息"); + // } + + await _unitOfWork.BeginTransactionAsync(); + + // 更新用户信息 + if (!string.IsNullOrEmpty(updateDto.Email)) + { + user.UpdateUserEmail(updateDto.Email); + } + + // 如果提供了新密码,则更新密码 + if (!string.IsNullOrEmpty(updateDto.Password)) + { + var (hashedPassword, salt) = _passwordHelper.HashPasswordWithSeparateSalt(updateDto.Password); + user.UpdateUserPassword(hashedPassword, salt); + } + var userStatus = (UserStatus)updateDto.StatusCode; + user.UpdateUserStatus((UserStatus)updateDto.StatusCode); + + await _userRepo.Update(user); + await _unitOfWork.CommitAsync(); + + return Result.Success("用户信息更新成功"); + } + catch (Exception e) + { + await _unitOfWork.RollbackAsync(); + return Result.Failure($"更新用户信息失败: {e.Message}"); + } + } + + public async Task>> GetUserPermissionsAsync(Guid userId) + { + try + { + // 权限验证:检查当前用户是否有权限查看用户权限 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result>.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:read")) + // { + // return Result>.Failure("没有权限查看用户权限"); + // } + + var user = await _userRepo.GetByGuidAsync(userId); + if (user == null) return Result>.Failure("用户不存在"); + if (user.RoleId == null) return Result>.Failure("用户角色不存在"); + var role = await _roleRepo.GetByGuidAsync(user.RoleId.Value); + if (role == null) return Result>.Failure("用户角色不存在"); + var permissions = role.Permissions.Select(x => x.Code.Value); + return Result>.Success(permissions); + } + catch (Exception ex) + { + return Result>.Failure($"获取用户权限失败: {ex.Message}"); + } + } + + // public async Task>> GetUserRolesAsync(Guid userId) + // { + // try + // { + // // 权限验证:检查当前用户是否有权限查看用户角色 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result>.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:read")) + // { + // return Result>.Failure("没有权限查看用户角色"); + // } + + // var roles = await _permissionCheckService.Get(userId); + // return Result>.Success(roles); + // } + // catch (Exception ex) + // { + // return Result>.Failure($"获取用户角色失败: {ex.Message}"); + // } + // } + + public async Task> CheckUserPermissionAsync(Guid userId, string permissionCode) + { + try + { + // 权限验证:检查当前用户是否有权限检查其他用户权限 + // var currentUserId = _currentUserContext.GetCurrentUserId(); + // if (!currentUserId.HasValue) + // { + // return Result.Failure("用户未登录"); + // } + + // if (!await _permissionCheckService.CheckUserPermissionAsync(currentUserId.Value, "user:read")) + // { + // return Result.Failure("没有权限检查用户权限"); + // } + + var hasPermission = await _permissionCheckService.CheckUserPermissionAsync(userId, permissionCode); + return Result.Success(hasPermission); + } + catch (Exception ex) + { + return Result.Failure($"检查用户权限失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserPermissionIntegrationService.cs b/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserPermissionIntegrationService.cs new file mode 100644 index 0000000..31c096e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/UserManagement/Service/UserPermissionIntegrationService.cs @@ -0,0 +1,142 @@ +// using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +// using UniversalAdminSystem.Domian.UserManagement.IRepository; +// using UniversalAdminSystem.Domian.PermissionManagement.IRepository; + +// namespace UniversalAdminSystem.Application.UserManagement.Service; + +// /// +// /// 用户权限集成服务 +// /// 提供用户管理与权限系统的集成功能 +// /// +// public class UserPermissionIntegrationService +// { +// private readonly IUserRepository _userRepository; +// private readonly IRoleRepository _roleRepository; +// private readonly IPermissionCheckService _permissionCheckService; + +// public UserPermissionIntegrationService( +// IUserRepository userRepository, +// IRoleRepository roleRepository, +// IPermissionCheckService permissionCheckService) +// { +// _userRepository = userRepository; +// _roleRepository = roleRepository; +// _permissionCheckService = permissionCheckService; +// } + +// /// +// /// 验证用户是否有管理其他用户的权限 +// /// +// public async Task CanManageUserAsync(Guid currentUserId, Guid targetUserId) +// { +// // 超级管理员可以管理所有用户 +// if (await IsSuperAdminAsync(currentUserId)) +// { +// return true; +// } + +// // 普通管理员只能管理普通用户 +// if (await IsAdminAsync(currentUserId)) +// { +// var targetUser = await _userRepository.GetByGuidAsync(targetUserId); +// return targetUser != null && !await IsAdminAsync(targetUserId); +// } + +// return false; +// } + +// /// +// /// 验证用户是否有删除其他用户的权限 +// /// +// public async Task CanDeleteUserAsync(Guid currentUserId, Guid targetUserId) +// { +// // 不能删除自己 +// if (currentUserId == targetUserId) +// { +// return false; +// } + +// // 超级管理员可以删除任何用户 +// if (await IsSuperAdminAsync(currentUserId)) +// { +// return true; +// } + +// // 普通管理员只能删除普通用户 +// if (await IsAdminAsync(currentUserId)) +// { +// var targetUser = await _userRepository.GetByGuidAsync(targetUserId); +// return targetUser != null && !await IsAdminAsync(targetUserId); +// } + +// return false; +// } + +// /// +// /// 验证用户是否有分配角色的权限 +// /// +// public async Task CanAssignRolesAsync(Guid currentUserId, Guid targetUserId, List roleIds) +// { +// // 超级管理员可以分配任何角色 +// if (await IsSuperAdminAsync(currentUserId)) +// { +// return true; +// } + +// // 普通管理员只能分配普通角色 +// if (await IsAdminAsync(currentUserId)) +// { +// var roles = await _roleRepository.GetByIdsAsync(roleIds); +// return roles.All(r => !r.Name.Value.Contains("Admin") && !r.Name.Value.Contains("Super")); +// } + +// return false; +// } + +// /// +// /// 检查是否为超级管理员 +// /// +// private async Task IsSuperAdminAsync(Guid userId) +// { +// var permissions = await _permissionCheckService.GetUserPermissionsAsync(userId); +// return permissions.Any(p => p.Contains("super") || p.Contains("system")); +// } + +// /// +// /// 检查是否为管理员 +// /// +// private async Task IsAdminAsync(Guid userId) +// { +// var roles = await _permissionCheckService.GetUserRolesAsync(userId); +// return roles.Any(r => r.Contains("Admin") || r.Contains("Manager")); +// } + +// /// +// /// 获取用户的权限级别 +// /// +// public async Task GetUserPermissionLevelAsync(Guid userId) +// { +// if (await IsSuperAdminAsync(userId)) +// { +// return UserPermissionLevel.SuperAdmin; +// } +// else if (await IsAdminAsync(userId)) +// { +// return UserPermissionLevel.Admin; +// } +// else +// { +// return UserPermissionLevel.User; +// } +// } +// } + +// /// +// /// 用户权限级别枚举 +// /// +// public enum UserPermissionLevel +// { +// User, // 普通用户 +// Admin, // 管理员 +// SuperAdmin // 超级管理员 +// } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/AggregateRoot.cs b/backend/src/UniversalAdminSystem.Domian/Core/AggregateRoot.cs new file mode 100644 index 0000000..f25d071 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/AggregateRoot.cs @@ -0,0 +1,8 @@ +namespace UniversalAdminSystem.Domian.Core; + +/// +/// 聚合根基类 +/// +public abstract class AggregateRoot +{ +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/DomainEvent.cs b/backend/src/UniversalAdminSystem.Domian/Core/DomainEvent.cs new file mode 100644 index 0000000..015f6ac --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/DomainEvent.cs @@ -0,0 +1,23 @@ +namespace UniversalAdminSystem.Domian.Core; + +/// +/// 领域事件基类 +/// 为所有领域事件提供统一的基础属性 +/// +public abstract class DomainEvent +{ + /// + /// 事件ID + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + /// 事件发生时间 + /// + public DateTime OccurredOn { get; } = DateTime.UtcNow; + + /// + /// 事件类型名称 + /// + public string EventType => GetType().Name; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/Events/PermissionEvents.cs b/backend/src/UniversalAdminSystem.Domian/Core/Events/PermissionEvents.cs new file mode 100644 index 0000000..273a140 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/Events/PermissionEvents.cs @@ -0,0 +1,78 @@ +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.Core.Events; + +/// +/// 权限创建事件 +/// +public class PermissionCreatedEvent : DomainEvent +{ + public PermissionId PermissionId { get; } + public string PermissionCode { get; } + + public PermissionCreatedEvent(PermissionId permissionId, string permissionCode) + { + PermissionId = permissionId; + PermissionCode = permissionCode; + } +} + +/// +/// 权限分配给角色事件 +/// +public class PermissionAssignedToRoleEvent : DomainEvent +{ + public RoleId RoleId { get; } + public PermissionId PermissionId { get; } + + public PermissionAssignedToRoleEvent(RoleId roleId, PermissionId permissionId) + { + RoleId = roleId; + PermissionId = permissionId; + } +} + +/// +/// 权限从角色移除事件 +/// +public class PermissionRemovedFromRoleEvent : DomainEvent +{ + public RoleId RoleId { get; } + public PermissionId PermissionId { get; } + + public PermissionRemovedFromRoleEvent(RoleId roleId, PermissionId permissionId) + { + RoleId = roleId; + PermissionId = permissionId; + } +} + +/// +/// 权限删除事件 +/// +public class PermissionDeletedEvent : DomainEvent +{ + public PermissionId PermissionId { get; } + public string PermissionCode { get; } + + public PermissionDeletedEvent(PermissionId permissionId, string permissionCode) + { + PermissionId = permissionId; + PermissionCode = permissionCode; + } +} + +/// +/// 权限变更事件 +/// +public class PermissionUpdatedEvent : DomainEvent +{ + public PermissionId PermissionId { get; } + public string PermissionCode { get; } + + public PermissionUpdatedEvent(PermissionId permissionId, string permissionCode) + { + PermissionId = permissionId; + PermissionCode = permissionCode; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/Events/RoleEvents.cs b/backend/src/UniversalAdminSystem.Domian/Core/Events/RoleEvents.cs new file mode 100644 index 0000000..c21d924 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/Events/RoleEvents.cs @@ -0,0 +1,50 @@ +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.Core.Events; + +/// +/// 角色创建事件 +/// +public class RoleCreatedEvent : DomainEvent +{ + public RoleId RoleId { get; } + public string RoleName { get; } + + public RoleCreatedEvent(RoleId roleId, string roleName) + { + RoleId = roleId; + RoleName = roleName; + } +} + +/// +/// 角色更新事件 +/// +public class RoleUpdatedEvent : DomainEvent +{ + public RoleId RoleId { get; } + public string OldName { get; } + public string NewName { get; } + + public RoleUpdatedEvent(RoleId roleId, string oldName, string newName) + { + RoleId = roleId; + OldName = oldName; + NewName = newName; + } +} + +/// +/// 角色删除事件 +/// +public class RoleDeletedEvent : DomainEvent +{ + public RoleId RoleId { get; } + public string RoleName { get; } + + public RoleDeletedEvent(RoleId roleId, string roleName) + { + RoleId = roleId; + RoleName = roleName; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/Events/UserEvents.cs b/backend/src/UniversalAdminSystem.Domian/Core/Events/UserEvents.cs new file mode 100644 index 0000000..c78aaf9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/Events/UserEvents.cs @@ -0,0 +1,66 @@ +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Domian.Core.Events; + +// /// +// /// 用户创建事件 +// /// +// public class UserCreatedEvent : DomainEvent +// { +// public UserId UserId { get; } +// public string Account { get; } + +// public UserCreatedEvent(UserId userId, string account) +// { +// UserId = userId; +// Account = account; +// } +// } + +// /// +// /// 用户角色分配事件 +// /// +// public class UserRoleAssignedEvent : DomainEvent +// { +// public UserId UserId { get; } +// public RoleId RoleId { get; } + +// public UserRoleAssignedEvent(UserId userId, RoleId roleId) +// { +// UserId = userId; +// RoleId = roleId; +// } +// } + +// /// +// /// 用户角色移除事件 +// /// +// public class UserRoleRemovedEvent : DomainEvent +// { +// public UserId UserId { get; } +// public RoleId RoleId { get; } + +// public UserRoleRemovedEvent(UserId userId, RoleId roleId) +// { +// UserId = userId; +// RoleId = roleId; +// } +// } + +/// +/// 用户状态变更事件 +/// +public class UserStatusChangedEvent : DomainEvent +{ + public UserId UserId { get; } + public UserStatus OldStatus { get; } + public UserStatus NewStatus { get; } + + public UserStatusChangedEvent(UserId userId, UserStatus oldStatus, UserStatus newStatus) + { + UserId = userId; + OldStatus = oldStatus; + NewStatus = newStatus; + } +} \ No newline at end of file diff --git a/backend/txt.txt b/backend/src/UniversalAdminSystem.Domian/Core/Exceptions/DomainException.cs similarity index 100% rename from backend/txt.txt rename to backend/src/UniversalAdminSystem.Domian/Core/Exceptions/DomainException.cs diff --git "a/backend/src/UniversalAdminSystem.Domian/Core/Exceptions/\351\242\206\345\237\237\345\274\202\345\270\270.txt" "b/backend/src/UniversalAdminSystem.Domian/Core/Exceptions/\351\242\206\345\237\237\345\274\202\345\270\270.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Domian/Core/Interfaces/IRepository.cs b/backend/src/UniversalAdminSystem.Domian/Core/Interfaces/IRepository.cs new file mode 100644 index 0000000..b5563bc --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/Interfaces/IRepository.cs @@ -0,0 +1,40 @@ +namespace UniversalAdminSystem.Domian.Core.Interfaces; + +/// +/// 通用仓储接口,提供通用的数据访问方法(CRUD) +/// +public interface IRepository where T : class +{ + /// + /// 新增 + /// + /// + /// + Task AddAsync(T entity); + + /// + /// 删除 + /// + /// + /// + Task RemoveAsync(Guid guid); + + /// + /// 更新 + /// + /// + /// + Task Update(T entity); + + /// + /// 查询所有 + /// + /// + Task> GetAllAsync(); + + /// + /// 查询指定 + /// + /// + Task GetByGuidAsync(Guid guid); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/PermissionId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/PermissionId.cs new file mode 100644 index 0000000..535f3a8 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/PermissionId.cs @@ -0,0 +1,22 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +/// +/// 权限ID值对象 +/// +public record PermissionId +{ + public Guid Value { get; init; } + + public PermissionId(Guid value) + { + Value = value; + } + + public static PermissionId Create(Guid value) + { + return new PermissionId(value); + } + + public static implicit operator Guid(PermissionId permissionId) => permissionId.Value; + public static implicit operator PermissionId(Guid value) => new(value); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/RoleId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/RoleId.cs new file mode 100644 index 0000000..a1e9ba0 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/RoleId.cs @@ -0,0 +1,19 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +public record RoleId +{ + public Guid Value { get; init; } + + private RoleId(Guid value) + { + if (Guid.Empty == value) + throw new ArgumentException("RoleId不能为空"); + + Value = value; + } + + public static RoleId Create(Guid value) => new(value); + + public static explicit operator RoleId(Guid value) => Create(value); + public static implicit operator Guid(RoleId roleId) => roleId.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserId.cs new file mode 100644 index 0000000..f7d5a44 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserId.cs @@ -0,0 +1,14 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +public record UserId +{ + public Guid Value { get; init; } + + public static UserId Create(Guid id) + { + return new UserId{ Value = id }; + } + + public static explicit operator UserId(Guid id) => Create(id); + public static implicit operator Guid(UserId userId) => userId.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserInfoId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserInfoId.cs new file mode 100644 index 0000000..0cc6764 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/UserInfoId.cs @@ -0,0 +1,50 @@ +using System; + +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +/// +/// 用户信息ID值对象 +/// +public class UserInfoId +{ + public Guid Value { get; private set; } + + private UserInfoId(Guid value) + { + Value = value; + } + + public static UserInfoId Create(Guid value) + { + return new UserInfoId(value); + } + + public static implicit operator Guid(UserInfoId userInfoId) + { + return userInfoId.Value; + } + + public static implicit operator UserInfoId(Guid value) + { + return new UserInfoId(value); + } + + public override bool Equals(object? obj) + { + if (obj is UserInfoId other) + { + return Value.Equals(other.Value); + } + return false; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public override string ToString() + { + return Value.ToString(); + } +} \ No newline at end of file diff --git "a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/\351\200\232\347\224\250\345\200\274\345\257\271\350\261\241.txt" "b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/\351\200\232\347\224\250\345\200\274\345\257\271\350\261\241.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Domian/DomainServices.cs b/backend/src/UniversalAdminSystem.Domian/DomainServices.cs new file mode 100644 index 0000000..6fe2454 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/DomainServices.cs @@ -0,0 +1,24 @@ +using System.Reflection; + +namespace UniversalAdminSystem.Domian; + +public class DomainServices +{ + public static IEnumerable<(Type ServiceType, Type ImplementationType)> DiscoverServices() + { + var domainAssembly = Assembly.GetExecutingAssembly(); + var serviceInterfaces = domainAssembly.GetTypes() + .Where(x => x.IsInterface && x.Name.EndsWith("DomainService")); + var serviceImplementations = domainAssembly.GetTypes() + .Where(x => x.IsClass && !x.IsAbstract && x.Name.EndsWith("DomainService")); + + foreach (var iface in serviceInterfaces) + { + var impl = serviceImplementations.FirstOrDefault(x => iface.IsAssignableFrom(x)); + if (impl != null) + { + yield return (iface, impl); + } + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs new file mode 100644 index 0000000..b05ab05 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs @@ -0,0 +1,85 @@ +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Domian.FileStorage.Aggregates; + +public class File : AggregateRoot +{ + public FileId Id { get; private set; } + public FileName Name { get; private set; } + public FilePath Path { get; private set; } + public FileSize Size { get; private set; } + public FileType Type { get; private set; } + public UserId OwnerId { get; private set; } + public DateTime UploadTime { get; private set; } + public bool IsFolder { get; private set; } + public FileId? ParentId { get; private set; } + public FileAccessLevel AccessLevel { get; private set; } + public string? SecurityCheckResult { get; private set; } + + private File() { } + + public static File Create( + FileName name, + FilePath path, + FileSize size, + FileType type, + UserId ownerId, + bool isFolder = false, + FileId? parentId = null, + FileAccessLevel accessLevel = FileAccessLevel.Private + ) + { + // if (!FileType.AllowedTypes.Contains(type.Value)) + // throw new ArgumentException("不支持的文件类型"); + if (size.Value > FileSize.MaxSize) + throw new ArgumentException($"文件大小不能超过{FileSize.MaxSize / 1024 / 1024}MB"); + return new File + { + Id = FileId.Create(), + Name = name, + Path = path, + Size = size, + Type = type, + OwnerId = ownerId, + UploadTime = DateTime.UtcNow, + IsFolder = isFolder, + ParentId = parentId, + AccessLevel = accessLevel + }; + } + + public void SetSecurityCheckResult(string result) + { + SecurityCheckResult = result; + } + + public void Rename(FileName newName) + { + Name = newName; + } + + public void Move(FileId? newParentId) + { + ParentId = newParentId; + } + + public void ChangeAccessLevel(FileAccessLevel accessLevel) + { + AccessLevel = accessLevel; + } + + public bool CanAccess(UserId userId, bool isSuperAdmin, bool isAdmin) + { + if (isSuperAdmin) return true; + if (isAdmin) + { + // 管理员可访问公开和受限文件 + return AccessLevel == FileAccessLevel.Public || AccessLevel == FileAccessLevel.Restricted || OwnerId == userId; + } + // 普通用户只能访问公开文件或自己拥有的文件 + return AccessLevel == FileAccessLevel.Public || OwnerId == userId; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/IRepository/IFileRepository.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/IRepository/IFileRepository.cs new file mode 100644 index 0000000..8ea664a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/IRepository/IFileRepository.cs @@ -0,0 +1,14 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +namespace UniversalAdminSystem.Domian.FileStorage.IRepository; + +public interface IFileRepository : IRepository +{ + Task> GetByOwnerAsync(UserId ownerId); + Task> GetByParentAsync(FileId? parentId); + Task> GetByTypeAsync(FileType type); + Task> GetFilesWithAccessAsync(UserId userId, FileAccessLevel accessLevel); + Task> GetAllFoldersAsync(UserId ownerId); +} diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs new file mode 100644 index 0000000..6e9e172 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs @@ -0,0 +1,6 @@ +namespace UniversalAdminSystem.Domian.FileStorage.Interface; + +public interface IFileDomainService +{ + bool CheckFileSecurity(out string result, string? name = null, string? type = null); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs new file mode 100644 index 0000000..b4b22d4 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs @@ -0,0 +1,19 @@ +using UniversalAdminSystem.Domian.FileStorage.Interface; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +namespace UniversalAdminSystem.Domian.FileStorage.Services; + +public class FileDomainService : IFileDomainService +{ + public bool CheckFileSecurity(out string result, string? name = null, string? type = null) + { + if (name != null && name.Contains("..")) + { + result = "文件名非法"; + return false; + } + + result = "安全"; + return true; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs new file mode 100644 index 0000000..7941d9a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs @@ -0,0 +1,8 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public enum FileAccessLevel +{ + Private = 0, + Public = 1, + Restricted = 2 +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileId.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileId.cs new file mode 100644 index 0000000..6ba91d8 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileId.cs @@ -0,0 +1,11 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public record FileId +{ + public Guid Value { get; init; } + private FileId(Guid value) { Value = value; } + public static FileId Create() => new(Guid.NewGuid()); + public static FileId Create(Guid value) => new(value); + public static implicit operator Guid(FileId id) => id.Value; + public static implicit operator FileId(Guid value) => new(value); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileName.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileName.cs new file mode 100644 index 0000000..303662e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileName.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public record FileName +{ + public string Value { get; } + private FileName(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("文件名不能为空"); + if (value.Length > 200) + throw new ArgumentException("文件名不能超过200字符"); + Value = value; + } + public static FileName Create(string value) => new(value); + public static implicit operator string(FileName name) => name.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FilePath.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FilePath.cs new file mode 100644 index 0000000..e7b71c3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FilePath.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public record FilePath +{ + public string Value { get; } + private FilePath(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("文件路径不能为空"); + if (value.Length > 500) + throw new ArgumentException("文件路径不能超过500字符"); + Value = value; + } + public static FilePath Create(string value) => new(value); + public static implicit operator string(FilePath path) => path.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileSize.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileSize.cs new file mode 100644 index 0000000..15a50ef --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileSize.cs @@ -0,0 +1,17 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public record FileSize +{ + public long Value { get; } + public const long MaxSize = 50 * 1024 * 1024; // 50MB + private FileSize(long value) + { + if (value < 0) + throw new ArgumentException("文件大小不能为负数"); + if (value > MaxSize) + throw new ArgumentException($"文件大小不能超过{MaxSize / 1024 / 1024}MB"); + Value = value; + } + public static FileSize Create(long value) => new(value); + public static implicit operator long(FileSize size) => size.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs new file mode 100644 index 0000000..f29af26 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs @@ -0,0 +1,17 @@ +namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; + +public record FileType +{ + public string Value { get; } + public static readonly string[] AllowedTypes = new[] { "jpg", "jpeg", "png", ".gif", ".bmp", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".txt", ".zip", ".rar" }; + private FileType(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("文件类型不能为空"); + // if (!AllowedTypes.Contains(value.ToLower())) + // throw new ArgumentException($"不支持的文件类型: {value}"); + Value = value.ToLower(); + } + public static FileType Create(string value) => new(value); + public static implicit operator string(FileType type) => type.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/LogManagement/Aggregates/LogEntry.cs b/backend/src/UniversalAdminSystem.Domian/LogManagement/Aggregates/LogEntry.cs new file mode 100644 index 0000000..b45f15d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/LogManagement/Aggregates/LogEntry.cs @@ -0,0 +1,51 @@ +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.LogManagement.Aggregates; + +/// +/// 日志条目聚合根 +/// +public class LogEntry : AggregateRoot +{ + public Guid Id { get; private set; } + public string Level { get; private set; } + public string Message { get; private set; } + public string Source { get; private set; } + public UserId? UserId { get; private set; } + public DateTime Timestamp { get; private set; } + public string? Context { get; private set; } + public string? Exception { get; private set; } + + private LogEntry() { } + + public static LogEntry Create( + string level, + string message, + string source, + UserId? userId = null, + string? context = null, + string? exception = null) + { + if (string.IsNullOrWhiteSpace(level)) + throw new ArgumentException("日志级别不能为空", nameof(level)); + + if (string.IsNullOrWhiteSpace(message)) + throw new ArgumentException("日志消息不能为空", nameof(message)); + + if (string.IsNullOrWhiteSpace(source)) + throw new ArgumentException("日志来源不能为空", nameof(source)); + + return new LogEntry + { + Id = Guid.NewGuid(), + Level = level, + Message = message, + Source = source, + UserId = userId, + Timestamp = DateTime.UtcNow, + Context = context, + Exception = exception + }; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/LogManagement/IRepository/ILogEntryRepository.cs b/backend/src/UniversalAdminSystem.Domian/LogManagement/IRepository/ILogEntryRepository.cs new file mode 100644 index 0000000..02fe2d6 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/LogManagement/IRepository/ILogEntryRepository.cs @@ -0,0 +1,12 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.LogManagement.Aggregates; + +namespace UniversalAdminSystem.Domian.LogManagement.IRepository; + +public interface ILogEntryRepository : IRepository +{ + Task> GetByLevelAsync(string level); + Task> GetByUserAsync(Guid userId); + Task> GetByDateRangeAsync(DateTime start, DateTime end); + Task> GetBySourceAsync(string source); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Permission.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Permission.cs new file mode 100644 index 0000000..3b6d879 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Permission.cs @@ -0,0 +1,220 @@ +using UniversalAdminSystem.Domian.PermissionManagement.Services; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.Events; +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.PermissionManagement.Aggregate; + +/// +/// 权限实体,一个具体的权限项 +/// +public class Permission : AggregateRoot +{ + /// + /// 唯一标识 + /// + public Guid PermissionId { get; private set; } + + /// + /// 权限编码(不能为空,不变性):提供机器可读的权限标识,用于程序中的权限检查 + /// + public PermissionCode Code { get; private init; } = null!; + + /// + /// 权限名称(不能为空) + /// + public PermissionName Name { get; private set; } = null!; + + /// + /// 描述:提供人类可读的权限描述,用于界面展示 + /// + public string? Description { get; private set; } + + /// + /// 类型:分类权限,便于管理和使用 + /// + public PermissionType PermissionType { get; private set; } + + /// + /// 作用资源:指明该权限控制的是哪个资源 + /// + public PermissionResource Resource { get; private set; } = null!; + + /// + /// 操作类型:指明对资源的具体操作 + /// + public PermissionAction Action { get; private set; } + + /// + /// 是否为系统权限(默认为false):系统权限不可删除,不可修改 + /// + public bool IsSystem { get; private set; } = false; + + /// + /// 创建时间:创建权限时的当前事件,不可修改 + /// + public DateTime CreateTime { get; private set; } = DateTime.UtcNow; + + /// + /// 更新时间:更新权限时的当前时间,每次更新权限时都会更新 + /// + public DateTime UpdateTime { get; private set; } = DateTime.UtcNow; + + + /// + /// 无参构造函数,供EFCore使用 + /// + private Permission() { } + + /// + /// 私有构造函数供工厂使用 + /// + /// + /// + private Permission( + Guid permissionId, + PermissionCode code, + PermissionName name, + string? description, + PermissionType permissionType, + PermissionResource resource, + PermissionAction action, + bool isSystem, + DateTime createTime, + DateTime updateTime + ) + { + PermissionId = permissionId; + Code = code; + Name = name; + Description = description; + PermissionType = permissionType; + Resource = resource; + Action = action; + IsSystem = isSystem; + CreateTime = createTime; + UpdateTime = updateTime; + } + + /// + /// 根据权限作用资源获取权限类型 + /// + /// + /// + private static PermissionType GetTypeByResource(string resource) + { + return resource switch + { + string r when r.StartsWith("data.", StringComparison.OrdinalIgnoreCase) => PermissionType.DATA, + string r when r.StartsWith("user.", StringComparison.OrdinalIgnoreCase) => PermissionType.USER, + string r when r.StartsWith("file.", StringComparison.OrdinalIgnoreCase) => PermissionType.FILE, + string r when r.StartsWith("enum.", StringComparison.OrdinalIgnoreCase) => PermissionType.ENUN, + string r when r.StartsWith("system.", StringComparison.OrdinalIgnoreCase) => PermissionType.SYSTEM, + _ => PermissionType.OTHER, + }; + } + + /// + /// 创建标准权限的工厂方法 + /// + /// + /// + /// + /// + /// + public static Permission CreateStandardPermission( + string name, + string resource, + int actionValue, + string? description = null + ) + { + // 校验数据完整性 + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("权限名称不能为空", nameof(name)); + if (string.IsNullOrWhiteSpace(resource)) + throw new ArgumentException("作用资源不能为空", nameof(resource)); + if (!Enum.IsDefined(typeof(PermissionAction), actionValue)) + throw new ArgumentException("无效的操作类型值", nameof(actionValue)); + + if (!ResourceActionValidator.IsValidCombination(resource, actionValue, out var permResource, out var permAction)) + throw new ArgumentException("资源与操作类型组合不合法"); + + // 显示转换类型,内部校验格式 + PermissionCode permCode = (PermissionCode)$"{resource}:{permAction}"; + PermissionName permName = (PermissionName)name; + PermissionType permType = GetTypeByResource(resource); + + // 创建标准权限 + var permission = new Permission( + Guid.NewGuid(), + permCode, + permName, + string.IsNullOrWhiteSpace(description) ? "暂无描述" : description, + permType, + permResource, + permAction, + false, + DateTime.UtcNow, + DateTime.UtcNow + ); + + return permission; + } + + /// + /// 创建系统权限的工厂方法 + /// + /// + /// + /// + /// + /// + public static Permission CreateSystemPermission( + string name, + string resource, + int actionValue, + string? description = null + ) + { + var perm = CreateStandardPermission(name, resource, actionValue, description); + perm.IsSystem = true; + return perm; + } + + /// + /// 修改权限名称 + /// + /// + /// + public void UpdateName(string name) + { + Name = PermissionName.Create(name); + UpdateTime = DateTime.Now; + } + + /// + /// 修改权限描述 + /// + /// + /// + public void UpdateDescription(string? description = null) + { + // 允许传入null表示清空权限描述 + if (string.IsNullOrWhiteSpace(description)) + { + Description = "暂无描述"; + return; + } + + // 参数非null才进行规则校验 + if (description.Length > 50) + { + throw new ArgumentException("权限描述长度不能超过50字符", nameof(description)); + } + + Description = description; + UpdateTime = DateTime.Now; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Role.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Role.cs new file mode 100644 index 0000000..56b9148 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Aggregate/Role.cs @@ -0,0 +1,186 @@ +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Domian.PermissionManagement.Aggregate; + +public class Role : AggregateRoot +{ + public RoleId RoleId { get; private set; } = null!; + public RoleName Name { get; private set; } = null!; + public RoleDescription? Description { get; private set; } + public bool IsSystem { get; private set; } = false; + public bool IsSupper { get; private set; } = false; + public DateTime CreateTime { get; private set; } = DateTime.UtcNow; + public DateTime UpdateTime { get; private set; } = DateTime.UtcNow; + private readonly List _permissions = []; + public IReadOnlyCollection Permissions => _permissions.AsReadOnly(); + public IReadOnlyCollection PermissionIds => _permissions.Select(p => p.PermissionId).ToList().AsReadOnly(); + + private Role() { } + + private Role(RoleName name, RoleDescription? description = null, bool isSystem = false, bool isSupper = false) + { + RoleId = RoleId.Create(Guid.NewGuid()); + Name = name; + Description = description ?? RoleDescription.Create("暂无描述"); + IsSystem = isSystem; + IsSupper = isSupper; + CreateTime = DateTime.UtcNow; + UpdateTime = DateTime.UtcNow; + _permissions = new List(); + } + + /// + /// 角色工厂方法(支持描述、系统标记、超级管理员标记) + /// + public static Role Create(string name, string? description = null, bool isSystem = false, bool isSupper = false) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("角色名称不能为空"); + var role = new Role(RoleName.Create(name), string.IsNullOrWhiteSpace(description) ? null : RoleDescription.Create(description), isSystem, isSupper); + + return role; + } + + /// + /// 设置名称 + /// + /// + /// + public void SetName(string name) + { + Name = RoleName.Create(name); + } + + /// + /// 设置描述 + /// + /// + public void SetDescription(string? description) + { + Description = string.IsNullOrWhiteSpace(description) ? null : RoleDescription.Create(description); + } + + /// + /// 标记为超级管理员 + /// + public void MarkAsSupper() + { + IsSupper = true; + } + + /// + /// 添加权限 + /// + /// 权限实体 + /// + public void AddPermission(Permission permission) + { + if (permission == null) + throw new ArgumentException("权限不能为空", nameof(permission)); + + if (!HasPermission(permission.PermissionId)) + { + _permissions.Add(permission); + UpdateTime = DateTime.UtcNow; + + // 添加权限分配事件 + } + } + + /// + /// 移除权限 + /// + /// 权限实体 + /// + /// + /// + public void RemovePermission(Permission permission) + { + if (permission == null) + throw new ArgumentException("权限不能为空", nameof(permission)); + + var existingPermission = _permissions.FirstOrDefault(p => p.PermissionId == permission.PermissionId); + if (existingPermission != null) + { + _permissions.Remove(existingPermission); + UpdateTime = DateTime.UtcNow; + + // 添加权限移除事件 + } + } + + /// + /// 批量分配权限 + /// + public void AddPermissions(IEnumerable permissions) + { + foreach (var permission in permissions) + { + AddPermission(permission); + } + } + + /// + /// 批量移除权限 + /// + public void RemovePermissions(IEnumerable permissions) + { + foreach (var permission in permissions) + { + RemovePermission(permission); + } + } + + /// + /// 校验角色是否拥有某权限 + /// + public bool HasPermission(Guid permissionId) + { + return _permissions.Any(p => p.PermissionId == permissionId); + } + + /// + /// 校验角色是否拥有全部指定权限 + /// + public bool HasAllPermissions(IEnumerable permissionIds) + { + return permissionIds.All(pid => _permissions.Any(p => p.PermissionId == pid)); + } + + /// + /// 校验角色是否拥有任意指定权限 + /// + public bool HasAnyPermission(IEnumerable permissionIds) + { + return permissionIds.Any(pid => _permissions.Any(p => p.PermissionId == pid)); + } + + /// + /// 检查角色是否有指定权限编码(通过权限ID匹配) + /// + /// 权限编码 + /// 对应的权限ID + /// 是否有权限 + public bool HasPermission(string permissionCode, Guid permissionId) + { + // 这里需要根据权限编码找到对应的权限ID + // 在实际使用中,可能需要通过权限服务来获取权限ID + return HasPermission(permissionId); + } + + /// + /// 清空所有权限 + /// + public void ClearPermissions() + { + _permissions.Clear(); + UpdateTime = DateTime.UtcNow; + } + + /// + /// 获取权限数量 + /// + public int PermissionCount => _permissions.Count; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Exceptions/PermissionDomainException.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Exceptions/PermissionDomainException.cs new file mode 100644 index 0000000..9f1965e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Exceptions/PermissionDomainException.cs @@ -0,0 +1,8 @@ +namespace UniversalAdminSystem.Domian.PermissionManagement.Exceptions; + +public class PermissionDomainException : Exception +{ + public PermissionDomainException(string message) : base(message) { } + + public PermissionDomainException(string message, Exception innerException) : base(message, innerException) { } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IPermissionRepository.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IPermissionRepository.cs new file mode 100644 index 0000000..930892f --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IPermissionRepository.cs @@ -0,0 +1,55 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Domian.PermissionManagement.IRepository; + +/// +/// 权限仓储接口 +/// 提供权限数据的持久化操作 +/// +public interface IPermissionRepository : IRepository +{ + /// + /// 批量添加系统权限 + /// + /// 系统权限集合 + /// 添加操作结果 + Task AddSystemPermissionsAsync(IEnumerable systemPermissions); + + /// + /// 根据权限ID集合获取权限集合 + /// + /// 权限ID集合 + /// 权限集合 + Task> GetByIdsAsync(IEnumerable permissionIds); + + /// + /// 根据权限编码获取权限 + /// + /// 权限编码 + /// 权限实体,如果不存在则返回null + Task GetByCodeAsync(PermissionCode permissionCode); + + /// + /// 根据条件查询权限 + /// + /// 权限名称(可选) + /// 权限资源(可选) + /// 权限操作类型(可选) + /// 权限类型(可选) + /// 符合条件的权限集合 + Task> QueryAsync( + string? name = null, + string? resource = null, + int? action = null, + PermissionType? type = null + ); + + /// + /// 根据权限编码判断权限是否存在 + /// + /// 权限编码 + /// 权限是否存在 + Task ExistsAsync(PermissionCode permissionCode); +} diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IRoleRepository.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IRoleRepository.cs new file mode 100644 index 0000000..d70e255 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/IRepository/IRoleRepository.cs @@ -0,0 +1,52 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; + +namespace UniversalAdminSystem.Domian.PermissionManagement.IRepository; + +/// +/// 角色仓储接口 +/// 提供角色数据的持久化操作 +/// +public interface IRoleRepository : IRepository +{ + /// + /// 根据角色名称查找角色 + /// + /// 角色名称 + /// 角色实体,如果不存在则返回null + Task GetByNameAsync(string roleName); + + /// + /// 根据角色ID集合获取角色列表 + /// + /// 角色ID集合 + /// 角色集合 + Task> GetByIdsAsync(IEnumerable roleIds); + + /// + /// 查询角色下的所有权限ID + /// + /// 角色ID + /// 权限ID集合 + Task> GetPermissionIdsAsync(Guid roleId); + + /// + /// 根据角色名称判断角色是否存在 + /// + /// 角色名称 + /// 角色是否存在 + Task ExistsAsync(string roleName); + + /// + /// 获取角色及其权限(包含权限导航属性) + /// + /// 角色ID + /// 角色实体,如果不存在则返回null + Task GetRoleWithPermissionsAsync(Guid roleId); + + /// + /// 获取所有角色及其权限 + /// + /// 角色集合 + Task> GetAllRolesWithPermissionsAsync(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Interfaces/IAssignPermissionDomainService.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Interfaces/IAssignPermissionDomainService.cs new file mode 100644 index 0000000..e398a93 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Interfaces/IAssignPermissionDomainService.cs @@ -0,0 +1,42 @@ +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; + +namespace UniversalAdminSystem.Domian.PermissionManagement.Interfaces; + +/// +/// 权限分配领域服务接口 +/// 提供权限分配给角色的领域业务逻辑 +/// +public interface IAssignPermissionDomainService +{ + /// + /// 为角色分配权限 + /// + /// 权限实体 + /// 角色实体 + /// 分配是否成功 + bool AssignPermission(Permission permission, Role role); + + /// + /// 为角色移除权限 + /// + /// 权限实体 + /// 角色实体 + /// 移除是否成功 + bool RemovePermission(Permission permission, Role role); + + /// + /// 批量分配权限 + /// + /// 权限集合 + /// 角色实体 + /// 分配是否成功 + bool AssignPermissions(IEnumerable permissions, Role role); + + /// + /// 批量移除权限 + /// + /// 权限集合 + /// 角色实体 + /// 移除是否成功 + bool RemovePermissions(IEnumerable permissions, Role role); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/AssignPermissionDomainService.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/AssignPermissionDomainService.cs new file mode 100644 index 0000000..a1b27c0 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/AssignPermissionDomainService.cs @@ -0,0 +1,87 @@ +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.Interfaces; + +namespace UniversalAdminSystem.Domian.PermissionManagement.Services; + +/// +/// 权限分配领域服务实现 +/// 提供权限分配给角色的领域业务逻辑实现 +/// +public class AssignPermissionDomainService : IAssignPermissionDomainService +{ + /// + /// 为角色分配权限 + /// + /// 权限实体 + /// 角色实体 + /// 分配是否成功 + public bool AssignPermission(Permission permission, Role role) + { + try + { + if (role == null) + throw new InvalidOperationException("权限分配失败!该角色不存在"); + + if (permission == null) + throw new InvalidOperationException("权限分配失败!该权限不存在"); + + role.AddPermission(permission); + return true; + } + catch (ArgumentException) + { + throw; + } + } + + public bool RemovePermission(Permission permission, Role role) + { + try + { + if (role == null) + throw new InvalidOperationException("权限移除失败!该角色不存在"); + if (permission == null) + throw new InvalidOperationException("权限移除失败!该权限不存在"); + role.RemovePermission(permission); + return true; + } + catch (ArgumentException) + { + throw; + } + } + + public bool AssignPermissions(IEnumerable permissions, Role role) + { + try + { + if (role == null) + throw new InvalidOperationException("权限分配失败!该角色不存在"); + if (permissions == null) + throw new InvalidOperationException("权限分配失败!权限集合为空"); + role.AddPermissions(permissions); + return true; + } + catch (ArgumentException) + { + throw; + } + } + + public bool RemovePermissions(IEnumerable permissions, Role role) + { + try + { + if (role == null) + throw new InvalidOperationException("权限移除失败!该角色不存在"); + if (permissions == null) + throw new InvalidOperationException("权限移除失败!权限集合为空"); + role.RemovePermissions(permissions); + return true; + } + catch (ArgumentException) + { + throw; + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/ResourceActionValidator.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/ResourceActionValidator.cs new file mode 100644 index 0000000..28c9901 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/Services/ResourceActionValidator.cs @@ -0,0 +1,52 @@ +using UniversalAdminSystem.Domian.PermissionManagement.Exceptions; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Domian.PermissionManagement.Services; + +/// +/// 资源-操作组合校验器 +/// 职责:验证资源与操作类型的组合是否合法 +/// +public class ResourceActionValidator +{ + private static readonly object _lock = new object(); + private static Dictionary> _validResources = new(); + + /// + /// 加载权限规则配置 + /// + public static void LoadRules(Dictionary> rules) + { + lock (_lock) + { + _validResources = rules.ToDictionary(kv => kv.Key, kv => kv.Value.ToHashSet()); + } + } + + /// + /// 获取当前配置的规则 + /// + public static Dictionary> GetCurrentRules() + { + lock (_lock) + { + return _validResources.ToDictionary(kv => kv.Key, kv => kv.Value.ToHashSet()); + } + } + + /// + /// 验证资源-操作组合有效性 + /// + public static bool IsValidCombination(string resource, int actionValue, out PermissionResource permSource, out PermissionAction permAction) + { + lock (_lock) + { + permSource = PermissionResource.Create(resource); + var resourceModule = permSource.GetModule(); + if (!Enum.TryParse(actionValue.ToString(), out PermissionAction validAction)) + throw new PermissionDomainException("无效的操作类型值"); + permAction = validAction; + return _validResources.TryGetValue(validAction, out var validModules) && validModules.Contains(resourceModule); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs new file mode 100644 index 0000000..7eefe34 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs @@ -0,0 +1,64 @@ +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public enum PermissionAction +{ + // 基础操作 + Read, // 读取资源 + Create, // 创建资源 + Update, // 更新资源 + Delete, // 删除资源 + + // 管理操作 + Manage, // 完全管理 + Approve, // 审批操作 + Audit, // 审计访问 + + // 特殊操作 + Execute, // 执行操作 + Import, // 导入数据 + Export, // 导出数据 + Restore, // 恢复数据 + Archive // 归档数据 +} + +/// +/// 扩展方法静态类 +/// +public static class PermissionActionExtensions +{ + /// + /// 获取操作类型的分类 + /// + /// + /// + public static string GetCategory(this PermissionAction action) + { + return action switch + { + PermissionAction.Read or + PermissionAction.Create or + PermissionAction.Update or + PermissionAction.Delete + => "Basic", + + PermissionAction.Manage or + PermissionAction.Approve or + PermissionAction.Audit + => "Management", + + _ + => "Special" + }; + } + + /// + /// 检查操作是否允许批量执行 + /// + /// + /// + public static bool AllowBulk(this PermissionAction action) + { + return action != PermissionAction.Manage && + action != PermissionAction.Audit; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionCode.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionCode.cs new file mode 100644 index 0000000..2f782ec --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionCode.cs @@ -0,0 +1,89 @@ +using System.Text.RegularExpressions; + +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public sealed partial class PermissionCode +{ + public string Value { get; private set; } + + private PermissionCode(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("权限编码不能为空", nameof(value)); + + if (!PermissionRegex().IsMatch(value)) + throw new ArgumentException("权限编码格式错误,应为'资源:操作'格式", nameof(value)); + + Value = value; + } + + [GeneratedRegex(@"^([a-z]+(\.[a-z]+(_[a-z]+)*){0,4})+:[a-zA-Z0-9]+$")] + private static partial Regex PermissionRegex(); + + /// + /// 工厂方法 + /// + /// + /// + public static PermissionCode Create(string value) {System.Console.WriteLine($"value: {value}"); return new PermissionCode(value);} + + /// + /// 隐式转换为string + /// + /// + public static implicit operator string(PermissionCode code) => code.Value; + + /// + /// 显式转换为PermissionCode,带验证逻辑 + /// + /// + public static explicit operator PermissionCode(string value) => Create(value); + + /// + /// 比较两个权限编码是否相等 + /// + /// + /// + public bool Equals(PermissionCode? other) => + other is not null && Value == other.Value; + + /// + /// 比较两个权限编码是否相等 + /// + /// + /// + public override bool Equals(object? obj) => + obj is PermissionCode other && Equals(other); + + /// + /// 获取哈希值 + /// + /// + public override int GetHashCode() => Value.GetHashCode(); + + /// + /// 获取字符串表示形式 + /// + /// + public override string ToString() => Value; + + /// + /// 比较两个权限编码是否相等 + /// + /// + /// + /// + public static bool operator ==(PermissionCode? left, PermissionCode? right) => + Equals(left, right); + + /// + /// 比较两个权限编码是否不相等 + /// + /// + /// + /// + public static bool operator !=(PermissionCode? left, PermissionCode? right) => + !Equals(left, right); + + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionName.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionName.cs new file mode 100644 index 0000000..dc1587a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionName.cs @@ -0,0 +1,54 @@ +using System.Text.RegularExpressions; + +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public sealed partial class PermissionName +{ + public string Value { get; private set; } = null!; + + private PermissionName(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("权限名称不能为空", nameof(value)); + } + + if (!MyRegex().IsMatch(value)) + { + throw new ArgumentException("权限名称只能包含中文、英文、数字、下划线和连字符", nameof(value)); + } + + if (value.Length > 20) + { + throw new ArgumentException("权限名称长度不能超过20个字符", nameof(value)); + } + + Value = value; + } + + /// + /// 正则表达式:格式校验规则 + /// + /// + [GeneratedRegex(@"^[\p{IsCJKUnifiedIdeographs}a-zA-Z0-9_-]{1,20}$")] + private static partial Regex MyRegex(); + + /// + /// 工厂方法 + /// + /// + /// + public static PermissionName Create(string value) => new(value); + + /// + /// 隐式转换为string + /// + /// + public static implicit operator string(PermissionName name) => name.Value; + + /// + /// 显式转换为PermissionName,带验证逻辑 + /// + /// + public static explicit operator PermissionName(string value) => Create(value); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionResource.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionResource.cs new file mode 100644 index 0000000..bbed098 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionResource.cs @@ -0,0 +1,66 @@ +using System.Text.RegularExpressions; + +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public partial class PermissionResource +{ + public string Value { get; private set; } = null!; + + private PermissionResource(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("资源名称不能为空", nameof(value)); + } + + if (!ENRegex().IsMatch(value)) + { + throw new ArgumentException("资源名称只能包含英文(小写)、下划线,以 “.” 结构分层,最多支持五层。例如:hantsune.m_i_k_u或初音.未_来", nameof(value)); + } + + Value = value; + } + + // [GeneratedRegex(@"^[\p{IsCJKUnifiedIdeographs}]+(\.[\p{IsCJKUnifiedIdeographs}]+(_[\p{IsCJKUnifiedIdeographs}]+)*){1,4}$", RegexOptions.Compiled)] + // private static partial Regex CNRegex(); + + [GeneratedRegex(@"^[a-z]+(\.[a-z]+(_[a-z]+)*){0,4}$", RegexOptions.Compiled)] + private static partial Regex ENRegex(); + + /// + /// 工厂方法 + /// + /// + /// + public static PermissionResource Create(string value) => new(value); + + /// + /// 隐式转换string + /// + /// + public static implicit operator string(PermissionResource resource) => resource.Value; + + /// + /// 显示转换PermissionResource,带验证逻辑 + /// + /// + public static explicit operator PermissionResource(string resource) => Create(resource); + + /// + /// 解析资源层级 + /// + /// + public IReadOnlyList GetResourceParts() + { + return Value.Split('.'); + } + + /// + /// 获取顶级模块 + /// + /// + public string GetModule() + { + return Value.Split('.')[0]; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionType.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionType.cs new file mode 100644 index 0000000..c43aa16 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionType.cs @@ -0,0 +1,34 @@ +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public enum PermissionType +{ + /// + /// 菜单 + /// + ENUN, + + /// + /// 数据 + /// + DATA, + + /// + /// 文件 + /// + FILE, + + /// + /// 用户 + /// + USER, + + /// + /// 系统 + /// + SYSTEM, + + /// + /// 其他 + /// + OTHER +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleDescription.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleDescription.cs new file mode 100644 index 0000000..e13d84b --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleDescription.cs @@ -0,0 +1,14 @@ +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public record RoleDescription +{ + public string? Value { get; } + private RoleDescription(string? value) + { + if (value?.Length > 200) + throw new ArgumentException("角色描述不能超过200个字符"); + Value = value; + } + public static RoleDescription Create(string? value) => new(value); + public static implicit operator string?(RoleDescription desc) => desc.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleName.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleName.cs new file mode 100644 index 0000000..76ffbe4 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/RoleName.cs @@ -0,0 +1,18 @@ +namespace UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +public record RoleName +{ + public string Value { get; private set; } + private RoleName(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("角色名称不能为空"); + if (value.Length > 50) + throw new ArgumentException("角色名称不能超过50个字符"); + Value = value; + } + public static RoleName Create(string value) => new(value); + + public static explicit operator RoleName(string value) => Create(value); + public static implicit operator string(RoleName name) => name.Value; +} \ No newline at end of file diff --git "a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/\346\235\203\351\231\220\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" "b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/\346\235\203\351\231\220\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Domian/SystemSettings/Aggregates/SystemSetting.cs b/backend/src/UniversalAdminSystem.Domian/SystemSettings/Aggregates/SystemSetting.cs new file mode 100644 index 0000000..f59732e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/SystemSettings/Aggregates/SystemSetting.cs @@ -0,0 +1,36 @@ +using System; +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.SystemSettings.ValueObjects; + +namespace UniversalAdminSystem.Domian.SystemSettings.Aggregates; + +public class SystemSetting : AggregateRoot +{ + public Guid Id { get; private set; } + public SettingKey Key { get; private set; } = null!; + public SettingValue Value { get; private set; } = null!; + public SettingDescription? Description { get; private set; } + public string? Group { get; private set; } + public DateTime CreateTime { get; private set; } + public DateTime UpdateTime { get; private set; } + + private SystemSetting() { } + public static SystemSetting Create(SettingKey key, SettingValue value, SettingDescription? description = null, string? group = null) + { + return new SystemSetting + { + Id = Guid.NewGuid(), + Key = key, + Value = value, + Description = description, + Group = group, + CreateTime = DateTime.UtcNow, + UpdateTime = DateTime.UtcNow + }; + } + public void UpdateValue(SettingValue value) + { + Value = value; + UpdateTime = DateTime.UtcNow; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/SystemSettings/IRepository/ISystemSettingRepository.cs b/backend/src/UniversalAdminSystem.Domian/SystemSettings/IRepository/ISystemSettingRepository.cs new file mode 100644 index 0000000..97078d9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/SystemSettings/IRepository/ISystemSettingRepository.cs @@ -0,0 +1,10 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.SystemSettings.Aggregates; + +namespace UniversalAdminSystem.Domian.SystemSettings.IRepository; + +public interface ISystemSettingRepository : IRepository +{ + Task GetByKeyAsync(string key); + Task> GetByGroupAsync(string group); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingDescription.cs b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingDescription.cs new file mode 100644 index 0000000..f7ac995 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingDescription.cs @@ -0,0 +1,14 @@ +namespace UniversalAdminSystem.Domian.SystemSettings.ValueObjects; + +public record SettingDescription +{ + public string? Value { get; } + private SettingDescription(string? value) + { + if (value?.Length > 200) + throw new ArgumentException("设置描述不能超过200字符"); + Value = value; + } + public static SettingDescription Create(string? value) => new(value); + public static implicit operator string?(SettingDescription desc) => desc.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingKey.cs b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingKey.cs new file mode 100644 index 0000000..eda4e82 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingKey.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.SystemSettings.ValueObjects; + +public record SettingKey +{ + public string Value { get; } + private SettingKey(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("设置Key不能为空"); + if (value.Length > 100) + throw new ArgumentException("设置Key不能超过100字符"); + Value = value; + } + public static SettingKey Create(string value) => new(value); + public static implicit operator string(SettingKey key) => key.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingValue.cs b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingValue.cs new file mode 100644 index 0000000..fed3c28 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/SystemSettings/ValueObjects/SettingValue.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.SystemSettings.ValueObjects; + +public record SettingValue +{ + public string Value { get; } + private SettingValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("设置值不能为空"); + if (value.Length > 500) + throw new ArgumentException("设置值不能超过500字符"); + Value = value; + } + public static SettingValue Create(string value) => new(value); + public static implicit operator string(SettingValue val) => val.Value; +} \ No newline at end of file diff --git "a/backend/src/UniversalAdminSystem.Domian/SystemSettings/\347\263\273\347\273\237\350\256\276\347\275\256\344\270\212\344\270\213\346\226\207.txt" "b/backend/src/UniversalAdminSystem.Domian/SystemSettings/\347\263\273\347\273\237\350\256\276\347\275\256\344\270\212\344\270\213\346\226\207.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Domian/UniversalAdminSystem.Domian.csproj b/backend/src/UniversalAdminSystem.Domian/UniversalAdminSystem.Domian.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UniversalAdminSystem.Domian.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/Aggregates/User.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/Aggregates/User.cs new file mode 100644 index 0000000..4d36edf --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/Aggregates/User.cs @@ -0,0 +1,123 @@ +using System.Dynamic; +using System.Text.RegularExpressions; +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Domian.UserManagement.Aggregates; + +public partial class User : AggregateRoot +{ + public UserId UserId { get; private set; } = null!; + public UserAccount Account { get; private set; } = null!; + public string Password { get; private set; } = null!; + public string Salt { get; private set; } = null!; + public UserEmail Email { get; private set; } = null!; + public UserStatus Status { get; private set; } + public UserInfoId? UserInfoId { get; private set; } + + public RoleId? RoleId { get; private set; } + + private User() { } + + private User( + UserInfoId userInfoId, + UserAccount account, + string password, + UserEmail email, + string salt, + UserStatus userStatus, + RoleId? role = null + ) + { + UserId = UserId.Create(Guid.NewGuid()); + UserInfoId = userInfoId; + Account = account; + Password = password; + Email = email; + Salt = salt; + Status = userStatus; + RoleId = role; + } + + public static User CreateUser(UserInfoId userInfoId, string account, string password, string email, string salt, UserStatus userStatus = UserStatus.Normal, Guid? roleId = null) + { + var user = new User( + userInfoId, + UserAccount.Create(account), + password, + UserEmail.Create(email), + salt, + userStatus, + roleId.HasValue ? RoleId.Create(roleId.Value) : null + ); + + return user; + } + + public User UpdateUserEmail(string email) + { + Email = UserEmail.Create(email); + return this; + } + + public User UpdateUserPassword(string password, string salt) + { + if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("密码不得为空"); + if (string.IsNullOrWhiteSpace(salt)) throw new ArgumentException("盐值不得为空"); + Password = password; + Salt = salt; + return this; + } + + public User UpdateUserStatus(UserStatus userStatus) + { + Status = userStatus; + return this; + } + + public User AssignRole(RoleId roleId) + { + if (RoleId != roleId) + { + RoleId = roleId; + } + return this; + } + + public User RemoveRole(RoleId roleId) + { + if (RoleId == roleId) + { + RoleId = null; + } + return this; + } + + public User ChangeStatus(UserStatus newStatus) + { + if (Status != newStatus) + { + var oldStatus = Status; + Status = newStatus; + } + return this; + } + + public User ChangePassword(string newPassword, string newSalt) + { + if (string.IsNullOrWhiteSpace(newPassword)) throw new ArgumentException("密码不得为空"); + Password = newPassword; + Salt = newSalt; + return this; + } + + public User SoftDelete() + { + Status = UserStatus.Deleted; + return this; + } + + [GeneratedRegex(@"^[a-zA-Z][a-zA-Z0-9_]{5,19}$", RegexOptions.IgnoreCase, "zh-CN")] + private static partial Regex MyRegex(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/Entities/UserInfo.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/Entities/UserInfo.cs new file mode 100644 index 0000000..cbea2c3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/Entities/UserInfo.cs @@ -0,0 +1,77 @@ +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.UserManagement.Entities; + +public class UserInfo +{ + /// + /// 信息唯一标识 + /// + public UserInfoId UserInfoId { get; private set; } = null!; + + /// + /// 用户性别 + /// + public UserGender? Gender { get; private set; } + + /// + /// 用户年龄 + /// + public int? Age { get; private set; } + + /// + /// 用户地址 + /// + public string? Address { get; private set; } + + /// + /// 用户姓名 + /// + public string? Name { get; private set; } + + /// + /// 用户电话 + /// + public string? Phone { get; private set; } + + /// + /// 供Efcore使用 + /// + private UserInfo() { } + + /// + /// 创建用户信息的工厂方法 + /// + /// 姓名 + /// 性别 + /// 电话 + /// 地址 + /// + public static UserInfo CreateUserInfo(string? name = null, UserGender? gender = null, string? phone = null, string? address = null) + { + var userInfo = new UserInfo + { + UserInfoId = UserInfoId.Create(Guid.NewGuid()), + Name = name, + Gender = gender, + Phone = phone, + Address = address + }; + return userInfo; + } + + public UserInfo UpdateUserInfo(UserGender? gender, int? age, string? addrss) + { + if (age < 0) + { + throw new ArgumentException("出现了!时间穿越!(年龄错误)"); + } + + Gender = gender; + Age = age; + Address = addrss; + + return this; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserInfoRepository.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserInfoRepository.cs new file mode 100644 index 0000000..4fee19d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserInfoRepository.cs @@ -0,0 +1,11 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.Entities; + +namespace UniversalAdminSystem.Domian.UserManagement.IRepository; + +/// +/// 用户信息仓储接口,提供特定于用户信息的额外方法 +/// +public interface IUserInfoRepository : IRepository +{ +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserRepository.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserRepository.cs new file mode 100644 index 0000000..28e0fb8 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/IRepository/IUserRepository.cs @@ -0,0 +1,39 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.Entities; + +namespace UniversalAdminSystem.Domian.UserManagement.IRepository; + +/// +/// 用户仓储接口,提供特定于User的额外方法 +/// +public interface IUserRepository : IRepository +{ + /// + /// 用户软删除 + /// + /// + /// + Task DeleteUserSoftAsync(Guid id); + + /// + /// 通过账号查询用户 + /// + /// + /// + Task GetUserByAccountAsync(string account); + + /// + /// 通过邮箱查询用户 + /// + /// + /// + Task GetUserByEmailAsync(string email); + + /// + /// 添加用户信息 + /// + /// + /// + Task AddUserInfoAsync(UserInfo userInfo); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserAccount.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserAccount.cs new file mode 100644 index 0000000..f811ef3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserAccount.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.UserManagement.ValueObj; + +public record UserAccount +{ + public string Value { get; } + private UserAccount(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("账号不能为空"); + if (!System.Text.RegularExpressions.Regex.IsMatch(value, @"^[a-zA-Z0-9_]{6,20}$")) + throw new ArgumentException("账号格式不正确,账号长度为6-20字符"); + Value = value; + } + public static UserAccount Create(string value) => new(value); + public static implicit operator string(UserAccount account) => account.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserEmail.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserEmail.cs new file mode 100644 index 0000000..69fb5fe --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserEmail.cs @@ -0,0 +1,16 @@ +namespace UniversalAdminSystem.Domian.UserManagement.ValueObj; + +public record UserEmail +{ + public string Value { get; } + private UserEmail(string value) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException("邮箱不能为空"); + if (!System.Text.RegularExpressions.Regex.IsMatch(value, @"^[^@]+@[^@]+\.[^@]+$")) + throw new ArgumentException("邮箱格式不正确"); + Value = value; + } + public static UserEmail Create(string value) => new(value); + public static implicit operator string(UserEmail email) => email.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserGender.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserGender.cs new file mode 100644 index 0000000..38f5d0f --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UserGender.cs @@ -0,0 +1,7 @@ +namespace UniversalAdminSystem.Domian.UserManagement.ValueObj; + +public enum UserGender +{ + Man, + Female +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UsersStatus.cs b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UsersStatus.cs new file mode 100644 index 0000000..6976962 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserManagement/ValueObj/UsersStatus.cs @@ -0,0 +1,9 @@ +namespace UniversalAdminSystem.Domian.UserManagement.ValueObj; + +public enum UserStatus +{ + Normal, + Freeze, + Logout, + Deleted +} \ No newline at end of file diff --git "a/backend/src/UniversalAdminSystem.Domian/UserManagement/\347\224\250\346\210\267\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" "b/backend/src/UniversalAdminSystem.Domian/UserManagement/\347\224\250\346\210\267\347\256\241\347\220\206\344\270\212\344\270\213\346\226\207.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs new file mode 100644 index 0000000..2402369 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs @@ -0,0 +1,9 @@ +namespace UniversalAdminSystem.Infrastructure.Auth; + +public class JwtSettings +{ + public string Key { get; set; } = string.Empty; // 密钥 + public string Issuer { get; set; } = string.Empty; // 发布者 + public string Audience { get; set; } = string.Empty; // 受众 + public int ExpireHours { get; set; } = 2; // 过期时间 +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs new file mode 100644 index 0000000..8e2960a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs @@ -0,0 +1,49 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; + +namespace UniversalAdminSystem.Infrastructure.Auth; + +public class JwtTokenBuilder +{ + private readonly JwtSettings _settings; + + public JwtTokenBuilder(IOptions options) + { + _settings = options.Value; + } + + public string BuildToken(List claims, int? expireHours = null) + { + Console.WriteLine($"开始构建JWT Token,Claims数量: {claims.Count}"); + Console.WriteLine($"JWT密钥长度: {_settings.Key.Length}"); + + if (string.IsNullOrEmpty(_settings.Key)) + { + throw new InvalidOperationException("JWT密钥不能为空"); + } + + if (_settings.Key.Length < 16) + { + throw new InvalidOperationException($"JWT密钥长度不足,当前长度: {_settings.Key.Length},建议至少16位"); + } + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + issuer: _settings.Issuer, + audience: _settings.Audience, + claims: claims, + expires: DateTime.UtcNow.AddHours(expireHours ?? _settings.ExpireHours), + signingCredentials: creds + ); + + var tokenString = new JwtSecurityTokenHandler().WriteToken(token); + Console.WriteLine($"JWT Token生成成功,长度: {tokenString.Length}"); + + return tokenString; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenService.cs new file mode 100644 index 0000000..cebcfec --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenService.cs @@ -0,0 +1,88 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; + +namespace UniversalAdminSystem.Infrastructure.Auth; + +public class JwtTokenService : IJwtTokenService +{ + private readonly JwtTokenBuilder _builder; + + public JwtTokenService(JwtTokenBuilder builder) + { + _builder = builder; + } + + public string GenerateToken(string userId, string roleId, UserStatus status) + { + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, userId), + new Claim(ClaimTypes.Role, roleId), + new Claim(ClaimTypes.StateOrProvince, status.ToString()) + }; + + return _builder.BuildToken(claims); + } + +public (string userId, string roleId, UserStatus status) ParseToken(string token) +{ + try + { + Console.WriteLine($"开始解析Token,长度: {token.Length}"); + + // 检查token是否为空 + if (string.IsNullOrEmpty(token)) + { + throw new ArgumentException("Token不能为空"); + } + + // 检查token格式 + var parts = token.Split('.'); + if (parts.Length != 3) + { + throw new ArgumentException($"Token格式错误,包含{parts.Length}个部分,应该是3个部分"); + } + + var handler = new JwtSecurityTokenHandler(); + + // 检查token是否可以读取 + if (!handler.CanReadToken(token)) + { + throw new ArgumentException("Token格式不正确,无法读取"); + } + + var jwt = handler.ReadJwtToken(token); + Console.WriteLine($"JWT解析成功,Claims数量: {jwt.Claims.Count()}"); + + var userId = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? ""; + var roleId = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value ?? ""; + var status = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.StateOrProvince)?.Value ?? ""; + + Console.WriteLine($"JWT解析 - 用户ID: {userId}, 角色ID: {roleId}, 状态: {status}"); + + if (string.IsNullOrEmpty(status)) + { + throw new ArgumentException("Token中缺少用户状态信息"); + } + + return (userId, roleId, (UserStatus)Enum.Parse(typeof(UserStatus), status)); + } + catch (Exception ex) + { + Console.WriteLine($"JWT解析失败: {ex.Message}"); + Console.WriteLine($"异常类型: {ex.GetType().Name}"); + throw; + } +} + + public string RefreshToken(string token, out string userId, out string roleId, out UserStatus status) + { + var (_userId, _roleId, _status) = ParseToken(token); + userId = _userId; + roleId = _roleId; + status = _status; + return GenerateToken(userId, roleId, status); + } +} \ No newline at end of file diff --git "a/backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" "b/backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs new file mode 100644 index 0000000..a5c82db --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs @@ -0,0 +1,61 @@ +namespace UniversalAdminSystem.Infrastructure.Configs; + +public class K2Config +{ + public string BaseUrl { get; set; } = string.Empty; + public string ApiKey { get; set; } = string.Empty; +} + +public class K2Request +{ + public string Model { get; set; } = string.Empty; + public List Messages { get; set; } = new(); + public float? Temperature { get; set; } + public int? MaxTokens { get; set; } + public bool Stream { get; set; } = true; + public StreamOptions StreamOptions { get; set; } = new(); +} + +public class StreamOptions +{ + public bool IncludeUsage { get; set; } = true; +} + +public class K2Message +{ + public string Role { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; // +} + +/// +/// K2响应模型 +/// +public class K2Response +{ + public string Id { get; set; } = string.Empty; + public string Object { get; set; } = string.Empty; + public long Created { get; set; } + public string Model { get; set; } = string.Empty; + public List Choices { get; set; } = new(); + public K2Usage Usage { get; set; } = new(); +} + +/// +/// K2选择模型 +/// +public class K2Choice +{ + public int Index { get; set; } + public K2Message Message { get; set; } = new(); + public string FinishReason { get; set; } = string.Empty; +} + +/// +/// K2使用情况模型 +/// +public class K2Usage +{ + public int PromptTokens { get; set; } + public int CompletionTokens { get; set; } + public int TotalTokens { get; set; } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Configs/SystemPermissionConfig.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/SystemPermissionConfig.cs new file mode 100644 index 0000000..7d49f2e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/SystemPermissionConfig.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace UniversalAdminSystem.Infrastructure.Configs; + +/// +/// 系统权限配置类,映射配置文件 +/// +/// +/// +/// +public sealed record SystemPermissionConfig( + [property: JsonPropertyName("Resource")] + string Resource, + + [property: JsonPropertyName("Action"), Required] + string Action, + + [property: JsonPropertyName("Name"), Required] + string Name +); + +/// +/// 系统权限配置根类,映射配置文件 +/// +public sealed class SystemPermissionConfigRoot +{ + [JsonPropertyName("SystemPermissions")] + public required IReadOnlyCollection Permissions { get; init; } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs new file mode 100644 index 0000000..9aaf2b3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs @@ -0,0 +1,97 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Application.UserManagement.Service; +using UniversalAdminSystem.Infrastructure.Persistence.Transaction; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Application.Authentication.Service; +using UniversalAdminSystem.Infrastructure.Auth; +using FluentValidation; +using UniversalAdminSystem.Application.PermissionManagement.Services; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Application.LogManagement.Interfaces; +using UniversalAdminSystem.Application.LogManagement.Services; +using UniversalAdminSystem.Application.SystemSettings.Interfaces; +using UniversalAdminSystem.Application.SystemSettings.Services; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Application.FileStorage.Services; + +namespace UniversalAdminSystem.Infrastructure.DependencyInject; + +public static class AddApplicationService +{ + /// + /// 自动扫描并批量注册服务接口及其实现(支持接口和实现分布在不同程序集,约定接口名为IXXXService,实现名为XXXService) + /// + public static IServiceCollection AddServiceByConvention( + this IServiceCollection services, + Assembly[] interfaceAssemblies, + Assembly[] implementationAssemblies) + { + var interfaces = interfaceAssemblies + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsInterface && t.Name.EndsWith("Service")); + var implementations = implementationAssemblies + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsClass && !t.IsAbstract && t.Name.EndsWith("Service")); + + foreach (var iface in interfaces) + { + var impl = implementations.FirstOrDefault(t => iface.IsAssignableFrom(t)); + if (impl != null) + { + services.AddScoped(iface, impl); + } + } + return services; + } + + public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration configuration) + { + // 自动注册应用服务 + services.AddServiceByConvention( + new Assembly[] + { + typeof(IUserManagementAppService).Assembly, + typeof(ILoginAppService).Assembly, + typeof(IFileAppService).Assembly, + typeof(ILogManagementAppService).Assembly, + typeof(ISystemSettingAppService).Assembly, + typeof(IPermissionManagementAppService).Assembly, + }, + new Assembly[] + { + typeof(UserManagementAppService).Assembly, + typeof(LoginAppService).Assembly, + typeof(FileAppService).Assembly, + typeof(LogManagementAppService).Assembly, + typeof(SystemSettingAppService).Assembly, + typeof(PermissionManagementAppService).Assembly, + } + ); + + // 手动注册应用服务(确保所有服务都被注册) + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // 注册用户权限集成服务 + // services.AddScoped(); + + // 注册JWT配置 + services.Configure(configuration.GetSection("Jwt")); + + // 注册工作单元 + services.AddScoped(); + + return services; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddDomainService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddDomainService.cs new file mode 100644 index 0000000..79cf963 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddDomainService.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using UniversalAdminSystem.Domian; +using UniversalAdminSystem.Domian.PermissionManagement.Services; +using UniversalAdminSystem.Domian.FileStorage.Services; +using UniversalAdminSystem.Domian.PermissionManagement.Interfaces; + +namespace UniversalAdminSystem.Infrastructure.DependencyInject; + +public static class AddDomainService +{ + public static IServiceCollection AddDomain(this IServiceCollection services) + { + // 自动发现并注册领域服务 + var list = DomainServices.DiscoverServices(); + foreach (var (serviceType, implementationType) in list) + { + services.AddScoped(serviceType, implementationType); + } + + // 手动注册领域服务(确保所有领域服务都被注册) + services.AddScoped(); + services.AddScoped(); + + return services; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs new file mode 100644 index 0000000..2026d77 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs @@ -0,0 +1,131 @@ +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using UniversalAdminSystem.Application.UserManagement.Interface; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.FileStorage.IRepository; +using UniversalAdminSystem.Domian.LogManagement.IRepository; +using UniversalAdminSystem.Domian.SystemSettings.IRepository; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.LogManagement.Aggregates; +using UniversalAdminSystem.Domian.SystemSettings.Aggregates; +using UniversalAdminSystem.Infrastructure.Auth; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; +using UniversalAdminSystem.Infrastructure.Persistence.Repositories; +using UniversalAdminSystem.Infrastructure.Services; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Application.PermissionManagement.Services; +using UniversalAdminSystem.Application.Authentication.Interfaces; +using UniversalAdminSystem.Infrastructure.FileStorage; +using FluentValidation; + +namespace UniversalAdminSystem.Infrastructure.DependencyInject; + +public static class AddInfrastrutureService +{ + /// + /// 自动扫描并批量注册仓储接口及其实现(支持接口和实现分布在不同程序集,约定接口名为IXXXRepository,实现名为XXXRepository) + /// + public static IServiceCollection AddRepositoriesByConvention( + this IServiceCollection services, + Assembly[] interfaceAssemblies, + Assembly[] implementationAssemblies) + { + var interfaces = interfaceAssemblies + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsInterface && t.Name.EndsWith("Repository")); + var implementations = implementationAssemblies + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsClass && !t.IsAbstract && t.Name.EndsWith("Repository")); + + foreach (var iface in interfaces) + { + var impl = implementations.FirstOrDefault(t => iface.IsAssignableFrom(t)); + if (impl != null) + { + services.AddScoped(iface, impl); + } + } + return services; + } + + public static IServiceCollection AddInfrastruture(this IServiceCollection services, IConfiguration configuration) + { + // 注册数据库上下文 + services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"))); + + // 注册JWT配置 + services.Configure(configuration.GetSection("Jwt")); + + // 自动注册所有仓储 + services.AddRepositoriesByConvention( + new Assembly[] + { + typeof(IUserRepository).Assembly, + typeof(IUserInfoRepository).Assembly, + typeof(IPermissionRepository).Assembly, + typeof(IRoleRepository).Assembly, + typeof(IFileRepository).Assembly, + typeof(ILogEntryRepository).Assembly, + typeof(ISystemSettingRepository).Assembly, + }, + new Assembly[] + { + typeof(UserRepository).Assembly, + typeof(UserInfoRepository).Assembly, + typeof(PermissionRepository).Assembly, + typeof(RoleRepository).Assembly, + typeof(FileRepository).Assembly, + typeof(LogEntryRepository).Assembly, + typeof(SystemSettingRepository).Assembly, + } + ); + + // 手动注册仓储(确保所有仓储都被注册) + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // 注册通用IRepository接口到具体实现 + services.AddScoped(typeof(IRepository), typeof(LogEntryRepository)); + services.AddScoped(typeof(IRepository), typeof(SystemSettingRepository)); + + // 注册JWT相关服务 + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + + // 注册K2模型相关服务 + services.AddHttpClient(); + services.AddScoped(); + services.AddScoped(); + + // 注册权限相关服务 + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // 注册缓存服务 + services.AddMemoryCache(); + // services.AddScoped(); + + // 注册文件存储服务 + services.AddScoped(); + + // 注册权限规则配置服务 + services.AddHostedService(); + services.AddHostedService(); + services.AddSingleton(); + + // 自动注册 Application 层所有 FluentValidation 校验器 + services.AddValidatorsFromAssembly(Assembly.Load("UniversalAdminSystem.Application")); + + return services; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..71e29de --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace UniversalAdminSystem.Infrastructure.DependencyInject; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddAllServiceRegistrations(this IServiceCollection services, IConfiguration configuration) + { + services.AddDomain(); + services.AddApplication(configuration); + services.AddInfrastruture(configuration); + return services; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs new file mode 100644 index 0000000..b2d9187 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Http; + +namespace UniversalAdminSystem.Infrastructure.FileStorage; + +public interface IFileStorageService +{ + Task UploadAsync(IFormFile file, Stream fileStream); + Task DownloadAsync(string fileName); + Task DeleteAsync(string fileName); + Task> ListFilesAsync(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs new file mode 100644 index 0000000..b624516 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs @@ -0,0 +1,82 @@ +using System.Collections.Concurrent; +using Microsoft.AspNetCore.Http; +using UniversalAdminSystem.Application.FileStorage.DTOs; +using UniversalAdminSystem.Domian.FileStorage.Interface; + +namespace UniversalAdminSystem.Infrastructure.FileStorage; + +public class LocalFileStorageService : IFileStorageService +{ + private readonly IFileDomainService _fileDomainService; + private readonly string _basePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads"); + private static readonly ConcurrentDictionary _fileIndex = new(); + + public LocalFileStorageService(IFileDomainService fileDomainService) + { + _fileDomainService = fileDomainService; + + if (!Directory.Exists(_basePath)) + Directory.CreateDirectory(_basePath); + } + + public async Task UploadAsync(IFormFile file, Stream fileStream) + { + try + { + if (!_fileDomainService.CheckFileSecurity(out var result, file.Name, file.ContentType)) + throw new InvalidOperationException($"上传失败!{result}"); + + var filePath = Path.Combine(_basePath, file.FileName); + using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + await fileStream.CopyToAsync(fs); + } + var fileInfo = new FileInfo(filePath); + var dto = new FileDto( + Guid.NewGuid(), // Id + file.Name, // Name + $"/uploads/{file.FileName}", // Path + fileInfo.Length, // Size + "application/octet-stream", // Type + Guid.Empty, // OwnerId (临时值) + DateTime.UtcNow, // UploadTime + false, // IsFolder + null, // ParentId + "Private" // AccessLevel + ); + _fileIndex[file.Name] = dto; + return dto.Path; + } + catch + { + throw; + } + } + + public async Task DownloadAsync(string fileName) + { + var filePath = Path.Combine(_basePath, fileName); + + if (!File.Exists(filePath)) throw new FileNotFoundException(); + var ms = new MemoryStream(); + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + await fs.CopyToAsync(ms); + } + ms.Position = 0; + return ms; + } + + public Task DeleteAsync(string fileName) + { + var filePath = Path.Combine(_basePath, fileName); + if (File.Exists(filePath)) File.Delete(filePath); + _fileIndex.TryRemove(fileName, out _); + return Task.CompletedTask; + } + + public Task> ListFilesAsync() + { + return Task.FromResult(_fileIndex.Values.AsEnumerable()); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs new file mode 100644 index 0000000..73db4cb --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs @@ -0,0 +1,227 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Domian.LogManagement.Aggregates; +using UniversalAdminSystem.Domian.SystemSettings.Aggregates; +using UniversalAdminSystem.Domian.SystemSettings.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using File = UniversalAdminSystem.Domian.FileStorage.Aggregates.File; + +namespace UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +public class UniversalAdminSystemDbContext : DbContext +{ + public UniversalAdminSystemDbContext(DbContextOptions options) : base(options) { } + + public DbSet UserInfos { get; set; } + public DbSet Users { get; set; } + public DbSet Roles { get; set; } + public DbSet Permissions { get; set; } + public DbSet LogEntries { get; set; } + public DbSet SystemSettings { get; set; } + public DbSet Files { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // 配置UserInfo实体 + modelBuilder.Entity(entity => + { + entity.HasKey(u => u.UserInfoId); + entity.Property(u => u.UserInfoId) + .HasConversion( + id => id.Value, + value => UserInfoId.Create(value) + ); + }); + + // 配置User实体 + modelBuilder.Entity(entity => + { + entity.HasKey(u => u.UserId); + entity.HasIndex(u => u.Account).IsUnique(); + + // 配置UserId值对象转换 + entity.Property(u => u.UserId) + .HasConversion( + id => id.Value, + value => UserId.Create(value) + ); + + // 配置UserInfoId值对象转换 + entity.Property(u => u.UserInfoId) + .HasConversion( + id => id!.Value, + value => UserInfoId.Create(value) + ); + + // 配置UserEmail值对象转换 + entity.Property(u => u.Email) + .HasConversion( + email => email.Value, + value => UserEmail.Create(value) + ); + + // 配置UserAccount值对象转换 + entity.Property(u => u.Account) + .HasConversion( + account => account.Value, + value => UserAccount.Create(value) + ); + + // 配置User和Role的一对多关系 + entity.HasOne() + .WithMany() + .HasForeignKey("RoleId") + .IsRequired(false) + .OnDelete(DeleteBehavior.SetNull); + }); + + // 配置Permission实体 + modelBuilder.Entity(entity => + { + entity.HasKey(p => p.PermissionId); + entity.HasIndex(p => p.Code).IsUnique(); + entity.Property(p => p.Code).HasConversion( + code => code.Value, + value => PermissionCode.Create(value) + ); + entity.Property(p => p.Name).HasConversion( + name => name.Value, + value => PermissionName.Create(value) + ); + + entity.Property(p => p.Code).HasConversion( + code => code.Value, + value => PermissionCode.Create(value) + ); + + entity.Property(p => p.Resource).HasConversion( + resource => resource.Value, + value => PermissionResource.Create(value) + ); + }); + + // 配置Role + modelBuilder.Entity(entity => + { + entity.HasKey(r => r.RoleId); + entity.HasIndex(r => r.Name).IsUnique(); + + // 配置RoleId值对象转换 + entity.Property(r => r.RoleId) + .HasConversion( + id => id.Value, + value => RoleId.Create(value) + ); + + // 配置RoleName值对象转换 + entity.Property(r => r.Name) + .HasConversion( + name => name.Value, + value => RoleName.Create(value) + ); + + // 配置RoleDescription值对象转换 + entity.Property(r => r.Description) + .HasConversion( + desc => desc!.Value, + value => RoleDescription.Create(value) + ); + + // 配置多对多关系 + entity.HasMany(r => r.Permissions) + .WithMany() + .UsingEntity( + "RolePermissions", + l => l.HasOne(typeof(Permission)).WithMany().HasForeignKey("PermissionId"), + r => r.HasOne(typeof(Role)).WithMany().HasForeignKey("RoleId"), + j => + { + j.HasKey("RoleId", "PermissionId"); + j.HasIndex("RoleId"); + j.HasIndex("PermissionId"); + } + ); + }); + + // 配置LogEntry实体 + modelBuilder.Entity(entity => + { + entity.HasKey(l => l.Id); + entity.Property(l => l.UserId) + .HasConversion( + userId => userId!.Value, + value => UserId.Create(value) + ); + }); + + // 配置SystemSetting实体 + modelBuilder.Entity(entity => + { + entity.HasKey(s => s.Id); + entity.Property(s => s.Key) + .HasConversion( + key => key.Value, + value => SettingKey.Create(value) + ); + entity.Property(s => s.Value) + .HasConversion( + value => value.Value, + val => SettingValue.Create(val) + ); + entity.Property(s => s.Description) + .HasConversion( + desc => desc!.Value, + value => SettingDescription.Create(value) + ); + }); + + // 配置File实体 + modelBuilder.Entity(entity => + { + entity.HasKey(f => f.Id); + entity.Property(f => f.Id) + .HasConversion( + id => id.Value, + value => FileId.Create(value) + ); + entity.Property(f => f.Name) + .HasConversion( + name => name.Value, + value => FileName.Create(value) + ); + entity.Property(f => f.Path) + .HasConversion( + path => path.Value, + value => FilePath.Create(value) + ); + entity.Property(f => f.Size) + .HasConversion( + size => size.Value, + value => FileSize.Create(value) + ); + entity.Property(f => f.Type) + .HasConversion( + type => type.Value, + value => FileType.Create(value) + ); + entity.Property(f => f.OwnerId) + .HasConversion( + ownerId => ownerId.Value, + value => UserId.Create(value) + ); + entity.Property(f => f.ParentId) + .HasConversion( + parentId => parentId!.Value, + value => FileId.Create(value) + ); + }); + + // 忽略值对象 + modelBuilder.Ignore(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/BaseRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/BaseRepository.cs new file mode 100644 index 0000000..a0d10fe --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/BaseRepository.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Npgsql; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +/// +/// 基础仓储,基本CRUD可重写 +/// +/// +public class BaseRepository : IRepository where T : class +{ + protected readonly UniversalAdminSystemDbContext _DbContext; + protected readonly DbSet _TDs; + + + public BaseRepository(UniversalAdminSystemDbContext dbContext) + { + _DbContext = dbContext; + _TDs = dbContext.Set(); + } + + public virtual async Task AddAsync(T entity) + { + try + { + var list = (await _TDs.AddAsync(entity)).Entity; + + return list; + } + catch (DbUpdateException ex) + { + // 处理数据库更新异常 + throw new Exception("数据库更新异常", ex); + } + catch (ArgumentNullException ex) + { + // 处理空参数异常 + throw new ArgumentNullException("实体不能为空", ex); + } + } + + public virtual async Task> GetAllAsync() + { + var list = await _TDs.AsNoTracking().ToListAsync(); + return list; + } + + public virtual async Task GetByGuidAsync(Guid guid) + { + var list = await _TDs.FindAsync(guid); + return list; + } + + public virtual async Task RemoveAsync(Guid guid) + { + var user = await GetByGuidAsync(guid); + + if (user == null) return; + + _TDs.Remove(user); + + } + + public async Task Update(T entity) + { + await Task.Run(() => _TDs.Update(entity)); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/FileRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/FileRepository.cs new file mode 100644 index 0000000..1811b67 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/FileRepository.cs @@ -0,0 +1,36 @@ +using UniversalAdminSystem.Domian.FileStorage.IRepository; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using File = UniversalAdminSystem.Domian.FileStorage.Aggregates.File; +using Microsoft.Extensions.FileProviders; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class FileRepository : BaseRepository, IFileRepository +{ + public FileRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) { } + + public async Task> GetByOwnerAsync(UserId ownerId) + => await _TDs.Where(f => f.OwnerId == ownerId).ToListAsync(); + + public async Task> GetByParentAsync(FileId? parentId) + => await _TDs.Where(f => f.ParentId == parentId).ToListAsync(); + + public async Task> GetByTypeAsync(FileType type) + => await _TDs.Where(f => f.Type == type).ToListAsync(); + + public async Task> GetFilesWithAccessAsync(UserId userId, FileAccessLevel accessLevel) + => await _TDs.Where(f => f.AccessLevel == accessLevel && (f.OwnerId == userId || f.AccessLevel == FileAccessLevel.Public)).ToListAsync(); + + public async Task> GetAllFoldersAsync(UserId ownerId) + => await _TDs.Where(f => f.OwnerId == ownerId && f.IsFolder).ToListAsync(); + + public override async Task GetByGuidAsync(Guid id) + { + var fileId = FileId.Create(id); + var file =await _TDs.FirstOrDefaultAsync(f => f.Id == fileId); + return file; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/LogEntryRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/LogEntryRepository.cs new file mode 100644 index 0000000..c85e12e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/LogEntryRepository.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.LogManagement.Aggregates; +using UniversalAdminSystem.Domian.LogManagement.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class LogEntryRepository : BaseRepository, ILogEntryRepository +{ + public LogEntryRepository(UniversalAdminSystemDbContext context) : base(context) + { + } + + public async Task> GetByLevelAsync(string level) + { + return await _DbContext.Set() + .Where(l => l.Level == level) + .ToListAsync(); + } + + public async Task> GetByUserAsync(Guid userId) + { + return await _DbContext.Set() + .Where(l => l.UserId.Value == userId) + .ToListAsync(); + } + + public async Task> GetByDateRangeAsync(DateTime start, DateTime end) + { + return await _DbContext.Set() + .Where(l => l.Timestamp >= start && l.Timestamp <= end) + .ToListAsync(); + } + + public async Task> GetBySourceAsync(string source) + { + return await _DbContext.Set() + .Where(l => l.Source == source) + .ToListAsync(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/PermissionRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/PermissionRepository.cs new file mode 100644 index 0000000..05db86d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/PermissionRepository.cs @@ -0,0 +1,109 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class PermissionRepository : BaseRepository, IPermissionRepository +{ + public PermissionRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + /// + /// 批量持久化系统权限 + /// + /// + /// + public async Task AddSystemPermissionsAsync(IEnumerable perms) + { + if (perms == null) throw new ArgumentNullException(nameof(perms)); + var permList = perms.ToList(); + if (!permList.Any()) return; + + using var transaction = await _DbContext.Database.BeginTransactionAsync(); + try + { + // 获取所有已存在的权限代码 + var existingCodes = await _TDs + .Select(p => p.Code.Value) + .ToListAsync(); + + Console.WriteLine($"数据库中已存在的权限代码数量: {existingCodes.Count}"); + + // 过滤出新的权限 + var newPermissions = permList + .Where(p => !existingCodes.Contains(p.Code.Value)) + .ToList(); + + Console.WriteLine($"需要添加的新权限数量: {newPermissions.Count}"); + + if (newPermissions.Count > 0) + { + await _TDs.AddRangeAsync(newPermissions); + await _DbContext.SaveChangesAsync(); + Console.WriteLine($"成功添加 {newPermissions.Count} 个新权限"); + } + else + { + Console.WriteLine("所有权限都已存在,跳过添加"); + } + + await transaction.CommitAsync(); + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + Console.WriteLine($"添加系统权限失败: {ex.Message}"); + throw; + } + } + + public async Task GetByCodeAsync(PermissionCode code) + { + if (code == null) throw new ArgumentNullException(nameof(code)); + + // 使用隐式转换,让EF Core能够正确转换查询 + var permission = await _TDs.FirstOrDefaultAsync(p => p.Code == code); + + if (permission != null) + { + System.Console.WriteLine($"找到权限: {permission.Code}"); + } + else + { + System.Console.WriteLine($"未找到权限代码: {code}"); + } + + return permission; + } + + public async Task ExistsAsync(PermissionCode code) + { + if (code == null) throw new ArgumentNullException(nameof(code)); + return await _TDs.AnyAsync(p => p.Code == code); + } + + public async Task> GetByIdsAsync(IEnumerable ids) + { + if (ids == null) throw new ArgumentNullException(nameof(ids)); + return await _TDs.Where(p => ids.Contains(p.PermissionId)).ToListAsync(); + } + + public async Task> QueryAsync(string? name = null, string? resource = null, int? action = null, PermissionType? type = null) + { + var query = _TDs.AsQueryable(); + if (!string.IsNullOrWhiteSpace(name)) + query = query.Where(p => p.Name.Value.Contains(name)); + if (!string.IsNullOrWhiteSpace(resource)) + query = query.Where(p => p.Resource.Value == resource); + if (action.HasValue) + query = query.Where(p => (int)p.Action == action.Value); + if (type.HasValue) + query = query.Where(p => p.PermissionType == type.Value); + return await query.ToListAsync(); + } +} + diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/RoleRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/RoleRepository.cs new file mode 100644 index 0000000..61158eb --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/RoleRepository.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class RoleRepository : BaseRepository, IRoleRepository +{ + public RoleRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public override async Task GetByGuidAsync(Guid guid) + { + var roleId = RoleId.Create(guid); + var role = await _TDs + .Include(r => r.Permissions) // 包含权限关联 + .FirstOrDefaultAsync(r => r.RoleId == roleId); + return role; + } + public async Task ExistsAsync(string name) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + return await _TDs.AnyAsync(r => r.Name == name); + } + + public async Task> GetByIdsAsync(IEnumerable ids) + { + if (ids == null) throw new ArgumentNullException(nameof(ids)); + return await _TDs + .Include(r => r.Permissions) + .Where(r => ids.Contains(r.RoleId)) + .ToListAsync(); + } + + public async Task GetByNameAsync(string name) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + + // 使用更明确的比较方式 + var allRoles = await _TDs + .Include(r => r.Permissions) + .ToListAsync(); + + return allRoles.FirstOrDefault(r => r.Name.Value == name); + } + + public async Task> GetPermissionIdsAsync(Guid roleId) + { + var role = await _TDs + .Include(r => r.Permissions) + .FirstOrDefaultAsync(r => r.RoleId == roleId); + if (role == null) throw new KeyNotFoundException("角色不存在"); + return role.PermissionIds.ToList().AsReadOnly(); + } + + /// + /// 获取角色及其权限(包含权限导航属性) + /// + public async Task GetRoleWithPermissionsAsync(Guid roleId) + { + return await _TDs + .Include(r => r.Permissions) + .FirstOrDefaultAsync(r => r.RoleId == roleId); + } + + /// + /// 获取所有角色及其权限 + /// + public async Task> GetAllRolesWithPermissionsAsync() + { + return await _TDs + .Include(r => r.Permissions) + .ToListAsync(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/SystemSettingRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/SystemSettingRepository.cs new file mode 100644 index 0000000..dae2f41 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/SystemSettingRepository.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.SystemSettings.Aggregates; +using UniversalAdminSystem.Domian.SystemSettings.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class SystemSettingRepository : BaseRepository, ISystemSettingRepository +{ + public SystemSettingRepository(UniversalAdminSystemDbContext context) : base(context) + { + } + + public async Task GetByKeyAsync(string key) + { + return await _DbContext.Set() + .FirstOrDefaultAsync(s => s.Key.Value == key); + } + + public async Task> GetByGroupAsync(string group) + { + return await _DbContext.Set() + .Where(s => s.Key.Value.StartsWith(group)) + .ToListAsync(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserInfoRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserInfoRepository.cs new file mode 100644 index 0000000..36a8f08 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserInfoRepository.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class UserInfoRepository : BaseRepository, IUserInfoRepository +{ + public UserInfoRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public override async Task GetByGuidAsync(Guid id) + { + var userInfoId = UserInfoId.Create(id); + var userInfo =await _TDs.FirstOrDefaultAsync(f => f.UserInfoId == userInfoId); + return userInfo; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserRepository.cs new file mode 100644 index 0000000..064ff16 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/UserRepository.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class UserRepository : BaseRepository, IUserRepository +{ + public UserRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public override async Task GetByGuidAsync(Guid guid) + { + var userId = UserId.Create(guid); + var user = await _TDs.FirstOrDefaultAsync(u => u.UserId == userId); + System.Console.WriteLine($"user:{user.Account}"); + return user; + } + + /// + /// 用户软删除 + /// + /// + /// + /// + public async Task DeleteUserSoftAsync(Guid id) + { + var user = await GetByGuidAsync(id) ?? throw new NullReferenceException("用户不存在"); + user.UpdateUserStatus(UserStatus.Deleted); + await Update(user); + } + + /// + /// 通过账号查询用户 + /// + /// + /// + public async Task GetUserByAccountAsync(string account) + { + var user = await _TDs.FirstOrDefaultAsync(u => u.Account == account); + return user; + } + + /// + /// 通过邮箱查询用户 + /// + /// + /// + public async Task GetUserByEmailAsync(string email) + { + var user = await _TDs.FirstOrDefaultAsync(u => u.Email == email); + return user; + } + + /// + /// 添加用户信息 + /// + /// + /// + public async Task AddUserInfoAsync(UserInfo userInfo) + { + await _DbContext.UserInfos.AddAsync(userInfo); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Transaction/UnitOfWork.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Transaction/UnitOfWork.cs new file mode 100644 index 0000000..8317faa --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Transaction/UnitOfWork.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Storage; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Transaction; + +public class UnitOfWork : IUnitOfWork +{ + private readonly UniversalAdminSystemDbContext _context; + private IDbContextTransaction _transaction; + + public UnitOfWork(UniversalAdminSystemDbContext context) + { + _context = context; + } + public async Task BeginTransactionAsync() + { + _transaction = await _context.Database.BeginTransactionAsync(); + } + + public async Task CommitAsync() + { + await _context.SaveChangesAsync(); + await _transaction.CommitAsync(); + } + + public async Task RollbackAsync() + { + if (_transaction != null) + { + await _transaction.RollbackAsync(); + _transaction = null; + } + } + + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + return await _context.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs new file mode 100644 index 0000000..5b3015f --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; +using UniversalAdminSystem.Infrastructure.Configs; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public class K2ConfigService +{ + private readonly K2Config _k2Config; + + public K2ConfigService(IOptions k2Config) + { + _k2Config = k2Config.Value; + } + + public K2Config GetK2Config() + { + return _k2Config; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs new file mode 100644 index 0000000..291fdfd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs @@ -0,0 +1,86 @@ +using System.Text; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using UniversalAdminSystem.Infrastructure.Configs; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public class K2ModelService +{ + private readonly K2Config _k2Config; + private readonly ILogger _logger; + private readonly HttpClient _httpClient; + + public K2ModelService(IOptions k2Config, ILogger logger, HttpClient httpClient) + { + _k2Config = k2Config.Value; + _logger = logger; + _httpClient = httpClient; + + // 设置默认请求头 + _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_k2Config.ApiKey}"); + _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json"); + } + + /// + /// 发送聊天请求到K2模型 + /// + /// 聊天消息列表 + /// 温度参数,控制随机性 + /// 最大token数 + /// 模型响应 + public async Task SendChatRequestAsync(List messages, string model = "Moonshot-Kimi-K2-Instruct", float temperature = 0.7f, int maxTokens = 1000) + { + try + { + _logger.LogInformation("开始发送K2模型请求"); + var k2Request = new K2Request + { + Model = model, + Messages = messages, + Temperature = temperature, + MaxTokens = maxTokens + }; + var resquest = new HttpRequestMessage(HttpMethod.Post, $"{_k2Config.BaseUrl}/chat/completions") + { + Content = new StringContent(JsonSerializer.Serialize(k2Request), Encoding.UTF8, "application/json") + }; + var response = await _httpClient.SendAsync(resquest, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None); + response.EnsureSuccessStatusCode(); + await using var responseStream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(responseStream, Encoding.UTF8); + var sb = new StringBuilder(); + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(line)) continue; + sb.Append(line); + } + _logger.LogInformation("K2模型请求成功完成"); + var k2Response = JsonSerializer.Deserialize(sb.ToString()); + return k2Response ?? throw new Exception("无法解析K2模型响应"); + } + catch (Exception ex) + { + _logger.LogError(ex, "K2模型请求过程中发生错误"); + throw; + } + } + + /// + /// 发送简单的文本请求 + /// + /// 提示文本 + /// 模型响应 + public async Task SendSimpleRequestAsync(string prompt) + { + var messages = new List + { + new K2Message { Role = "user", Content = prompt } + }; + + var response = await SendChatRequestAsync(messages); + return response?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/PasswordHelper.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/PasswordHelper.cs new file mode 100644 index 0000000..1a7261c --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/PasswordHelper.cs @@ -0,0 +1,62 @@ +using System.Security.Cryptography; +using System.Text.RegularExpressions; +using UniversalAdminSystem.Application.UserManagement.Interface; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public class PasswordHelper : IPasswordHelper +{ + private const int SaltSize = 16; + private const int HashSize = 32; + private const int Iterations = 1000; + + /// + /// 密码加密 + /// + /// + /// + public (string hashedPassword, string salt) HashPasswordWithSeparateSalt(string password) + { + if (!Regex.IsMatch(password, "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z][^\u4e00-\u9fa5]{5,19}$", RegexOptions.IgnoreCase)) throw new ArgumentException("密码必须是数字+字母组合并且不含中文 长度6至20位"); + byte[] salt = new byte[SaltSize]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(salt); + } + + using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, Iterations, HashAlgorithmName.SHA256)) + { + byte[] hash = pbkdf2.GetBytes(HashSize); + return (hashedPassword: Convert.ToBase64String(hash), + salt: Convert.ToBase64String(salt)); + } + + } + /// + /// 密码比对 + /// + /// + /// + /// + /// + public bool VerifyPasswordWithSeparateSalt(string password, string storedHashedPassword, string storedSalt) + { + byte[] storedHash = Convert.FromBase64String(storedHashedPassword); + byte[] salt = Convert.FromBase64String(storedSalt); + + using (var pbkdf2 = new Rfc2898DeriveBytes( + password, + salt, + Iterations, + HashAlgorithmName.SHA256)) + { + byte[] computedHash = pbkdf2.GetBytes(HashSize); + + // 安全比较 + return CryptographicOperations.FixedTimeEquals( + storedHash.AsSpan(), + computedHash.AsSpan()); + } + } + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionCacheService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionCacheService.cs new file mode 100644 index 0000000..f7ae1a9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionCacheService.cs @@ -0,0 +1,105 @@ +// using Microsoft.Extensions.Caching.Memory; +// using UniversalAdminSystem.Application.PermissionManagement.Interfaces; + +// namespace UniversalAdminSystem.Infrastructure.Services; + +// /// +// /// 权限缓存服务 +// /// 提供权限信息的缓存功能,提高权限检查性能 +// /// +// public class PermissionCacheService +// { +// private readonly IMemoryCache _cache; +// private readonly IPermissionCheckService _permissionCheckService; +// private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(30); // 缓存30分钟 + +// public PermissionCacheService(IMemoryCache cache, IPermissionCheckService permissionCheckService) +// { +// _cache = cache; +// _permissionCheckService = permissionCheckService; +// } + +// /// +// /// 检查用户是否有指定权限(带缓存) +// /// +// /// 用户ID +// /// 权限编码 +// /// 是否有权限 +// public async Task CheckUserPermissionAsync(Guid userId, string permissionCode) +// { +// var cacheKey = $"user_permission_{userId}_{permissionCode}"; + +// if (_cache.TryGetValue(cacheKey, out bool cachedResult)) +// { +// return cachedResult; +// } + +// var result = await _permissionCheckService.CheckUserPermissionAsync(userId, permissionCode); +// _cache.Set(cacheKey, result, _cacheExpiration); + +// return result; +// } + +// /// +// /// 获取用户的所有权限(带缓存) +// /// +// /// 用户ID +// /// 权限编码列表 +// public async Task> GetUserPermissionsAsync(Guid userId) +// { +// var cacheKey = $"user_permissions_{userId}"; + +// if (_cache.TryGetValue(cacheKey, out IEnumerable cachedPermissions)) +// { +// return cachedPermissions; +// } + +// var permissions = await _permissionCheckService.GetUserPermissionAsync(userId); +// _cache.Set(cacheKey, permissions, _cacheExpiration); + +// return permissions; +// } + +// /// +// /// 获取用户的所有角色(带缓存) +// /// +// /// 用户ID +// /// 角色名称列表 +// public async Task> GetUserRolesAsync(Guid userId) +// { +// var cacheKey = $"user_roles_{userId}"; + +// if (_cache.TryGetValue(cacheKey, out IEnumerable cachedRoles)) +// { +// return cachedRoles; +// } + +// var roles = await _permissionCheckService.GetUserRolesAsync(userId); +// _cache.Set(cacheKey, roles, _cacheExpiration); + +// return roles; +// } + +// /// +// /// 清除用户权限缓存 +// /// +// /// 用户ID +// public void ClearUserCache(Guid userId) +// { +// var pattern = $"user_*_{userId}*"; +// // 注意:IMemoryCache不支持模式匹配删除,这里只是示例 +// // 在实际项目中,可能需要使用Redis或其他支持模式匹配的缓存 +// _cache.Remove($"user_permissions_{userId}"); +// _cache.Remove($"user_roles_{userId}"); +// } + +// /// +// /// 清除所有权限缓存 +// /// +// public void ClearAllCache() +// { +// // 注意:IMemoryCache不支持清空所有缓存 +// // 在实际项目中,可能需要使用Redis或其他缓存方案 +// Console.WriteLine("权限缓存已清除"); +// } +// } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionInitializationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionInitializationService.cs new file mode 100644 index 0000000..cfc412c --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionInitializationService.cs @@ -0,0 +1,106 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.Services; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public class PermissionInitializationService : IHostedService +{ + private readonly SystemPermissionConfigLoader _loader; + private readonly IServiceScopeFactory _scopeFactory; + private static bool _initialized = false; + + public PermissionInitializationService( + SystemPermissionConfigLoader loader, + IServiceScopeFactory scopeFactory + ) + { + _loader = loader; + _scopeFactory = scopeFactory; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + if (_initialized) + { + return; + } + + cancellationToken.ThrowIfCancellationRequested(); + + try + { + // 首先加载权限规则配置 + var rulesConfigPath = Path.Combine(AppContext.BaseDirectory, "PermissionRules.json"); + if (File.Exists(rulesConfigPath)) + { + var rulesContent = await File.ReadAllTextAsync(rulesConfigPath, cancellationToken); + var rulesDict = System.Text.Json.JsonSerializer.Deserialize>>(rulesContent); + + if (rulesDict != null) + { + var rules = rulesDict.ToDictionary( + kv => Enum.Parse(kv.Key, true), + kv => kv.Value.ToHashSet() + ); + + ResourceActionValidator.LoadRules(rules); + Console.WriteLine("权限规则配置加载成功"); + } + } + else + { + Console.WriteLine("权限规则配置文件不存在,使用默认配置"); + // 设置默认规则 + var defaultRules = new Dictionary> + { + { PermissionAction.Read, new HashSet { "data", "file", "user", "role", "permission", "config", "system" } }, + { PermissionAction.Create, new HashSet { "data", "user", "file", "role", "permission" } }, + { PermissionAction.Update, new HashSet { "data", "user", "config", "role", "permission" } }, + { PermissionAction.Delete, new HashSet { "data", "user", "file", "role", "permission" } }, + { PermissionAction.Manage, new HashSet { "system", "user" } } + }; + ResourceActionValidator.LoadRules(defaultRules); + } + + // 加载配置文件 + var configPath = Path.Combine(AppContext.BaseDirectory, "SystemPermissions.json"); + var permissions = await _loader.LoadFromFileAsync(configPath); + + // 初始化到数据库 + using (var scope = _scopeFactory.CreateScope()) + { + var permissionRepository = scope.ServiceProvider.GetRequiredService(); + + var systemPermissions = new List(); + foreach (var perm in permissions) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!Enum.TryParse(perm.Action, out var action)) + { + throw new InvalidOperationException($"无效的权限操作: {perm.Action}"); + } + + var sysPerm = Permission.CreateSystemPermission(perm.Name, perm.Resource, (int)action); + systemPermissions.Add(sysPerm); + } + + // 持久化 + await permissionRepository.AddSystemPermissionsAsync(systemPermissions); + Console.WriteLine($"成功初始化 {systemPermissions.Count} 个系统权限"); + } + + _initialized = true; + } + catch (Exception ex) + { + throw new InvalidOperationException("初始化系统权限失败", ex); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleConfigService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleConfigService.cs new file mode 100644 index 0000000..bf10a03 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleConfigService.cs @@ -0,0 +1,112 @@ +using UniversalAdminSystem.Domian.PermissionManagement.Services; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Infrastructure.Services; + +/// +/// 权限规则配置服务 +/// 职责:统一管理权限规则配置的加载、更新、热重载 +/// +public class PermissionRuleConfigService : IDisposable +{ + private readonly PermissionRuleFileService _fileService; + private readonly string _configFilePath; + + public PermissionRuleConfigService(string configFilePath) + { + _configFilePath = configFilePath; + _fileService = new PermissionRuleFileService(configFilePath, OnConfigChanged); + } + + /// + /// 初始化配置(应用启动时调用) + /// + public void Initialize() + { + try + { + if (File.Exists(_configFilePath)) + { + var rules = _fileService.LoadFromFile(); + ResourceActionValidator.LoadRules(rules); + Console.WriteLine("权限规则配置加载成功"); + + // 启动文件监听 + _fileService.StartFileWatcher(); + } + else + { + Console.WriteLine("权限规则配置文件不存在,使用默认配置"); + } + } + catch (Exception ex) + { + Console.WriteLine($"权限规则配置初始化失败: {ex.Message}"); + } + } + + /// + /// 获取当前配置 + /// + public Dictionary> GetCurrentConfig() + { + return ResourceActionValidator.GetCurrentRules(); + } + + /// + /// 更新配置(支持后台维护) + /// + public void UpdateConfig(Dictionary> newConfig) + { + try + { + // 转换为领域模型格式 + var rules = newConfig.ToDictionary( + kv => Enum.Parse(kv.Key, true), + kv => kv.Value.ToHashSet() + ); + + // 更新校验器 + ResourceActionValidator.LoadRules(rules); + + // 保存到文件 + _fileService.SaveToFile(rules); + + Console.WriteLine("权限规则配置更新成功"); + } + catch (Exception ex) + { + throw new InvalidOperationException($"更新权限规则配置失败: {ex.Message}", ex); + } + } + + /// + /// 手动刷新配置 + /// + public void RefreshConfig() + { + try + { + var rules = _fileService.LoadFromFile(); + ResourceActionValidator.LoadRules(rules); + Console.WriteLine("权限规则配置刷新成功"); + } + catch (Exception ex) + { + throw new InvalidOperationException($"刷新权限规则配置失败: {ex.Message}", ex); + } + } + + /// + /// 配置变更回调 + /// + private void OnConfigChanged(Dictionary> rules) + { + ResourceActionValidator.LoadRules(rules); + } + + public void Dispose() + { + _fileService?.Dispose(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleFileService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleFileService.cs new file mode 100644 index 0000000..9c18a29 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/PermissionRuleFileService.cs @@ -0,0 +1,155 @@ +using System.Text.Json; +using System.Threading.Tasks; +using UniversalAdminSystem.Domian.PermissionManagement.Exceptions; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Infrastructure.Services; + +/// +/// 权限规则文件服务 +/// 职责:负责权限规则配置文件的读取、写入和文件监听 +/// +public class PermissionRuleFileService : IDisposable +{ + private readonly string _configFilePath; + private FileSystemWatcher? _fileWatcher; + private readonly Action>> _onConfigChanged; + + public PermissionRuleFileService(string configFilePath, Action>> onConfigChanged) + { + _configFilePath = configFilePath; + _onConfigChanged = onConfigChanged; + } + + /// + /// 从文件加载权限规则配置 + /// + public Dictionary> LoadFromFile() + { + try + { + if (!File.Exists(_configFilePath)) + { + throw new FileNotFoundException($"权限规则配置文件不存在: {_configFilePath}"); + } + + // await using var stream = File.OpenRead(_configFilePath); + var jsonContent = File.ReadAllText(_configFilePath); + var configDict = JsonSerializer.Deserialize>>(jsonContent) ?? throw new PermissionDomainException("权限规则配置文件格式错误"); + + // 转换为领域模型格式 + var rules = configDict.ToDictionary( + kv => Enum.Parse(kv.Key, true), + kv => kv.Value.ToHashSet() + ); + + return rules; + } + catch (Exception ex) when (ex is not PermissionDomainException) + { + throw new PermissionDomainException($"加载权限规则配置文件失败: {ex.Message}", ex); + } + } + + /// + /// 将权限规则配置写入文件 + /// + public void SaveToFile(Dictionary> rules) + { + try + { + // 转换为JSON格式 + var configDict = rules.ToDictionary( + kv => kv.Key.ToString(), + kv => kv.Value.ToList() + ); + + var jsonContent = JsonSerializer.Serialize(configDict, new JsonSerializerOptions + { + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + // 确保目录存在 + var directory = Path.GetDirectoryName(_configFilePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + File.WriteAllText(_configFilePath, jsonContent); + } + catch (Exception ex) + { + throw new PermissionDomainException($"保存权限规则配置文件失败: {ex.Message}", ex); + } + } + + /// + /// 启动文件监听(热重载) + /// + public void StartFileWatcher() + { + try + { + _fileWatcher?.Dispose(); + + var directory = Path.GetDirectoryName(_configFilePath); + var fileName = Path.GetFileName(_configFilePath); + + if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(fileName)) + { + throw new ArgumentException("配置文件路径无效"); + } + + _fileWatcher = new FileSystemWatcher(directory, fileName) + { + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime, + EnableRaisingEvents = true + }; + + _fileWatcher.Changed += OnConfigFileChanged; + _fileWatcher.Created += OnConfigFileChanged; + } + catch (Exception ex) + { + throw new PermissionDomainException($"启动文件监听失败: {ex.Message}", ex); + } + } + + /// + /// 文件变更事件处理 + /// + private void OnConfigFileChanged(object sender, FileSystemEventArgs e) + { + try + { + // 延迟加载,避免文件写入过程中读取 + Thread.Sleep(100); + + var rules = LoadFromFile(); + _onConfigChanged?.Invoke(rules); + + Console.WriteLine($"权限规则配置文件已重新加载: {e.FullPath}"); + } + catch (Exception ex) + { + // 记录错误但不抛出异常,避免影响系统运行 + Console.WriteLine($"权限规则配置文件变更加载失败: {ex.Message}"); + } + } + + /// + /// 停止文件监听 + /// + public void StopFileWatcher() + { + _fileWatcher?.Dispose(); + _fileWatcher = null; + } + + public void Dispose() + { + StopFileWatcher(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs new file mode 100644 index 0000000..394173d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs @@ -0,0 +1,529 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using UniversalAdminSystem.Domian.PermissionManagement.Aggregate; +using UniversalAdminSystem.Domian.PermissionManagement.IRepository; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.Services; +using UniversalAdminSystem.Domian.UserManagement.Aggregates; +using UniversalAdminSystem.Domian.UserManagement.IRepository; +using UniversalAdminSystem.Domian.UserManagement.ValueObj; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Domian.UserManagement.Entities; +using UniversalAdminSystem.Application.UserManagement.Interface; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public class SystemInitializationService : IHostedService +{ + private readonly SystemPermissionConfigLoader _loader; + private readonly IServiceScopeFactory _scopeFactory; + private static bool _initialized = false; + + public SystemInitializationService( + SystemPermissionConfigLoader loader, + IServiceScopeFactory scopeFactory + ) + { + _loader = loader; + _scopeFactory = scopeFactory; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + if (_initialized) + { + return; + } + + cancellationToken.ThrowIfCancellationRequested(); + + try + { + // 首先加载权限规则配置 + var rulesConfigPath = Path.Combine(AppContext.BaseDirectory, "PermissionRules.json"); + if (File.Exists(rulesConfigPath)) + { + var rulesContent = await File.ReadAllTextAsync(rulesConfigPath, cancellationToken); + var rulesDict = System.Text.Json.JsonSerializer.Deserialize>>(rulesContent); + + if (rulesDict != null) + { + var rules = rulesDict.ToDictionary( + kv => Enum.Parse(kv.Key, true), + kv => kv.Value.ToHashSet() + ); + + ResourceActionValidator.LoadRules(rules); + Console.WriteLine("权限规则配置加载成功"); + } + } + else + { + Console.WriteLine("权限规则配置文件不存在,使用默认配置"); + // 设置默认规则 + var defaultRules = new Dictionary> + { + { PermissionAction.Read, new HashSet { "data", "file", "user", "role", "permission", "config", "system" } }, + { PermissionAction.Create, new HashSet { "data", "user", "file", "role", "permission" } }, + { PermissionAction.Update, new HashSet { "data", "user", "config", "role", "permission" } }, + { PermissionAction.Delete, new HashSet { "data", "user", "file", "role", "permission" } }, + { PermissionAction.Manage, new HashSet { "system", "user" } } + }; + ResourceActionValidator.LoadRules(defaultRules); + } + + // 初始化到数据库 + using (var scope = _scopeFactory.CreateScope()) + { + var permissionRepository = scope.ServiceProvider.GetRequiredService(); + var roleRepository = scope.ServiceProvider.GetRequiredService(); + var userRepository = scope.ServiceProvider.GetRequiredService(); + var userInfoRepository = scope.ServiceProvider.GetRequiredService(); + var unitOfWork = scope.ServiceProvider.GetRequiredService(); + var passwordHelper = scope.ServiceProvider.GetRequiredService(); + + // 1. 创建系统权限 + Console.WriteLine("开始创建系统权限..."); + var systemPermissions = CreateSystemPermissions(); + await permissionRepository.AddSystemPermissionsAsync(systemPermissions); + Console.WriteLine($"成功创建 {systemPermissions.Count} 个系统权限"); + + // 2. 创建角色 + Console.WriteLine("开始创建角色..."); + var roles = await CreateRolesAsync(roleRepository, permissionRepository); + Console.WriteLine($"成功创建 {roles.Count} 个角色"); + + // 3. 提交角色创建到数据库 + Console.WriteLine("提交角色创建到数据库..."); + await unitOfWork.SaveChangesAsync(); + Console.WriteLine("角色创建提交成功"); + + // 4. 为超级管理员分配所有权限(使用安全方法) + Console.WriteLine("开始为超级管理员分配权限..."); + var superAdminRole = await roleRepository.GetByNameAsync("超级管理员"); + if (superAdminRole != null) + { + await AssignAllPermissionsToRoleSafely(superAdminRole, permissionRepository); + Console.WriteLine("超级管理员权限分配完成"); + } + + // 5. 为管理员分配管理权限 + Console.WriteLine("开始为管理员分配权限..."); + var adminRole = await roleRepository.GetByNameAsync("管理员"); + if (adminRole != null) + { + await AssignAdminPermissionsToRoleSafely(adminRole, permissionRepository); + Console.WriteLine("管理员权限分配完成"); + } + + // 6. 为普通用户分配基本权限 + Console.WriteLine("开始为普通用户分配权限..."); + var normalUserRole = await roleRepository.GetByNameAsync("普通用户"); + if (normalUserRole != null) + { + await AssignBasicPermissionsToRoleSafely(normalUserRole, permissionRepository); + Console.WriteLine("普通用户权限分配完成"); + } + + // 7. 创建超级管理员用户 + await CreateSuperAdminAsync(userRepository, userInfoRepository, roleRepository, passwordHelper); + Console.WriteLine("成功创建超级管理员用户"); + + // 8. 创建管理员用户 + await CreateAdminUserAsync(userRepository, userInfoRepository, roleRepository, passwordHelper); + Console.WriteLine("成功创建管理员用户"); + + // 9. 提交所有更改到数据库 + Console.WriteLine("开始提交数据库更改..."); + await unitOfWork.BeginTransactionAsync(); + await unitOfWork.CommitAsync(); + Console.WriteLine("数据库更改提交成功"); + + _initialized = true; + Console.WriteLine("系统初始化完成"); + } + } + catch (Exception ex) + { + throw new InvalidOperationException("初始化系统失败", ex); + } + } + + private List CreateSystemPermissions() + { + var permissions = new List(); + + // 文件权限 + permissions.Add(Permission.CreateSystemPermission("文件上传", "file", (int)PermissionAction.Create)); + permissions.Add(Permission.CreateSystemPermission("文件下载", "file", (int)PermissionAction.Read)); + permissions.Add(Permission.CreateSystemPermission("文件删除", "file", (int)PermissionAction.Delete)); + + // 用户权限 + permissions.Add(Permission.CreateSystemPermission("创建用户", "user", (int)PermissionAction.Create)); + permissions.Add(Permission.CreateSystemPermission("查看用户", "user", (int)PermissionAction.Read)); + permissions.Add(Permission.CreateSystemPermission("更新用户", "user", (int)PermissionAction.Update)); + permissions.Add(Permission.CreateSystemPermission("删除用户", "user", (int)PermissionAction.Delete)); + + // 角色权限 + permissions.Add(Permission.CreateSystemPermission("创建角色", "role", (int)PermissionAction.Create)); + permissions.Add(Permission.CreateSystemPermission("查看角色", "role", (int)PermissionAction.Read)); + permissions.Add(Permission.CreateSystemPermission("更新角色", "role", (int)PermissionAction.Update)); + permissions.Add(Permission.CreateSystemPermission("删除角色", "role", (int)PermissionAction.Delete)); + + // 权限管理 + permissions.Add(Permission.CreateSystemPermission("创建权限", "permission", (int)PermissionAction.Create)); + permissions.Add(Permission.CreateSystemPermission("查看权限", "permission", (int)PermissionAction.Read)); + permissions.Add(Permission.CreateSystemPermission("更新权限", "permission", (int)PermissionAction.Update)); + permissions.Add(Permission.CreateSystemPermission("删除权限", "permission", (int)PermissionAction.Delete)); + + // 系统管理 + permissions.Add(Permission.CreateSystemPermission("系统管理", "system", (int)PermissionAction.Manage)); + + return permissions; + } + + private async Task> CreateRolesAsync(IRoleRepository roleRepository, IPermissionRepository permissionRepository) + { + var roles = new List(); + + // 检查是否已存在超级管理员角色 + var existingSuperAdmin = await roleRepository.GetByNameAsync("超级管理员"); + if (existingSuperAdmin == null) + { + // 1. 超级管理员角色 + var superAdminRole = Role.Create("超级管理员", "系统最高权限管理员", true, true); + Console.WriteLine($"创建超级管理员角色: {superAdminRole.Name.Value} (ID: {superAdminRole.RoleId.Value})"); + await roleRepository.AddAsync(superAdminRole); + roles.Add(superAdminRole); + } + else + { + Console.WriteLine($"超级管理员角色已存在: {existingSuperAdmin.Name.Value}"); + roles.Add(existingSuperAdmin); + } + + // 检查是否已存在管理员角色 + var existingAdmin = await roleRepository.GetByNameAsync("管理员"); + if (existingAdmin == null) + { + // 2. 管理员角色 + var adminRole = Role.Create("管理员", "系统管理员", true, false); + Console.WriteLine($"创建管理员角色: {adminRole.Name.Value} (ID: {adminRole.RoleId.Value})"); + await roleRepository.AddAsync(adminRole); + roles.Add(adminRole); + } + else + { + Console.WriteLine($"管理员角色已存在: {existingAdmin.Name.Value}"); + roles.Add(existingAdmin); + } + + // 检查是否已存在普通用户角色 + var existingNormalUser = await roleRepository.GetByNameAsync("普通用户"); + if (existingNormalUser == null) + { + // 3. 普通用户角色 + var normalUserRole = Role.Create("普通用户", "普通用户", true, false); + Console.WriteLine($"创建普通用户角色: {normalUserRole.Name.Value} (ID: {normalUserRole.RoleId.Value})"); + await roleRepository.AddAsync(normalUserRole); + roles.Add(normalUserRole); + } + else + { + Console.WriteLine($"普通用户角色已存在: {existingNormalUser.Name.Value}"); + roles.Add(existingNormalUser); + } + + Console.WriteLine("角色创建完成,暂时跳过权限分配"); + + return roles; + } + + private async Task AssignAllPermissionsToRoleSafely(Role role, IPermissionRepository permissionRepository) + { + Console.WriteLine($"开始为角色 {role.Name.Value} 分配所有权限..."); + + // 获取所有权限ID(避免实体跟踪冲突) + var allPermissions = await permissionRepository.GetAllAsync(); + var permissionIds = allPermissions.Select(p => p.PermissionId).ToList(); + + Console.WriteLine($"找到 {permissionIds.Count} 个权限"); + + // 为角色分配所有权限(使用ID而不是实体) + foreach (var permissionId in permissionIds) + { + if (!role.HasPermission(permissionId)) + { + // 通过ID查找权限实体,避免重复跟踪 + var permission = await permissionRepository.GetByGuidAsync(permissionId); + if (permission != null) + { + role.AddPermission(permission); + } + } + } + + Console.WriteLine($"成功为角色 {role.Name.Value} 分配了 {permissionIds.Count} 个权限"); + } + + private async Task AssignAdminPermissionsToRoleSafely(Role role, IPermissionRepository permissionRepository) + { + // 获取管理员需要的权限 + var adminPermissionCodes = new[] + { + "user:Create", "user:Read", "user:Update", "user:Delete", + "role:Create", "role:Read", "role:Update", "role:Delete", + "permission:Create", "permission:Read", "permission:Update", "permission:Delete", + "system:Manage", + "file:Read","file:Create","file:Delete" + }; + + var adminPermissionIds = new List(); + foreach (var code in adminPermissionCodes) + { + var permission = await permissionRepository.GetByCodeAsync(PermissionCode.Create(code)); + if (permission != null) + { + adminPermissionIds.Add(permission.PermissionId); + } + } + + // 为角色分配权限(使用ID而不是实体) + foreach (var permissionId in adminPermissionIds) + { + if (!role.HasPermission(permissionId)) + { + // 通过ID查找权限实体,避免重复跟踪 + var permission = await permissionRepository.GetByGuidAsync(permissionId); + if (permission != null) + { + role.AddPermission(permission); + } + } + } + Console.WriteLine($"为角色 {role.Name.Value} 分配了 {adminPermissionIds.Count} 个管理权限"); + } + + private async Task AssignBasicPermissionsToRoleSafely(Role role, IPermissionRepository permissionRepository) + { + // 获取普通用户需要的基本权限 + var basicPermissionCodes = new[] + { + "user:Read", "role:Read", "permission:Read", + "file:Create", "file:Read" + }; + + var basicPermissionIds = new List(); + foreach (var code in basicPermissionCodes) + { + var permission = await permissionRepository.GetByCodeAsync(PermissionCode.Create(code)); + if (permission != null) + { + basicPermissionIds.Add(permission.PermissionId); + } + } + + // 为角色分配权限(使用ID而不是实体) + foreach (var permissionId in basicPermissionIds) + { + if (!role.HasPermission(permissionId)) + { + // 通过ID查找权限实体,避免重复跟踪 + var permission = await permissionRepository.GetByGuidAsync(permissionId); + if (permission != null) + { + role.AddPermission(permission); + } + } + } + Console.WriteLine($"为角色 {role.Name.Value} 分配了 {basicPermissionIds.Count} 个基本权限"); + } + + private async Task CreateSuperAdminAsync( + IUserRepository userRepository, + IUserInfoRepository userInfoRepository, + IRoleRepository roleRepository, + IPasswordHelper passwordHelper) + { + // 检查是否已存在超级管理员 + var existingSuperAdmin = await userRepository.GetUserByAccountAsync(UserAccount.Create("admin123")); + if (existingSuperAdmin != null) + { + Console.WriteLine("超级管理员已存在,跳过创建"); + return; + } + + Console.WriteLine("开始创建超级管理员..."); + + // 创建用户信息 + var userInfo = UserInfo.CreateUserInfo( + "超级管理员", + UserGender.Man, + null, + "系统超级管理员" + ); + await userInfoRepository.AddAsync(userInfo); + Console.WriteLine("用户信息创建成功"); + + // 获取超级管理员角色 + Console.WriteLine("开始查找超级管理员角色..."); + var superAdminRole = await roleRepository.GetByNameAsync("超级管理员"); + if (superAdminRole == null) + { + // 尝试获取所有角色来调试 + var allRoles = await roleRepository.GetAllAsync(); + Console.WriteLine($"数据库中所有角色数量: {allRoles.Count()}"); + foreach (var role in allRoles) + { + Console.WriteLine($"角色: {role.Name.Value} (ID: {role.RoleId.Value})"); + } + + // 尝试直接通过ID查找 + Console.WriteLine("尝试通过ID查找角色..."); + var rolesById = await roleRepository.GetAllRolesWithPermissionsAsync(); + foreach (var role in rolesById) + { + Console.WriteLine($"通过ID找到角色: {role.Name.Value} (ID: {role.RoleId.Value})"); + } + + throw new InvalidOperationException("超级管理员角色不存在"); + } + Console.WriteLine($"获取超级管理员角色成功: {superAdminRole.Name.Value} (ID: {superAdminRole.RoleId.Value})"); + + // 加密密码 + var (hashedPassword, salt) = passwordHelper.HashPasswordWithSeparateSalt("admin123"); + + // 创建超级管理员用户 + var superAdmin = User.CreateUser( + userInfo.UserInfoId, + "admin123", // 修改为符合格式要求的账号 + hashedPassword, // 加密后的密码 + "admin@system.com", + salt, // 加密盐值 + UserStatus.Normal, + superAdminRole.RoleId.Value + ); + + await userRepository.AddAsync(superAdmin); + Console.WriteLine("超级管理员用户创建成功"); + + Console.WriteLine("超级管理员创建成功"); + Console.WriteLine("账号: admin123"); + Console.WriteLine("密码: admin123"); + } + + private async Task CreateAdminUserAsync( + IUserRepository userRepository, + IUserInfoRepository userInfoRepository, + IRoleRepository roleRepository, + IPasswordHelper passwordHelper) + { + // 检查是否已存在管理员用户 + var existingAdmin = await userRepository.GetUserByAccountAsync(UserAccount.Create("manager")); + if (existingAdmin != null) + { + Console.WriteLine("管理员用户已存在,跳过创建"); + return; + } + + Console.WriteLine("开始创建管理员用户..."); + + // 创建用户信息 + var userInfo = UserInfo.CreateUserInfo( + "系统管理员", + UserGender.Man, + null, + "系统管理员,负责日常系统管理" + ); + await userInfoRepository.AddAsync(userInfo); + Console.WriteLine("管理员用户信息创建成功"); + + // 获取管理员角色 + Console.WriteLine("开始查找管理员角色..."); + var adminRole = await roleRepository.GetByNameAsync("管理员"); + if (adminRole == null) + { + // 尝试获取所有角色来调试 + var allRoles = await roleRepository.GetAllAsync(); + Console.WriteLine($"数据库中所有角色数量: {allRoles.Count()}"); + foreach (var role in allRoles) + { + Console.WriteLine($"角色: {role.Name.Value} (ID: {role.RoleId.Value})"); + } + + throw new InvalidOperationException("管理员角色不存在"); + } + Console.WriteLine($"获取管理员角色成功: {adminRole.Name.Value} (ID: {adminRole.RoleId.Value})"); + + // 加密密码 + var (hashedPassword, salt) = passwordHelper.HashPasswordWithSeparateSalt("manager123"); + + // 创建管理员用户 + var admin = User.CreateUser( + userInfo.UserInfoId, + "manager", // 管理员账号 + hashedPassword, // 加密后的密码 + "manager@system.com", + salt, // 加密盐值 + UserStatus.Normal, + adminRole.RoleId.Value + ); + + await userRepository.AddAsync(admin); + Console.WriteLine("管理员用户创建成功"); + + Console.WriteLine("管理员用户创建成功"); + Console.WriteLine("账号: manager"); + Console.WriteLine("密码: manager123"); + Console.WriteLine("角色: 管理员"); + } + + /// + /// 清理重复的权限数据 + /// + private async Task CleanupDuplicatePermissionsAsync(IPermissionRepository permissionRepository) + { + try + { + Console.WriteLine("开始清理重复的权限数据..."); + + // 获取所有权限 + var allPermissions = await permissionRepository.GetAllAsync(); + var permissionGroups = allPermissions.GroupBy(p => p.Code.Value).ToList(); + + var duplicates = permissionGroups.Where(g => g.Count() > 1).ToList(); + + if (duplicates.Any()) + { + Console.WriteLine($"发现 {duplicates.Count} 个重复的权限代码"); + + foreach (var group in duplicates) + { + Console.WriteLine($"权限代码 '{group.Key}' 有 {group.Count()} 个重复项"); + + // 保留第一个,删除其他的 + var toKeep = group.First(); + var toDelete = group.Skip(1).ToList(); + + Console.WriteLine($"保留权限ID: {toKeep.PermissionId}, 删除 {toDelete.Count} 个重复项"); + + // 这里需要实现删除方法,暂时跳过 + // foreach (var perm in toDelete) + // { + // await permissionRepository.DeleteAsync(perm.PermissionId); + // } + } + } + else + { + Console.WriteLine("没有发现重复的权限数据"); + } + } + catch (Exception ex) + { + Console.WriteLine($"清理重复权限时出错: {ex.Message}"); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemPermissionConfigLoader.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemPermissionConfigLoader.cs new file mode 100644 index 0000000..b43ab2d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemPermissionConfigLoader.cs @@ -0,0 +1,83 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using UniversalAdminSystem.Infrastructure.Configs; + +namespace UniversalAdminSystem.Infrastructure.Services; + +public sealed class SystemPermissionConfigLoader(ILogger logger) +{ + private readonly ILogger _logger = logger; + + /// + /// json反序列化规则 + /// + private static readonly JsonSerializerOptions SerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名使用驼峰命名 + AllowTrailingCommas = true, // 允许 JSON 文件中存在尾随逗号 + ReadCommentHandling = JsonCommentHandling.Skip, // 跳过注释 + }; + + /// + /// 异步加载配置文件 + /// + /// + /// + /// + /// + public async Task> LoadFromFileAsync(string filePath) + { + // 文件存在性检查 + if (!File.Exists(filePath)) + { + _logger.LogError("系统权限配置文件未找到", [filePath]); + throw new FileNotFoundException("系统权限配置文件未找到", filePath); + } + + // 读取并解析JSON + try + { + await using var stream = File.OpenRead(filePath); // 打开文件流 + var configRoot = await JsonSerializer.DeserializeAsync( // 开始异步JSON反序列化,完成并关闭文件流 + stream, + SerializerOptions + ); + + if (configRoot?.Permissions is null || configRoot.Permissions.Count == 0) + { + _logger.LogWarning("配置文件中未找到有效的系统权限", [filePath]); + return []; + } + + // 校验系统权限配置集合 + ValidateConfigs(configRoot.Permissions); + + return configRoot.Permissions; + } + catch (JsonException ex) + { + _logger.LogError(ex, "JSON解析错误", [filePath]); + throw new InvalidOperationException("JSON 结构与目标类型不匹配", ex); + } + } + + /// + /// 校验配置集合的有效性 + /// + /// + private void ValidateConfigs(IEnumerable configs) + { + foreach (var config in configs) + { + if (string.IsNullOrWhiteSpace(config.Resource)) + { + _logger.LogWarning("权限配置缺少资源", [config]); + } + + // 利用 System.ComponentModel.DataAnnotations 的验证 + var context = new ValidationContext(config); + Validator.ValidateObject(config, context, validateAllProperties: true); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj new file mode 100644 index 0000000..c7d12bd --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + net8.0 + enable + enable + + + -- Gitee From e7ef1993a6440f307da554b8ecd9d1b8497e2cff Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Thu, 7 Aug 2025 16:07:14 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E5=AE=9E=E4=BD=93=EF=BC=8C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8Capi=E5=AE=8C=E6=88=90=EF=BC=8C=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E8=AE=BE=E8=AE=A1=E5=AE=8C=E6=88=90=E5=B0=9A=E6=9C=89?= =?UTF-8?q?=E6=9C=AA=E5=AE=9E=E7=8E=B0=E6=96=B9=E6=B3=95=EF=BC=8C=E7=AD=89?= =?UTF-8?q?=E5=BE=85AI=E5=B7=A5=E5=85=B7=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/AIQuestionsController.cs | 76 +++++++++++++++++++ .../PermissionRules.json | 8 +- .../UniversalAdminSystem.Api/appsettings.json | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs new file mode 100644 index 0000000..b157abc --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Mvc; +using UniversalAdminSystem.Api.Attributes; +using UniversalAdminSystem.Application.AIQuestions.DTOs; +using UniversalAdminSystem.Application.AIQuestions.Interfaces; +using UniversalAdminSystem.Application.Common.Results; + +namespace UniversalAdminSystem.Api.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class AIQuestionsController : ControllerBase +{ + private readonly IAIQusetionsAppService _AIQusetionAppService; + + public AIQuestionsController(IAIQusetionsAppService aIQusetionsAppService) + { + _AIQusetionAppService = aIQusetionsAppService; + } + + [HttpPost("visitor")] + public Task VisitorAccess(ContentDto content) + { + + } + + [HttpPost("user")] + [RequirePermission("document:Read")] + public async Task CreateUserConversation(ConversationsDto conversationsDto) + { + try + { + var result = await _AIQusetionAppService.CreateConversation(conversationsDto); + return Ok(Result.Success(result)); + } + catch (System.Exception e) + { + return BadRequest(Result.Failure(e.Message)); + } + } + + [HttpPost("user/{id}")] + [RequirePermission("document:Read")] + public Task UserAccessById(Guid id,ContentDto content) + { + + } + + [HttpGet("user/{userid}")] + [RequirePermission("document:Read")] + public async Task GetUsersConversationsByUserId(Guid userid) + { + try + { + var list = await _AIQusetionAppService.GetUsersConversationsByUserId(userid); + return Ok(Result>.Success(list)); + } + catch (System.Exception e) + { + return BadRequest(Result.Failure(e.Message)); + } + } + + [HttpDelete("user/{ConversationsId}")] + public async Task DeleteUserConversations(Guid ConversationsId) + { + try + { + await _AIQusetionAppService.DeleteUserConversations(ConversationsId); + return Ok(Result.Success()); + } + catch (System.Exception e) + { + return BadRequest(Result.Failure(e.Message)); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/PermissionRules.json b/backend/src/UniversalAdminSystem.Api/PermissionRules.json index 234271e..7e0ed8b 100644 --- a/backend/src/UniversalAdminSystem.Api/PermissionRules.json +++ b/backend/src/UniversalAdminSystem.Api/PermissionRules.json @@ -1,10 +1,14 @@ { - "Read": ["data", "file", "user", "role", "permission", "config", "system", "report"], + "Read": ["data", "file", "user", "role", "permission", "config", "system", "report","document"], "Create": ["data", "user", "file", "role", "permission"], "Update": ["data", "user", "config", "role", "permission"], "Delete": ["data", "user", "file", "role", "permission"], "Manage": ["system", "user"], "Execute": ["job", "script"], "Import": ["data", "file"], - "Export": ["data", "file", "report"] + "Export": ["data", "file", "report"], + "Public": ["document"], + "Restricted": ["document"], + "Private": ["document"] + } diff --git a/backend/src/UniversalAdminSystem.Api/appsettings.json b/backend/src/UniversalAdminSystem.Api/appsettings.json index 5a0c1dd..3b56ca8 100644 --- a/backend/src/UniversalAdminSystem.Api/appsettings.json +++ b/backend/src/UniversalAdminSystem.Api/appsettings.json @@ -8,7 +8,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "pgSql": "server=127.0.0.1;port=5432;uid=postgres;password=031028@yue;database=universal_admin" + "pgSql": "server=127.0.0.1;port=5433;uid=postgres;password=123456;database=universal_admin" }, "Jwt": { "Key": "YourSuperSecretKey1232347509872093oiqewupori", -- Gitee From 245836a6573fc1430529913e12decd87d5364e39 Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Thu, 7 Aug 2025 16:07:32 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E5=AE=9E=E4=BD=93=EF=BC=8C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8Capi=E5=AE=8C=E6=88=90=EF=BC=8C=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E8=AE=BE=E8=AE=A1=E5=AE=8C=E6=88=90=E5=B0=9A=E6=9C=89?= =?UTF-8?q?=E6=9C=AA=E5=AE=9E=E7=8E=B0=E6=96=B9=E6=B3=95=EF=BC=8C=E7=AD=89?= =?UTF-8?q?=E5=BE=85AI=E5=B7=A5=E5=85=B7=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AIQuestions/DTOs/ContentDto.cs | 3 + .../AIQuestions/DTOs/ContentResultDto.cs | 3 + .../AIQuestions/DTOs/ConversationsDto.cs | 3 + .../DTOs/ConversationsResultDto.cs | 3 + .../Interfaces/IAIQusetionsAppService.cs | 20 + .../Service/AIQusetionsAppService.cs | 98 +++++ .../Core/ValueObjects/ChunkId.cs | 20 + .../Core/ValueObjects/ConversationId.cs | 22 + .../Core/ValueObjects/MessageId.cs | 22 + .../ValueObjects/FileAccessLevel.cs | 6 +- .../ValueObjects/PermissionAction.cs | 8 +- .../Aggregates/Conversations.cs | 45 ++ .../UserConversations/Aggregates/Message.cs | 43 ++ .../IRepository/IConversationsRepository.cs | 29 ++ .../IRepository/IMessageRepository.cs | 21 + .../knowledge/Aggregates/DocumentChunk.cs | 48 +++ .../IRepository/IDocumentChunkRepository.cs | 40 ++ .../knowledge/ValueObj/TextEmbedding.cs | 20 + .../DependencyInject/AddApplicationService.cs | 4 + .../AddInfrastructureService.cs | 7 +- .../20250807030354_RAG1.Designer.cs | 392 ++++++++++++++++++ .../Migrations/20250807030354_RAG1.cs | 291 +++++++++++++ ...versalAdminSystemDbContextModelSnapshot.cs | 389 +++++++++++++++++ .../UniversalAdminSystemDbContext.cs | 72 +++- .../Repositories/ConversationsRepository.cs | 29 ++ .../Repositories/DocumentChunkRepository.cs | 48 +++ .../Repositories/MessageRepository.cs | 26 ++ .../Services/SystemInitializationService.cs | 10 +- ...UniversalAdminSystem.Infrastructure.csproj | 1 + 29 files changed, 1714 insertions(+), 9 deletions(-) create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentResultDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsResultDto.cs create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/Interfaces/IAIQusetionsAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ChunkId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ConversationId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/MessageId.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Conversations.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Message.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IConversationsRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IMessageRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/knowledge/Aggregates/DocumentChunk.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/knowledge/IRepository/IDocumentChunkRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Domian/knowledge/ValueObj/TextEmbedding.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.Designer.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentDto.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentDto.cs new file mode 100644 index 0000000..9bdf028 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.AIQuestions.DTOs; + +public record ContentDto(string Content); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentResultDto.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentResultDto.cs new file mode 100644 index 0000000..98786f9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ContentResultDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.AIQuestions.DTOs; + +public record ContentResultDto(string Role,string Content); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsDto.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsDto.cs new file mode 100644 index 0000000..e28eaf9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.AIQuestions.DTOs; + +public record ConversationsDto(Guid UserId,string Title); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsResultDto.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsResultDto.cs new file mode 100644 index 0000000..1349ba6 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/DTOs/ConversationsResultDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.AIQuestions.DTOs; + +public record ConversationsResultDto(Guid ConversationId,string? Title); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/Interfaces/IAIQusetionsAppService.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/Interfaces/IAIQusetionsAppService.cs new file mode 100644 index 0000000..54072f3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/Interfaces/IAIQusetionsAppService.cs @@ -0,0 +1,20 @@ +using UniversalAdminSystem.Application.AIQuestions.DTOs; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; + +namespace UniversalAdminSystem.Application.AIQuestions.Interfaces; + +public interface IAIQusetionsAppService +{ + + Task> GetUsersConversationsByUserId(Guid userId); + + Task DeleteUserConversations(Guid ConversationsId); + + Task VisitorAccess(ContentDto content); + + Task UserAccess(Guid id, ContentDto content); + + Task> GetConversationMessage(Guid Id); + + Task CreateConversation(ConversationsDto conversationsDto); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs new file mode 100644 index 0000000..6ad0b4b --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs @@ -0,0 +1,98 @@ +using UniversalAdminSystem.Application.AIQuestions.DTOs; +using UniversalAdminSystem.Application.AIQuestions.Interfaces; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Application.PermissionManagement.Interfaces; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; +using UniversalAdminSystem.Domian.UserConversations.IRepository; +using UniversalAdminSystem.Domian.UserManagement.IRepository; + +namespace UniversalAdminSystem.Application.AIQuestions.Service; + +public class AIQusetionsAppService : IAIQusetionsAppService +{ + private readonly IMessageRepository _messageRepo; + + private readonly IConversationsRepository _conversationsRepo; + + private readonly IPermissionCheckService _permissioncheck; + + private readonly IUnitOfWork _work; + + public AIQusetionsAppService(IMessageRepository message, IConversationsRepository conversations, IPermissionCheckService permissioncheck, IUnitOfWork work) + { + _messageRepo = message; + _conversationsRepo = conversations; + _permissioncheck = permissioncheck; + _work = work; + + } + + public async Task CreateConversation(ConversationsDto conversationsDto) + { + try + { + await _work.BeginTransactionAsync(); + var Conversation = Conversations.Create(conversationsDto.UserId, conversationsDto.Title); + var entity = await _conversationsRepo.AddAsync(Conversation); + await _work.CommitAsync(); + return new ConversationsResultDto(entity.Id.Value, entity.Title); + } + catch (System.Exception) + { + await _work.RollbackAsync(); + throw new Exception("用户会话会话创建失败"); + } + } + + public async Task DeleteUserConversations(Guid ConversationsId) + { + try + { + await _work.BeginTransactionAsync(); + await _messageRepo.RemoveByConversationIdAsync(ConversationsId); + await _conversationsRepo.RemoveConversation(ConversationsId); + await _work.CommitAsync(); + } + catch (System.Exception) + { + await _work.RollbackAsync(); + throw new Exception("删除异常"); + } + } + + public async Task> GetConversationMessage(Guid Id) + { + try + { + var list = await _messageRepo.GetByConversationIdAsync(Id); + return list.Select(m => new ContentResultDto(m.Role,m.Content)); + } + catch (System.Exception) + { + throw new Exception("获取用户会话消息失败"); + } + } + + public async Task> GetUsersConversationsByUserId(Guid userId) + { + try + { + var list = await _conversationsRepo.GetByUserIdAsync(userId); + return list.Select(m => new ConversationsResultDto(m.Id.Value, m.Title)); + } + catch (System.Exception) + { + throw new Exception("获取用户会话失败"); + } + } + + public Task UserAccess(Guid id, ContentDto content) + { + throw new NotImplementedException(); + } + + public Task VisitorAccess(ContentDto content) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ChunkId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ChunkId.cs new file mode 100644 index 0000000..858b370 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ChunkId.cs @@ -0,0 +1,20 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +public record ChunkId +{ + public Guid Value { get; init; } + private ChunkId(Guid value) + { + if (Guid.Empty == value) + { + throw new ArgumentException("ChunkId不能为空"); + } + + Value = value; + } + + public static ChunkId Create(Guid Id) => new(Id); + + public static explicit operator ChunkId(Guid value) => Create(value); + public static implicit operator Guid(ChunkId Id) => Id.Value; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ConversationId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ConversationId.cs new file mode 100644 index 0000000..9361c39 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/ConversationId.cs @@ -0,0 +1,22 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +public record ConversationId +{ + public Guid Value { get; init; } + + private ConversationId(Guid value) + { + if (value == Guid.Empty) + { + throw new ArgumentException("ConversationId不能为空"); + } + Value = value; + } + + public static ConversationId Create(Guid value) + { + return new ConversationId(value); + } + + public override string ToString() => Value.ToString(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/MessageId.cs b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/MessageId.cs new file mode 100644 index 0000000..2a2e6a7 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/Core/ValueObjects/MessageId.cs @@ -0,0 +1,22 @@ +namespace UniversalAdminSystem.Domian.Core.ValueObjects; + +public record MessageId +{ + public Guid Value { get; init; } + + private MessageId(Guid value) + { + if (value == Guid.Empty) + { + throw new ArgumentException("MessageId不能为空"); + } + Value = value; + } + + public static MessageId Create(Guid value) + { + return new MessageId(value); + } + + public override string ToString() => Value.ToString(); +} diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs index 7941d9a..42fb077 100644 --- a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileAccessLevel.cs @@ -2,7 +2,7 @@ namespace UniversalAdminSystem.Domian.FileStorage.ValueObjects; public enum FileAccessLevel { - Private = 0, - Public = 1, - Restricted = 2 + Public, + Restricted, + Private, } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs index 7eefe34..b8fdaa2 100644 --- a/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs +++ b/backend/src/UniversalAdminSystem.Domian/PermissionManagement/ValueObjects/PermissionAction.cs @@ -18,7 +18,13 @@ public enum PermissionAction Import, // 导入数据 Export, // 导出数据 Restore, // 恢复数据 - Archive // 归档数据 + Archive, // 归档数据 + + // 可访问文档权限等级 + Public, //公开 + Restricted, //受限 + Private //私有 + } /// diff --git a/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Conversations.cs b/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Conversations.cs new file mode 100644 index 0000000..8efcd71 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Conversations.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using UniversalAdminSystem.Domian.Core.ValueObjects; + +namespace UniversalAdminSystem.Domian.UserConversations.Aggregates; + +public class Conversations +{ + public ConversationId Id { get; private set; } = null!; + + public UserId UserId { get; private set; } = null!; + + public string? Title { get; private set; } + + public DateTime CreateDate { get; private set; } + + public DateTime UpdateDate { get; private set; } + + protected Conversations() { } + + private Conversations(ConversationId id, UserId userid, string? title, DateTime createDate, DateTime updateDate) + { + Id = id; + UserId = userid; + Title = title; + CreateDate = createDate; + UpdateDate = updateDate; + } + + public static Conversations Create(Guid userid, string? title) + { + if (userid == Guid.Empty) throw new ArgumentException("用户id不能为空"); + return new Conversations( + ConversationId.Create(Guid.NewGuid()), + UserId.Create(userid), + title, + DateTime.UtcNow, + DateTime.UtcNow + ); + } + + public void UpdateUpdateDate(DateTime time) + { + UpdateDate = time; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Message.cs b/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Message.cs new file mode 100644 index 0000000..c6ff4ff --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserConversations/Aggregates/Message.cs @@ -0,0 +1,43 @@ +using System.Data.Common; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.PermissionManagement.ValueObjects; + +namespace UniversalAdminSystem.Domian.UserConversations.Aggregates; + +public class Message +{ + public MessageId Id { get; private set; } = null!; + + public ConversationId ConversationId { get; private set; } = null!; + + public string Role { get; private set; } = null!; + + public string Content { get; private set; } = null!; + + public DateTime CreateDate { get; private set; } + + protected Message() { } + + private Message(MessageId id, ConversationId conversationId, string role, string content, DateTime time) + { + Id = id; + ConversationId = conversationId; + Role = role; + Content = content; + CreateDate = time; + + } + + public static Message Create(ConversationId conversationId, string role, string content) + { + if (role == null || role.Length == 0) throw new ArgumentException("角色不得为空"); + if (content == null || content.Length == 0) throw new ArgumentException("内容不得为空"); + return new Message( + MessageId.Create(Guid.NewGuid()), + conversationId, + role, + content, + DateTime.UtcNow + ); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IConversationsRepository.cs b/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IConversationsRepository.cs new file mode 100644 index 0000000..d9e3b49 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IConversationsRepository.cs @@ -0,0 +1,29 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; + +namespace UniversalAdminSystem.Domian.UserConversations.IRepository; + +public interface IConversationsRepository : IRepository +{ + /// + /// 获取所有用户Id的会话 + /// + /// + /// + Task> GetByUserIdAsync(Guid userId); + + /// + /// 删除所有用户id的会话 + /// + /// + /// + Task RemoveByUserIdAsync(Guid userId); + + + /// + /// 删除会话及相关消息 + /// + /// + /// + Task RemoveConversation(Guid ConversationId); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IMessageRepository.cs b/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IMessageRepository.cs new file mode 100644 index 0000000..faa6beb --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/UserConversations/IRepository/IMessageRepository.cs @@ -0,0 +1,21 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; + +namespace UniversalAdminSystem.Domian.UserConversations.IRepository; + +public interface IMessageRepository : IRepository +{ + /// + /// 获取某个会话的所有消息 + /// + /// + /// + Task> GetByConversationIdAsync(Guid conversationId); + + /// + /// 删除某个会话的所有消息 + /// + /// + /// + Task RemoveByConversationIdAsync(Guid conversationId); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/knowledge/Aggregates/DocumentChunk.cs b/backend/src/UniversalAdminSystem.Domian/knowledge/Aggregates/DocumentChunk.cs new file mode 100644 index 0000000..99a063b --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/knowledge/Aggregates/DocumentChunk.cs @@ -0,0 +1,48 @@ +using UniversalAdminSystem.Domian.Core; +using UniversalAdminSystem.Domian.Core.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.knowledge.ValueObj; + +namespace UniversalAdminSystem.Domian.knowledge.Aggregates; + + +public class DocumentChunk : AggregateRoot +{ + public ChunkId Id { get; private set; } = null!; + + public FileId FileId { get; private set; } = null!; + + public string Content { get; private set; } = string.Empty; + + public TextEmbedding Embedding { get; private set; } = null!; + + public FileAccessLevel Level { get; private set; } + + protected DocumentChunk() { } + + private DocumentChunk(ChunkId id, FileId fileId, string content, TextEmbedding embedding,FileAccessLevel level) + { + Id = id; + FileId = fileId; + Content = content; + Embedding = embedding; + Level = level; + } + + public static DocumentChunk CreateDocumentChunk(ChunkId id, FileId fileId, string content, TextEmbedding embedding,FileAccessLevel level = FileAccessLevel.Public) + { + if (string.IsNullOrWhiteSpace(content)) + { + throw new ArgumentException("内容不能为空"); + } + + if (embedding == null) + { + throw new ArgumentException("嵌入向量不能为空"); + } + + return new DocumentChunk(id, fileId, content, embedding,level); + } + + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/knowledge/IRepository/IDocumentChunkRepository.cs b/backend/src/UniversalAdminSystem.Domian/knowledge/IRepository/IDocumentChunkRepository.cs new file mode 100644 index 0000000..5258578 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/knowledge/IRepository/IDocumentChunkRepository.cs @@ -0,0 +1,40 @@ +using UniversalAdminSystem.Domian.Core.Interfaces; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.knowledge.Aggregates; +using UniversalAdminSystem.Domian.knowledge.ValueObj; + +namespace UniversalAdminSystem.Domian.knowledge.IRepository; + +public interface IDocumentChunkRepository : IRepository +{ + /// + /// 数据库中匹配余弦值的前topK个相似文档 + /// + /// + /// + /// + /// + Task> FindSimilarDocumentsAsync(TextEmbedding queryEmbedding, FileAccessLevel level, int topK = 5); + + /// + /// 批量文件id删除 + /// + /// + /// + Task BatchDeleteDocumentChunkAsync(Guid Id); + + /// + /// 以id批量修改文本块等级 + /// + /// + /// + /// + Task BatchModificationChunkLevelAsync(Guid FileId, FileAccessLevel level); + + /// + /// 批量添加文本块 + /// + /// + /// + Task BulkAddDocumentChunkAsync(IEnumerable chunks); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Domian/knowledge/ValueObj/TextEmbedding.cs b/backend/src/UniversalAdminSystem.Domian/knowledge/ValueObj/TextEmbedding.cs new file mode 100644 index 0000000..ad08a0d --- /dev/null +++ b/backend/src/UniversalAdminSystem.Domian/knowledge/ValueObj/TextEmbedding.cs @@ -0,0 +1,20 @@ +namespace UniversalAdminSystem.Domian.knowledge.ValueObj; + +public record TextEmbedding +{ + public float[] Value { get; init; } + + private TextEmbedding(float[] value) + { + if (value == null) + { + throw new ArgumentException("向量不得为空"); + } + + Value = value; + } + + public static TextEmbedding Create(float[] Vector) => new(Vector); + + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs index 9aaf2b3..4ff51a5 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs @@ -17,6 +17,8 @@ using UniversalAdminSystem.Application.SystemSettings.Interfaces; using UniversalAdminSystem.Application.SystemSettings.Services; using UniversalAdminSystem.Application.FileStorage.Interfaces; using UniversalAdminSystem.Application.FileStorage.Services; +using UniversalAdminSystem.Application.AIQuestions.Interfaces; +using UniversalAdminSystem.Application.AIQuestions.Service; namespace UniversalAdminSystem.Infrastructure.DependencyInject; @@ -60,6 +62,7 @@ public static class AddApplicationService typeof(ILogManagementAppService).Assembly, typeof(ISystemSettingAppService).Assembly, typeof(IPermissionManagementAppService).Assembly, + typeof(IAIQusetionsAppService).Assembly }, new Assembly[] { @@ -69,6 +72,7 @@ public static class AddApplicationService typeof(LogManagementAppService).Assembly, typeof(SystemSettingAppService).Assembly, typeof(PermissionManagementAppService).Assembly, + typeof(AIQusetionsAppService).Assembly } ); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs index 2026d77..fdeb6cb 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs @@ -20,6 +20,8 @@ using UniversalAdminSystem.Application.PermissionManagement.Services; using UniversalAdminSystem.Application.Authentication.Interfaces; using UniversalAdminSystem.Infrastructure.FileStorage; using FluentValidation; +using UniversalAdminSystem.Domian.UserConversations.IRepository; +using UniversalAdminSystem.Domian.knowledge.IRepository; namespace UniversalAdminSystem.Infrastructure.DependencyInject; @@ -54,7 +56,7 @@ public static class AddInfrastrutureService public static IServiceCollection AddInfrastruture(this IServiceCollection services, IConfiguration configuration) { // 注册数据库上下文 - services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"))); + services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"),x=>x.UseVector())); // 注册JWT配置 services.Configure(configuration.GetSection("Jwt")); @@ -70,6 +72,9 @@ public static class AddInfrastrutureService typeof(IFileRepository).Assembly, typeof(ILogEntryRepository).Assembly, typeof(ISystemSettingRepository).Assembly, + typeof(IConversationsRepository).Assembly, + typeof(IDocumentChunkRepository).Assembly, + }, new Assembly[] { diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.Designer.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.Designer.cs new file mode 100644 index 0000000..9d2ff06 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.Designer.cs @@ -0,0 +1,392 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Pgvector; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +#nullable disable + +namespace UniversalAdminSystem.Infrastructure.Migrations +{ + [DbContext(typeof(UniversalAdminSystemDbContext))] + [Migration("20250807030354_RAG1")] + partial class RAG1 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("RolePermissions", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("PermissionId") + .HasColumnType("uuid"); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId"); + + b.ToTable("RolePermissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.FileStorage.Aggregates.File", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessLevel") + .HasColumnType("integer"); + + b.Property("IsFolder") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityCheckResult") + .HasColumnType("text"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UploadTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.LogManagement.Aggregates.LogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Context") + .HasColumnType("text"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("Level") + .IsRequired() + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Source") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("LogEntries"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PermissionType") + .HasColumnType("integer"); + + b.Property("Resource") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSupper") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RoleId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.SystemSettings.Aggregates.SystemSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Group") + .HasColumnType("text"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SystemSettings"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Conversations", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("UpdateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Conversations"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Message", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Account") + .IsRequired() + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.HasKey("UserId"); + + b.HasIndex("Account") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Entities.UserInfo", b => + { + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.HasKey("UserInfoId"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.knowledge.Aggregates.DocumentChunk", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("Embedding") + .IsRequired() + .HasColumnType("vector(1536)"); + + b.Property("FileId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Chunks"); + }); + + modelBuilder.Entity("RolePermissions", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", null) + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.SetNull); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.cs new file mode 100644 index 0000000..326eafe --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250807030354_RAG1.cs @@ -0,0 +1,291 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Pgvector; + +#nullable disable + +namespace UniversalAdminSystem.Infrastructure.Migrations +{ + /// + public partial class RAG1 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:vector", ",,"); + + migrationBuilder.CreateTable( + name: "Chunks", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + FileId = table.Column(type: "uuid", nullable: false), + Content = table.Column(type: "text", nullable: false), + Embedding = table.Column(type: "vector(1536)", nullable: false), + Level = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Chunks", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Conversations", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Title = table.Column(type: "text", nullable: true), + CreateDate = table.Column(type: "timestamp with time zone", nullable: false), + UpdateDate = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Conversations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Files", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Path = table.Column(type: "text", nullable: false), + Size = table.Column(type: "bigint", nullable: false), + Type = table.Column(type: "text", nullable: false), + OwnerId = table.Column(type: "uuid", nullable: false), + UploadTime = table.Column(type: "timestamp with time zone", nullable: false), + IsFolder = table.Column(type: "boolean", nullable: false), + ParentId = table.Column(type: "uuid", nullable: true), + AccessLevel = table.Column(type: "integer", nullable: false), + SecurityCheckResult = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Files", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "LogEntries", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Level = table.Column(type: "text", nullable: false), + Message = table.Column(type: "text", nullable: false), + Source = table.Column(type: "text", nullable: false), + UserId = table.Column(type: "uuid", nullable: true), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Context = table.Column(type: "text", nullable: true), + Exception = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LogEntries", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Messages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ConversationId = table.Column(type: "uuid", nullable: false), + Role = table.Column(type: "text", nullable: false), + Content = table.Column(type: "text", nullable: false), + CreateDate = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Messages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Permissions", + columns: table => new + { + PermissionId = table.Column(type: "uuid", nullable: false), + Code = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: true), + PermissionType = table.Column(type: "integer", nullable: false), + Resource = table.Column(type: "text", nullable: false), + Action = table.Column(type: "integer", nullable: false), + IsSystem = table.Column(type: "boolean", nullable: false), + CreateTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdateTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Permissions", x => x.PermissionId); + }); + + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + RoleId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: true), + IsSystem = table.Column(type: "boolean", nullable: false), + IsSupper = table.Column(type: "boolean", nullable: false), + CreateTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdateTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.RoleId); + }); + + migrationBuilder.CreateTable( + name: "SystemSettings", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Key = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: true), + Group = table.Column(type: "text", nullable: true), + CreateTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdateTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SystemSettings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "UserInfos", + columns: table => new + { + UserInfoId = table.Column(type: "uuid", nullable: false), + Gender = table.Column(type: "integer", nullable: true), + Age = table.Column(type: "integer", nullable: true), + Address = table.Column(type: "text", nullable: true), + Name = table.Column(type: "text", nullable: true), + Phone = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserInfos", x => x.UserInfoId); + }); + + migrationBuilder.CreateTable( + name: "RolePermissions", + columns: table => new + { + RoleId = table.Column(type: "uuid", nullable: false), + PermissionId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RolePermissions", x => new { x.RoleId, x.PermissionId }); + table.ForeignKey( + name: "FK_RolePermissions_Permissions_PermissionId", + column: x => x.PermissionId, + principalTable: "Permissions", + principalColumn: "PermissionId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_RolePermissions_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "RoleId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + Account = table.Column(type: "text", nullable: false), + Password = table.Column(type: "text", nullable: false), + Salt = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Status = table.Column(type: "integer", nullable: false), + UserInfoId = table.Column(type: "uuid", nullable: true), + RoleId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.UserId); + table.ForeignKey( + name: "FK_Users_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "RoleId", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateIndex( + name: "IX_Permissions_Code", + table: "Permissions", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_RolePermissions_PermissionId", + table: "RolePermissions", + column: "PermissionId"); + + migrationBuilder.CreateIndex( + name: "IX_RolePermissions_RoleId", + table: "RolePermissions", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Roles_Name", + table: "Roles", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Users_Account", + table: "Users", + column: "Account", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Users_RoleId", + table: "Users", + column: "RoleId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Chunks"); + + migrationBuilder.DropTable( + name: "Conversations"); + + migrationBuilder.DropTable( + name: "Files"); + + migrationBuilder.DropTable( + name: "LogEntries"); + + migrationBuilder.DropTable( + name: "Messages"); + + migrationBuilder.DropTable( + name: "RolePermissions"); + + migrationBuilder.DropTable( + name: "SystemSettings"); + + migrationBuilder.DropTable( + name: "UserInfos"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Permissions"); + + migrationBuilder.DropTable( + name: "Roles"); + } + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs new file mode 100644 index 0000000..bde02a3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs @@ -0,0 +1,389 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Pgvector; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +#nullable disable + +namespace UniversalAdminSystem.Infrastructure.Migrations +{ + [DbContext(typeof(UniversalAdminSystemDbContext))] + partial class UniversalAdminSystemDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("RolePermissions", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("PermissionId") + .HasColumnType("uuid"); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId"); + + b.ToTable("RolePermissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.FileStorage.Aggregates.File", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessLevel") + .HasColumnType("integer"); + + b.Property("IsFolder") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityCheckResult") + .HasColumnType("text"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UploadTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.LogManagement.Aggregates.LogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Context") + .HasColumnType("text"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("Level") + .IsRequired() + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Source") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("LogEntries"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PermissionType") + .HasColumnType("integer"); + + b.Property("Resource") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSupper") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RoleId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.SystemSettings.Aggregates.SystemSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Group") + .HasColumnType("text"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SystemSettings"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Conversations", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("UpdateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Conversations"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Message", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Account") + .IsRequired() + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.HasKey("UserId"); + + b.HasIndex("Account") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Entities.UserInfo", b => + { + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.HasKey("UserInfoId"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.knowledge.Aggregates.DocumentChunk", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("Embedding") + .IsRequired() + .HasColumnType("vector(1536)"); + + b.Property("FileId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Chunks"); + }); + + modelBuilder.Entity("RolePermissions", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", null) + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.SetNull); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs index 73db4cb..0173534 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs @@ -10,6 +10,12 @@ using UniversalAdminSystem.Domian.SystemSettings.Aggregates; using UniversalAdminSystem.Domian.SystemSettings.ValueObjects; using UniversalAdminSystem.Domian.FileStorage.ValueObjects; using File = UniversalAdminSystem.Domian.FileStorage.Aggregates.File; +using UniversalAdminSystem.Domian.knowledge.Aggregates; +using System.Data.Common; +using UniversalAdminSystem.Domian.knowledge.ValueObj; +using NpgsqlTypes; +using Pgvector; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; namespace UniversalAdminSystem.Infrastructure.Persistence.DbContexts; @@ -24,9 +30,17 @@ public class UniversalAdminSystemDbContext : DbContext public DbSet LogEntries { get; set; } public DbSet SystemSettings { get; set; } public DbSet Files { get; set; } + + public DbSet Conversations { get; set; } + + public DbSet Messages { get; set; } + + public DbSet Chunks { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { + // 启用"vector"扩展 + modelBuilder.HasPostgresExtension("vector"); // 配置UserInfo实体 modelBuilder.Entity(entity => { @@ -64,7 +78,7 @@ public class UniversalAdminSystemDbContext : DbContext email => email.Value, value => UserEmail.Create(value) ); - + // 配置UserAccount值对象转换 entity.Property(u => u.Account) .HasConversion( @@ -220,7 +234,61 @@ public class UniversalAdminSystemDbContext : DbContext value => FileId.Create(value) ); }); - + + modelBuilder.Entity(entity => + { + entity.HasKey(d => d.Id); + + entity.Property(d => d.FileId) + .HasConversion( + fileId => fileId.Value, + value => FileId.Create(value) + ); + + entity.Property(d => d.Id) + .HasConversion(id => id.Value, + value => ChunkId.Create(value)); + + entity.Property(d => d.Embedding) + .HasColumnType("vector(1536)") + .HasConversion( + embedding => new Vector(embedding.Value), + value => TextEmbedding.Create(value.ToArray()) + ); + + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(c => c.Id); + + entity.Property(c => c.Id) + .HasConversion(id => id.Value, + value => ConversationId.Create(value) + ); + + entity.Property(c => c.UserId) + .HasConversion(userid => userid.Value, + value => UserId.Create(value) + ); + + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(m => m.Id); + + entity.Property(m => m.Id) + .HasConversion(id => id.Value, + value => MessageId.Create(value) + ); + + entity.Property(m => m.ConversationId) + .HasConversion(Conversationid => Conversationid.Value, + value => ConversationId.Create(value) + ); + }); + // 忽略值对象 modelBuilder.Ignore(); } diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs new file mode 100644 index 0000000..dbb70bf --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; +using UniversalAdminSystem.Domian.UserConversations.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class ConversationsRepository : BaseRepository, IConversationsRepository +{ + public ConversationsRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public async Task> GetByUserIdAsync(Guid userId) + { + var list = await _TDs.Where(c => c.UserId.Value == userId).AsNoTracking().ToListAsync(); + return list; + } + + public async Task RemoveByUserIdAsync(Guid userId) + { + await _TDs.Where(c => c.UserId.Value == userId).ExecuteDeleteAsync(); + } + + public async Task RemoveConversation(Guid ConversationId) + { + await _TDs.Where(c => c.Id.Value == ConversationId).ExecuteDeleteAsync(); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs new file mode 100644 index 0000000..4823e82 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs @@ -0,0 +1,48 @@ +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Pgvector.EntityFrameworkCore; +using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.knowledge.Aggregates; +using UniversalAdminSystem.Domian.knowledge.IRepository; +using UniversalAdminSystem.Domian.knowledge.ValueObj; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class DocumentChunkRepository : BaseRepository, IDocumentChunkRepository +{ + public DocumentChunkRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public async Task BatchDeleteDocumentChunkAsync(Guid Id) + { + await _TDs.Where(d => d.FileId.Value == Id).ExecuteDeleteAsync(); + } + + public async Task BatchModificationChunkLevelAsync(Guid FileId, FileAccessLevel level) + { + await _TDs.Where(d => d.FileId.Value == FileId) + .ExecuteUpdateAsync( + set => + set.SetProperty(d => d.Level, level) + ); + } + + public async Task BulkAddDocumentChunkAsync(IEnumerable chunks) + { + await _TDs.AddRangeAsync(chunks); + } + + public async Task> FindSimilarDocumentsAsync(TextEmbedding queryEmbedding, FileAccessLevel level, int topK = 5) + { + var results = await _TDs + .Where(c => c.Level <= level) + .OrderBy(c => c.Embedding.CosineDistance(queryEmbedding)) + .Take(topK) + .ToListAsync(); + return results; + } + + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs new file mode 100644 index 0000000..1b6a713 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.UserConversations.Aggregates; +using UniversalAdminSystem.Domian.UserConversations.IRepository; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.Persistence.Repositories; + +public class MessageRepository : BaseRepository, IMessageRepository +{ + public MessageRepository(UniversalAdminSystemDbContext dbContext) : base(dbContext) + { + } + + public async Task> GetByConversationIdAsync(Guid conversationId) + { + var list = await _TDs.Where(m => m.ConversationId.Value == conversationId).AsNoTracking().ToListAsync(); + return list; + } + + public async Task RemoveByConversationIdAsync(Guid conversationId) + { + + await _TDs.Where(m => m.ConversationId.Value == conversationId).ExecuteDeleteAsync(); + } +} + diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs index 394173d..0ad4507 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/SystemInitializationService.cs @@ -157,6 +157,10 @@ public class SystemInitializationService : IHostedService permissions.Add(Permission.CreateSystemPermission("文件上传", "file", (int)PermissionAction.Create)); permissions.Add(Permission.CreateSystemPermission("文件下载", "file", (int)PermissionAction.Read)); permissions.Add(Permission.CreateSystemPermission("文件删除", "file", (int)PermissionAction.Delete)); + permissions.Add(Permission.CreateSystemPermission("文档权限", "document", (int)PermissionAction.Private)); + permissions.Add(Permission.CreateSystemPermission("文档权限", "document", (int)PermissionAction.Public)); + permissions.Add(Permission.CreateSystemPermission("文档权限", "document", (int)PermissionAction.Restricted)); + permissions.Add(Permission.CreateSystemPermission("文档权限", "document", (int)PermissionAction.Read)); // 用户权限 permissions.Add(Permission.CreateSystemPermission("创建用户", "user", (int)PermissionAction.Create)); @@ -275,7 +279,8 @@ public class SystemInitializationService : IHostedService "role:Create", "role:Read", "role:Update", "role:Delete", "permission:Create", "permission:Read", "permission:Update", "permission:Delete", "system:Manage", - "file:Read","file:Create","file:Delete" + "file:Read","file:Create","file:Delete", + "document:Restricted","document:Read" }; var adminPermissionIds = new List(); @@ -310,7 +315,8 @@ public class SystemInitializationService : IHostedService var basicPermissionCodes = new[] { "user:Read", "role:Read", "permission:Read", - "file:Create", "file:Read" + "file:Create", "file:Read", + "document:Read","document:Restricted" }; var basicPermissionIds = new List(); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj index c7d12bd..3f870fa 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj +++ b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj @@ -8,6 +8,7 @@ + -- Gitee From ac9830f6fda219e38a2c36c19fab2819cec33efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=B4=A4=E5=9B=BD?= <3482108437@qq.com> Date: Thu, 7 Aug 2025 16:22:26 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=E4=BD=BF=E7=94=A8uniapp=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E7=99=BB=E5=BD=95/=E6=B3=A8=E5=86=8C/=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/App.vue | 17 ++ frontend/index.html | 20 +++ frontend/main.js | 23 +++ frontend/manifest.json | 75 +++++++++ frontend/pages.json | 32 ++++ frontend/pages/chat/chat.vue | 225 +++++++++++++++++++++++++++ frontend/pages/login/login.vue | 158 +++++++++++++++++++ frontend/pages/register/register.vue | 141 +++++++++++++++++ frontend/static/logo.png | Bin 0 -> 4023 bytes frontend/uni.promisify.adaptor.js | 13 ++ frontend/uni.scss | 76 +++++++++ 11 files changed, 780 insertions(+) create mode 100644 frontend/App.vue create mode 100644 frontend/index.html create mode 100644 frontend/main.js create mode 100644 frontend/manifest.json create mode 100644 frontend/pages.json create mode 100644 frontend/pages/chat/chat.vue create mode 100644 frontend/pages/login/login.vue create mode 100644 frontend/pages/register/register.vue create mode 100644 frontend/static/logo.png create mode 100644 frontend/uni.promisify.adaptor.js create mode 100644 frontend/uni.scss diff --git a/frontend/App.vue b/frontend/App.vue new file mode 100644 index 0000000..8c2b732 --- /dev/null +++ b/frontend/App.vue @@ -0,0 +1,17 @@ + + + diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..b5d330d --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + +
+ + + diff --git a/frontend/main.js b/frontend/main.js new file mode 100644 index 0000000..df66165 --- /dev/null +++ b/frontend/main.js @@ -0,0 +1,23 @@ +import App from './App' + +// #ifndef VUE3 +import Vue from 'vue' +import './uni.promisify.adaptor' +Vue.config.productionTip = false +App.mpType = 'app' +const app = new Vue({ + ...App +}) +app.$mount() +// #endif + +// #ifdef VUE3 +import { createSSRApp } from 'vue' +export function createApp() { + const app = createSSRApp(App) + return { + app + } +} +// #endif + diff --git a/frontend/manifest.json b/frontend/manifest.json new file mode 100644 index 0000000..b6ebc91 --- /dev/null +++ b/frontend/manifest.json @@ -0,0 +1,75 @@ +{ + "name" : "uniapp-ai", + "appid" : "__UNI__3924A7E", + "description" : "", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "minSdkVersion" : 21 + }, + /* ios打包配置 */ + "ios" : { + "dSYMs" : false + }, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "", + "setting" : { + "urlCheck" : false + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "3" +} diff --git a/frontend/pages.json b/frontend/pages.json new file mode 100644 index 0000000..03cf3d3 --- /dev/null +++ b/frontend/pages.json @@ -0,0 +1,32 @@ +{ + "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages + { + "path" : "pages/login/login", + "style" : + { + "navigationBarTitleText" : "" + } + }, + { + "path" : "pages/chat/chat", + "style" : + { + "navigationBarTitleText" : "" + } + }, + { + "path" : "pages/register/register", + "style" : + { + "navigationBarTitleText" : "" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "uni-app", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + }, + "uniIdRouter": {} +} diff --git a/frontend/pages/chat/chat.vue b/frontend/pages/chat/chat.vue new file mode 100644 index 0000000..a660afb --- /dev/null +++ b/frontend/pages/chat/chat.vue @@ -0,0 +1,225 @@ + + + + + \ No newline at end of file diff --git a/frontend/pages/login/login.vue b/frontend/pages/login/login.vue new file mode 100644 index 0000000..b711f5e --- /dev/null +++ b/frontend/pages/login/login.vue @@ -0,0 +1,158 @@ + + + + + \ No newline at end of file diff --git a/frontend/pages/register/register.vue b/frontend/pages/register/register.vue new file mode 100644 index 0000000..75328be --- /dev/null +++ b/frontend/pages/register/register.vue @@ -0,0 +1,141 @@ + + + + + \ No newline at end of file diff --git a/frontend/static/logo.png b/frontend/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5771e209bb677e2ebd5ff766ad5ee11790f305a GIT binary patch literal 4023 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 SYs}WS&%*SCNx89m_ { + res.then((res) => { + if (!res) return resolve(res) + return res[0] ? reject(res[0]) : resolve(res[1]) + }); + }); + }, +}); \ No newline at end of file diff --git a/frontend/uni.scss b/frontend/uni.scss new file mode 100644 index 0000000..b9249e9 --- /dev/null +++ b/frontend/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16px; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; -- Gitee From 81491bf988ba38b6ea9bfc9670a861566cb32445 Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Thu, 7 Aug 2025 17:47:11 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BB=93=E5=82=A8?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/AIQuestionsController.cs | 4 ++-- .../Service/AIQusetionsAppService.cs | 20 +++++++++++++------ .../Repositories/ConversationsRepository.cs | 13 ++++++++---- .../Repositories/DocumentChunkRepository.cs | 8 +++++--- .../Repositories/MessageRepository.cs | 10 ++++++---- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs index b157abc..913abe9 100644 --- a/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs +++ b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs @@ -20,7 +20,7 @@ public class AIQuestionsController : ControllerBase [HttpPost("visitor")] public Task VisitorAccess(ContentDto content) { - + throw new NotImplementedException();//尚未实现 } [HttpPost("user")] @@ -42,7 +42,7 @@ public class AIQuestionsController : ControllerBase [RequirePermission("document:Read")] public Task UserAccessById(Guid id,ContentDto content) { - + throw new NotImplementedException();//尚未实现 } [HttpGet("user/{userid}")] diff --git a/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs b/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs index 6ad0b4b..101ccb6 100644 --- a/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/AIQuestions/Service/AIQusetionsAppService.cs @@ -16,31 +16,38 @@ public class AIQusetionsAppService : IAIQusetionsAppService private readonly IPermissionCheckService _permissioncheck; + private readonly IUserRepository _userRepository; + private readonly IUnitOfWork _work; - public AIQusetionsAppService(IMessageRepository message, IConversationsRepository conversations, IPermissionCheckService permissioncheck, IUnitOfWork work) + public AIQusetionsAppService(IMessageRepository message, + IConversationsRepository conversations, + IPermissionCheckService permissioncheck, + IUnitOfWork work, + IUserRepository userRepository) { _messageRepo = message; _conversationsRepo = conversations; _permissioncheck = permissioncheck; _work = work; - + _userRepository = userRepository; } public async Task CreateConversation(ConversationsDto conversationsDto) { try { + var user = await _userRepository.GetByGuidAsync(conversationsDto.UserId) ?? throw new Exception("用户不存在"); await _work.BeginTransactionAsync(); var Conversation = Conversations.Create(conversationsDto.UserId, conversationsDto.Title); var entity = await _conversationsRepo.AddAsync(Conversation); await _work.CommitAsync(); return new ConversationsResultDto(entity.Id.Value, entity.Title); } - catch (System.Exception) + catch (System.Exception e) { await _work.RollbackAsync(); - throw new Exception("用户会话会话创建失败"); + throw new Exception( $"用户会话创建失败:{e.Message}"); } } @@ -77,12 +84,13 @@ public class AIQusetionsAppService : IAIQusetionsAppService { try { + var user = await _userRepository.GetByGuidAsync(userId) ?? throw new Exception("用户不存在"); var list = await _conversationsRepo.GetByUserIdAsync(userId); return list.Select(m => new ConversationsResultDto(m.Id.Value, m.Title)); } - catch (System.Exception) + catch (System.Exception e) { - throw new Exception("获取用户会话失败"); + throw new Exception($"获取用户会话失败:{e.Message}"); } } diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs index dbb70bf..4f1ad8b 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs @@ -1,4 +1,6 @@ +using System.Net.WebSockets; using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; using UniversalAdminSystem.Domian.UserConversations.Aggregates; using UniversalAdminSystem.Domian.UserConversations.IRepository; using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; @@ -13,17 +15,20 @@ public class ConversationsRepository : BaseRepository, IConversat public async Task> GetByUserIdAsync(Guid userId) { - var list = await _TDs.Where(c => c.UserId.Value == userId).AsNoTracking().ToListAsync(); + var usersid = UserId.Create(userId); + var list = await _TDs.Where(c => c.UserId == usersid).AsNoTracking().ToListAsync(); return list; } public async Task RemoveByUserIdAsync(Guid userId) { - await _TDs.Where(c => c.UserId.Value == userId).ExecuteDeleteAsync(); + var usersId = UserId.Create(userId); + await _TDs.Where(c => c.UserId == usersId).ExecuteDeleteAsync(); } - public async Task RemoveConversation(Guid ConversationId) + public async Task RemoveConversation(Guid ConsId) { - await _TDs.Where(c => c.Id.Value == ConversationId).ExecuteDeleteAsync(); + var ConId = ConversationId.Create(ConsId); + await _TDs.Where(c => c.Id == ConId).ExecuteDeleteAsync(); } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs index 4823e82..16e13e5 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/DocumentChunkRepository.cs @@ -17,12 +17,14 @@ public class DocumentChunkRepository : BaseRepository, IDocumentC public async Task BatchDeleteDocumentChunkAsync(Guid Id) { - await _TDs.Where(d => d.FileId.Value == Id).ExecuteDeleteAsync(); + var fileid = FileId.Create(Id); + await _TDs.Where(d => d.FileId == fileid).ExecuteDeleteAsync(); } - public async Task BatchModificationChunkLevelAsync(Guid FileId, FileAccessLevel level) + public async Task BatchModificationChunkLevelAsync(Guid fileId, FileAccessLevel level) { - await _TDs.Where(d => d.FileId.Value == FileId) + var filesid = FileId.Create(fileId); + await _TDs.Where(d => d.FileId == filesid) .ExecuteUpdateAsync( set => set.SetProperty(d => d.Level, level) diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs index 1b6a713..7f70ecb 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using UniversalAdminSystem.Domian.Core.ValueObjects; using UniversalAdminSystem.Domian.UserConversations.Aggregates; using UniversalAdminSystem.Domian.UserConversations.IRepository; using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; @@ -11,16 +12,17 @@ public class MessageRepository : BaseRepository, IMessageRepository { } - public async Task> GetByConversationIdAsync(Guid conversationId) + public async Task> GetByConversationIdAsync(Guid conId) { - var list = await _TDs.Where(m => m.ConversationId.Value == conversationId).AsNoTracking().ToListAsync(); + var ConId = ConversationId.Create(conId); + var list = await _TDs.Where(m => m.ConversationId == ConId).AsNoTracking().ToListAsync(); return list; } public async Task RemoveByConversationIdAsync(Guid conversationId) { - - await _TDs.Where(m => m.ConversationId.Value == conversationId).ExecuteDeleteAsync(); + var ConId = ConversationId.Create(conversationId); + await _TDs.Where(m => m.ConversationId == ConId).ExecuteDeleteAsync(); } } -- Gitee From d6719c82f72db700ad761966f8cc6f536f2f5511 Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Fri, 8 Aug 2025 11:41:20 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E5=B0=86=E4=BC=9A=E8=AF=9D=EF=BC=8C?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9A=84=E8=8E=B7=E5=8F=96=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E4=B8=BA=E4=BB=8E=E6=97=A9=E5=88=B0=E6=99=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Persistence/Repositories/ConversationsRepository.cs | 2 +- .../Persistence/Repositories/MessageRepository.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs index 4f1ad8b..bfa2c99 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/ConversationsRepository.cs @@ -16,7 +16,7 @@ public class ConversationsRepository : BaseRepository, IConversat public async Task> GetByUserIdAsync(Guid userId) { var usersid = UserId.Create(userId); - var list = await _TDs.Where(c => c.UserId == usersid).AsNoTracking().ToListAsync(); + var list = await _TDs.Where(c => c.UserId == usersid).OrderBy(c=>c.UpdateDate).AsNoTracking().ToListAsync(); return list; } diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs index 7f70ecb..d25bb42 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/Repositories/MessageRepository.cs @@ -15,7 +15,7 @@ public class MessageRepository : BaseRepository, IMessageRepository public async Task> GetByConversationIdAsync(Guid conId) { var ConId = ConversationId.Create(conId); - var list = await _TDs.Where(m => m.ConversationId == ConId).AsNoTracking().ToListAsync(); + var list = await _TDs.Where(m => m.ConversationId == ConId).OrderBy(m=>m.CreateDate).AsNoTracking().ToListAsync(); return list; } -- Gitee From efbbd59d62371fc98db3620d1cb6b7eabaac4e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=97=AD?= <1619917346@qq.com> Date: Fri, 8 Aug 2025 14:38:52 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AllowedFiles.json | 84 +++++++++++ .../Controllers/FileController.cs | 24 +--- .../K2ModelTests.http | 61 -------- .../K2Model_README.md | 133 ------------------ .../src/UniversalAdminSystem.Api/Program.cs | 14 +- .../FileStorage/DTOs/FileDto.cs | 16 +-- .../FileStorage/Interfaces/IFileAppService.cs | 3 +- .../Interfaces}/IFileStorageService.cs | 7 +- .../Interfaces/IFileValidationService.cs | 7 + .../FileStorage/Services/FileAppService.cs | 66 ++++++--- .../UniversalAdminSystem.Application.csproj | 4 + .../FileStorage/ValueObjects/FileType.cs | 3 +- .../Auth/JwtTokenBuilder.cs | 23 +-- ...4\350\257\201\345\256\236\347\216\260.txt" | 0 .../Configs/AllowedFileConfig.cs | 21 +++ .../JwtSettings.cs => Configs/JwtConfig.cs} | 4 +- .../Configs/K2Config.cs | 50 +++++-- .../DependencyInject/AddApplicationService.cs | 4 +- .../AddInfrastructureService.cs | 81 ++++++++--- .../ServiceCollectionExtensions.cs | 2 +- .../FileStorage/FileValidationService.cs | 96 +++++++++++++ .../FileStorage/LocalFileStorageService.cs | 46 ++---- .../Services/FileScaner.cs | 38 +++++ .../Services/K2ConfigService.cs | 19 --- .../Services/K2ModelService.cs | 114 +++++++++------ .../Services/TextExtractor.cs | 6 + ...UniversalAdminSystem.Infrastructure.csproj | 2 + 27 files changed, 534 insertions(+), 394 deletions(-) create mode 100644 backend/src/UniversalAdminSystem.Api/AllowedFiles.json delete mode 100644 backend/src/UniversalAdminSystem.Api/K2Model_README.md rename backend/src/{UniversalAdminSystem.Infrastructure/FileStorage => UniversalAdminSystem.Application/FileStorage/Interfaces}/IFileStorageService.cs (42%) create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs delete mode 100644 "backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Configs/AllowedFileConfig.cs rename backend/src/UniversalAdminSystem.Infrastructure/{Auth/JwtSettings.cs => Configs/JwtConfig.cs} (77%) create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/FileScaner.cs delete mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Services/TextExtractor.cs diff --git a/backend/src/UniversalAdminSystem.Api/AllowedFiles.json b/backend/src/UniversalAdminSystem.Api/AllowedFiles.json new file mode 100644 index 0000000..02cda35 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Api/AllowedFiles.json @@ -0,0 +1,84 @@ +{ + "AllowedFiles": [ + { + "Mime": "image/jpeg", + "Ext": "jpg", + "Signature": "FF D8" + }, + { + "Mime": "image/png", + "Ext": "png", + "Signature": "89 50 4E 47 0D 0A 1A 0A" + }, + { + "Mime": "image/gif", + "Ext": "gif", + "Signature": "47 49 46 38 37 61" + }, + { + "Mime": "image/gif", + "Ext": "gif", + "Signature": "47 49 46 38 39 61" + }, + { + "Mime": "image/bmp", + "Ext": "bmp", + "Signature": "42 4D" + }, + { + "Mime": "image/webp", + "Ext": "webp", + "Signature": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50" + }, + { + "Mime": "application/pdf", + "Ext": "pdf", + "Signature": "25 50 44 46" + }, + { + "Mime": "application/json", + "Ext": "json", + "Signature": "" + }, + { + "Mime": "text/markdown", + "Ext": "md", + "Signature": "" + }, + { + "Mime": "text/plain", + "Ext": "txt", + "Signature": "" + }, + { + "Mime": "application/xml", + "Ext": "xml", + "Signature": "" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Ext": "docx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Ext": "xlsx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Ext": "pptx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.ms-excel", + "Ext": "xls", + "Signature": "D0 CF 11 E0 A1 B1 1A E1" + }, + { + "Mime": "application/msword", + "Ext": "doc", + "Signature": "D0 CF 11 E0 A1 B1 1A E1" + } + ] +} diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs index 98f6832..99b4836 100644 --- a/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs +++ b/backend/src/UniversalAdminSystem.Api/Controllers/FileController.cs @@ -4,7 +4,6 @@ using UniversalAdminSystem.Application.FileStorage.Interfaces; using UniversalAdminSystem.Api.Attributes; using UniversalAdminSystem.Domian.FileStorage.ValueObjects; using UniversalAdminSystem.Application.Common.Results; -using UniversalAdminSystem.Infrastructure.FileStorage; using UniversalAdminSystem.Application.Common.Interfaces; namespace UniversalAdminSystem.Api.Controllers; @@ -25,33 +24,18 @@ public class FileController : ControllerBase } [HttpPost("upload")] - [RequirePermission("file:Create")] // 临时注释掉权限检查 + [RequirePermission("file:Create")] public async Task Upload(IFormFile file, Guid? parentId = null) { - if (file == null || file.Length == 0) - { - return BadRequest(Result.Failure("请选择要上传的文件")); - } - + if (file == null || file.Length == 0) return BadRequest(Result.Failure("请选择要上传的文件")); try { - await _unitOfWork.BeginTransactionAsync(); - var filePath = await _fileStorageService.UploadAsync(file, file.OpenReadStream()); - var fileUploadDto = new FileUploadDto( - file.FileName, - filePath, - file.Length, - file.ContentType, - parentId.HasValue ? (FileId)parentId.Value : Guid.Empty); - - var res = await _fileAppService.UploadAsync(fileUploadDto); - await _unitOfWork.CommitAsync(); + var res = await _fileAppService.UploadAsync(file); + Console.WriteLine(res); return Ok(Result.Success(res)); - } catch (Exception ex) { - await _unitOfWork.RollbackAsync(); return BadRequest(Result.Failure(ex.Message)); } } diff --git a/backend/src/UniversalAdminSystem.Api/K2ModelTests.http b/backend/src/UniversalAdminSystem.Api/K2ModelTests.http index 26e23ce..ae1fd04 100644 --- a/backend/src/UniversalAdminSystem.Api/K2ModelTests.http +++ b/backend/src/UniversalAdminSystem.Api/K2ModelTests.http @@ -1,64 +1,3 @@ -@baseUrl = https://localhost:7001 - -### 获取K2模型配置 -GET {{baseUrl}}/api/K2Model/config -Content-Type: application/json - -### - -### 发送简单聊天请求 -POST {{baseUrl}}/api/K2Model/chat -Content-Type: application/json - -{ - "prompt": "你好,请介绍一下你自己", - "model": "qwen-turbo" -} - -### - -### 发送多轮对话请求 -POST {{baseUrl}}/api/K2Model/conversation -Content-Type: application/json - -{ - "messages": [ - { - "role": "system", - "content": "你是一个有用的AI助手" - }, - { - "role": "user", - "content": "请帮我写一个简单的C#函数来计算两个数的和" - } - ], - "model": "qwen-turbo", - "temperature": 0.7, - "maxTokens": 1000 -} - -### - -### 测试复杂对话 -POST {{baseUrl}}/api/K2Model/conversation -Content-Type: application/json - -{ - "messages": [ - { - "role": "system", - "content": "你是一个专业的软件工程师,擅长C#和.NET开发" - }, - { - "role": "user", - "content": "请解释一下依赖注入模式在ASP.NET Core中的应用" - } - ], - "model": "qwen-turbo", - "temperature": 0.5, - "maxTokens": 1500 -} - ### POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions Content-Type: application/json diff --git a/backend/src/UniversalAdminSystem.Api/K2Model_README.md b/backend/src/UniversalAdminSystem.Api/K2Model_README.md deleted file mode 100644 index f3d93bc..0000000 --- a/backend/src/UniversalAdminSystem.Api/K2Model_README.md +++ /dev/null @@ -1,133 +0,0 @@ -# K2模型集成指南 - -## 概述 - -本项目已集成K2模型(基于阿里云DashScope的兼容模式),提供了完整的AI对话功能。 - -## 配置说明 - -### 1. 配置文件设置 - -在 `appsettings.json` 中配置K2模型参数: - -```json -{ - "K2": { - "BaseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "ApiKey": "your-api-key-here" - } -} -``` - -### 2. 配置参数说明 - -- **BaseUrl**: K2模型的API基础URL -- **ApiKey**: 阿里云DashScope的API密钥 - -## API接口 - -### 1. 获取配置信息 - -``` -GET /api/K2Model/config -``` - -返回配置信息(不包含敏感数据)。 - -### 2. 简单聊天请求 - -``` -POST /api/K2Model/chat -``` - -请求体: -```json -{ - "prompt": "你好,请介绍一下你自己", - "model": "qwen-turbo" -} -``` - -### 3. 多轮对话请求 - -``` -POST /api/K2Model/conversation -``` - -请求体: -```json -{ - "messages": [ - { - "role": "system", - "content": "你是一个有用的AI助手" - }, - { - "role": "user", - "content": "请帮我写一个简单的C#函数" - } - ], - "model": "qwen-turbo", - "temperature": 0.7, - "maxTokens": 1000 -} -``` - -## 使用示例 - -### 1. 简单对话 - -```csharp -// 在控制器中注入服务 -private readonly K2ModelService _k2ModelService; - -// 发送简单请求 -var response = await _k2ModelService.SendSimpleRequestAsync("你好"); -``` - -### 2. 多轮对话 - -```csharp -var messages = new List -{ - new K2Message { Role = "system", Content = "你是一个专业的程序员" }, - new K2Message { Role = "user", Content = "请解释一下依赖注入" } -}; - -var response = await _k2ModelService.SendChatRequestAsync(messages); -``` - -## 测试方法 - -1. 启动应用程序 -2. 使用提供的 `K2ModelTests.http` 文件进行API测试 -3. 在Swagger UI中查看和测试API接口 - -## 注意事项 - -1. **API密钥安全**: 确保API密钥的安全性,不要在客户端代码中暴露 -2. **错误处理**: 服务已包含完整的错误处理和日志记录 -3. **性能优化**: 使用HttpClient工厂模式,支持连接池和重试机制 -4. **配置验证**: 启动时会验证配置的有效性 - -## 支持的模型 - -- `qwen-turbo`: 通义千问Turbo模型 -- `qwen-plus`: 通义千问Plus模型 -- `qwen-max`: 通义千问Max模型 - -## 故障排除 - -1. **401错误**: 检查API密钥是否正确 -2. **404错误**: 检查BaseUrl配置是否正确 -3. **网络错误**: 检查网络连接和防火墙设置 - -## 扩展功能 - -可以根据需要扩展以下功能: - -1. 流式响应支持 -2. 模型参数缓存 -3. 请求限流 -4. 响应内容过滤 -5. 多模型支持 \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Api/Program.cs b/backend/src/UniversalAdminSystem.Api/Program.cs index d580a5b..29cae8a 100644 --- a/backend/src/UniversalAdminSystem.Api/Program.cs +++ b/backend/src/UniversalAdminSystem.Api/Program.cs @@ -2,10 +2,12 @@ using UniversalAdminSystem.Infrastructure.DependencyInject; using UniversalAdminSystem.Infrastructure.Services; using UniversalAdminSystem.Api.Middleware; using Microsoft.OpenApi.Models; -using UniversalAdminSystem.Infrastructure.Configs; var builder = WebApplication.CreateBuilder(args); +// 加载外部的允许文件配置,避免 appsettings.json 膨胀 +builder.Configuration.AddJsonFile("AllowedFiles.json", optional: false, reloadOnChange: true); + // 添加CORS服务 builder.Services.AddCors(options => { @@ -34,8 +36,6 @@ builder.Services.AddCors(options => }); }); -builder.Services.Configure(builder.Configuration.GetSection("K2")); - // Add services to the container. builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => @@ -73,9 +73,13 @@ var configPath = Path.Combine(Directory.GetCurrentDirectory(), "PermissionRules. var permissionRuleConfigService = new PermissionRuleConfigService(configPath); permissionRuleConfigService.Initialize(); -// 注册为单例服务,供后续使用 builder.Services.AddSingleton(permissionRuleConfigService); -builder.Services.AddAllServiceRegistrations(builder.Configuration); + +// 显式注册 AllowedFilesConfig +builder.Services.Configure( + builder.Configuration.GetSection("AllowedFiles")); + +builder.Services.AddAllService(builder.Configuration); builder.Services.AddControllers(); var app = builder.Build(); diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs index 79c8621..d757142 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileDto.cs @@ -1,7 +1,3 @@ -using UniversalAdminSystem.Domian.Core.ValueObjects; -using UniversalAdminSystem.Domian.FileStorage.ValueObjects; -using UniversalAdminSystem.Domian.UserManagement.ValueObj; - namespace UniversalAdminSystem.Application.FileStorage.DTOs; public record FileDto( @@ -30,11 +26,13 @@ public record FileDownloadDto( string Name, string Path, string Type -); +); public record FileUploadResultDto( - Guid Id, - string Name, - string Path, - string Type + Guid? Id = null, + string? Name = null, + string? Path = null, + long? Size = null, + string? Type = null, + string? Message = null ); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs index ef8a47b..bb7ec7a 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileAppService.cs @@ -3,12 +3,13 @@ using UniversalAdminSystem.Domian.UserManagement.ValueObj; using UniversalAdminSystem.Application.FileStorage.DTOs; using UniversalAdminSystem.Domian.Core.ValueObjects; using UniversalAdminSystem.Application.Common.Results; +using Microsoft.AspNetCore.Http; namespace UniversalAdminSystem.Application.FileStorage.Interfaces; public interface IFileAppService { - Task UploadAsync(FileUploadDto dto); + Task UploadAsync(IFormFile file); Task> GetList(); // Task> DownloadAsync(FileId fileId); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileStorageService.cs similarity index 42% rename from backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs rename to backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileStorageService.cs index b2d9187..5aa8613 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/IFileStorageService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileStorageService.cs @@ -1,11 +1,12 @@ using Microsoft.AspNetCore.Http; +using UniversalAdminSystem.Application.FileStorage.DTOs; -namespace UniversalAdminSystem.Infrastructure.FileStorage; +namespace UniversalAdminSystem.Application.FileStorage.Interfaces; public interface IFileStorageService { - Task UploadAsync(IFormFile file, Stream fileStream); + Task SaveAsync(IFormFile file); Task DownloadAsync(string fileName); Task DeleteAsync(string fileName); - Task> ListFilesAsync(); + Task> ListFilesAsync(); } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs new file mode 100644 index 0000000..68d5fa8 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs @@ -0,0 +1,7 @@ +namespace UniversalAdminSystem.Application.FileStorage.Interfaces; + +public interface IFileValidationService +{ + (bool isValid, string message) ValidateFile(long fileSize, Stream stream); + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs index f4ae062..c4c82f1 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs @@ -1,49 +1,70 @@ +using Microsoft.AspNetCore.Http; +using UniversalAdminSystem.Application.Common.Interfaces; using UniversalAdminSystem.Application.FileStorage.DTOs; using UniversalAdminSystem.Application.FileStorage.Interfaces; using UniversalAdminSystem.Domian.Core.ValueObjects; using UniversalAdminSystem.Domian.FileStorage.IRepository; -using UniversalAdminSystem.Domian.FileStorage.Services; using UniversalAdminSystem.Domian.FileStorage.ValueObjects; -using UniversalAdminSystem.Application.Common.Results; -using UniversalAdminSystem.Application.Common.Interfaces; -using UniversalAdminSystem.Application.PermissionManagement.Interfaces; using File = UniversalAdminSystem.Domian.FileStorage.Aggregates.File; -using UniversalAdminSystem.Domian.FileStorage.Interface; namespace UniversalAdminSystem.Application.FileStorage.Services; public class FileAppService : IFileAppService { private readonly IFileRepository _fileRepository; + private readonly IFileValidationService _fileValidator; + private readonly IFileStorageService _localfileStorage; + private readonly IUnitOfWork _unitOfWork; + - public FileAppService(IFileRepository fileRepository) + public FileAppService(IFileRepository fileRepository, IFileValidationService fileValidator, IFileStorageService localfileStorage, IUnitOfWork unitOfWork) { _fileRepository = fileRepository; + _fileValidator = fileValidator; + _localfileStorage = localfileStorage; + _unitOfWork = unitOfWork; } - public async Task UploadAsync(FileUploadDto dto) + public async Task UploadAsync(IFormFile file) { try { - var file = File.Create( - FileName.Create(dto.Name), - FilePath.Create(dto.Path), - FileSize.Create(dto.Size), - FileType.Create(dto.Type), + using var fileStream = file.OpenReadStream(); + var (isValid, message) = _fileValidator.ValidateFile(file.Length, fileStream); + if (!isValid) throw new Exception($"校验结果: {message}"); + var fileInfo = await _localfileStorage.SaveAsync(file); + await _unitOfWork.BeginTransactionAsync(); + // Console.WriteLine("-------------"); + // Console.WriteLine($"{fileInfo.Name}"); + // Console.WriteLine($"{fileInfo.FullName}"); + // Console.WriteLine($"{file.Length}"); + // Console.WriteLine($"{file.ContentType}"); + // Console.WriteLine("-------------"); + var fileEntity = File.Create + ( + FileName.Create(fileInfo.Name), + FilePath.Create(fileInfo.FullName), + FileSize.Create(file.Length), + FileType.Create(file.ContentType), (UserId)Guid.NewGuid(), false, - dto.ParentId.HasValue ? (FileId)dto.ParentId.Value : null); - await _fileRepository.AddAsync(file); - return new FileUploadResultDto( - file.Id.Value, - file.Name.Value, - file.Path.Value, - file.Type.Value + null + ); + await _fileRepository.AddAsync(fileEntity); + return new FileUploadResultDto + ( + fileEntity.Id.Value, + fileInfo.Name, + fileInfo.FullName, + file.Length, + file.ContentType, + message ); } catch (Exception ex) { - throw new InvalidOperationException($"上传文件失败: {ex.Message}"); + await _unitOfWork.RollbackAsync(); + return new FileUploadResultDto(Message: ex.Message); } } @@ -54,6 +75,7 @@ public class FileAppService : IFileAppService file.Id, file.Name, file.Path, + file.Size, file.Type )); } @@ -76,9 +98,9 @@ public class FileAppService : IFileAppService } catch (System.Exception) { - + throw new Exception("删除异常"); } - + } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj b/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj index 3491141..0f8f710 100644 --- a/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj +++ b/backend/src/UniversalAdminSystem.Application/UniversalAdminSystem.Application.csproj @@ -4,6 +4,10 @@
+ + + + net8.0 enable diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs index f29af26..95eb037 100644 --- a/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/ValueObjects/FileType.cs @@ -6,8 +6,7 @@ public record FileType public static readonly string[] AllowedTypes = new[] { "jpg", "jpeg", "png", ".gif", ".bmp", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".txt", ".zip", ".rar" }; private FileType(string value) { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException("文件类型不能为空"); + if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("文件类型不能为空"); // if (!AllowedTypes.Contains(value.ToLower())) // throw new ArgumentException($"不支持的文件类型: {value}"); Value = value.ToLower(); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs index 8e2960a..d12bae5 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtTokenBuilder.cs @@ -3,41 +3,42 @@ using System.Security.Claims; using System.Text; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using UniversalAdminSystem.Infrastructure.Configs; namespace UniversalAdminSystem.Infrastructure.Auth; public class JwtTokenBuilder { - private readonly JwtSettings _settings; + private readonly JwtConfig _jwtConfig; - public JwtTokenBuilder(IOptions options) + public JwtTokenBuilder(IOptions options) { - _settings = options.Value; + _jwtConfig = options.Value; } public string BuildToken(List claims, int? expireHours = null) { Console.WriteLine($"开始构建JWT Token,Claims数量: {claims.Count}"); - Console.WriteLine($"JWT密钥长度: {_settings.Key.Length}"); + Console.WriteLine($"JWT密钥长度: {_jwtConfig.Key.Length}"); - if (string.IsNullOrEmpty(_settings.Key)) + if (string.IsNullOrEmpty(_jwtConfig.Key)) { throw new InvalidOperationException("JWT密钥不能为空"); } - if (_settings.Key.Length < 16) + if (_jwtConfig.Key.Length < 16) { - throw new InvalidOperationException($"JWT密钥长度不足,当前长度: {_settings.Key.Length},建议至少16位"); + throw new InvalidOperationException($"JWT密钥长度不足,当前长度: {_jwtConfig.Key.Length},建议至少16位"); } - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key)); + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Key)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( - issuer: _settings.Issuer, - audience: _settings.Audience, + issuer: _jwtConfig.Issuer, + audience: _jwtConfig.Audience, claims: claims, - expires: DateTime.UtcNow.AddHours(expireHours ?? _settings.ExpireHours), + expires: DateTime.UtcNow.AddHours(expireHours ?? _jwtConfig.ExpireHours), signingCredentials: creds ); diff --git "a/backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" "b/backend/src/UniversalAdminSystem.Infrastructure/Auth/\350\256\244\350\257\201\345\256\236\347\216\260.txt" deleted file mode 100644 index e69de29..0000000 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Configs/AllowedFileConfig.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/AllowedFileConfig.cs new file mode 100644 index 0000000..86986c9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/AllowedFileConfig.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace UniversalAdminSystem.Infrastructure.Configs; + +public class AllowedFileConfig +{ + [property: JsonPropertyName("Mime")] + public string Mime { get; set; } = string.Empty; + + [property: JsonPropertyName("Ext")] + public string Ext { get; set; } = string.Empty; + + [property: JsonPropertyName("Signature")] + public string Signature { get; set; } = string.Empty; +} + +public class AllowedFilesConfig +{ + [property: JsonPropertyName("AllowedFiles")] + public IEnumerable AllowedFiles { get; set; } = new List(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/JwtConfig.cs similarity index 77% rename from backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs rename to backend/src/UniversalAdminSystem.Infrastructure/Configs/JwtConfig.cs index 2402369..d433712 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Auth/JwtSettings.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/JwtConfig.cs @@ -1,6 +1,6 @@ -namespace UniversalAdminSystem.Infrastructure.Auth; +namespace UniversalAdminSystem.Infrastructure.Configs; -public class JwtSettings +public class JwtConfig { public string Key { get; set; } = string.Empty; // 密钥 public string Issuer { get; set; } = string.Empty; // 发布者 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs index a5c82db..35fff4b 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/K2Config.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace UniversalAdminSystem.Infrastructure.Configs; public class K2Config @@ -27,9 +29,6 @@ public class K2Message public string Content { get; set; } = string.Empty; // } -/// -/// K2响应模型 -/// public class K2Response { public string Id { get; set; } = string.Empty; @@ -40,9 +39,6 @@ public class K2Response public K2Usage Usage { get; set; } = new(); } -/// -/// K2选择模型 -/// public class K2Choice { public int Index { get; set; } @@ -50,12 +46,48 @@ public class K2Choice public string FinishReason { get; set; } = string.Empty; } -/// -/// K2使用情况模型 -/// public class K2Usage { public int PromptTokens { get; set; } public int CompletionTokens { get; set; } public int TotalTokens { get; set; } +} + +public class K2StreamResponse +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("object")] + public string Object { get; set; } = string.Empty; + + [JsonPropertyName("created")] + public long Created { get; set; } + + [JsonPropertyName("model")] + public string Model { get; set; } = string.Empty; + + [JsonPropertyName("choices")] + public List Choices { get; set; } = new(); +} + +public class K2StreamChoice +{ + [JsonPropertyName("index")] + public int Index { get; set; } + + [JsonPropertyName("delta")] + public K2StreamDelta Delta { get; set; } = new(); + + [JsonPropertyName("finish_reason")] + public string? FinishReason { get; set; } +} + +public class K2StreamDelta +{ + [JsonPropertyName("role")] + public string? Role { get; set; } + + [JsonPropertyName("content")] + public string? Content { get; set; } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs index 9aaf2b3..a7eb95f 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddApplicationService.cs @@ -7,7 +7,6 @@ using UniversalAdminSystem.Application.UserManagement.Service; using UniversalAdminSystem.Infrastructure.Persistence.Transaction; using UniversalAdminSystem.Application.Authentication.Interfaces; using UniversalAdminSystem.Application.Authentication.Service; -using UniversalAdminSystem.Infrastructure.Auth; using FluentValidation; using UniversalAdminSystem.Application.PermissionManagement.Services; using UniversalAdminSystem.Application.PermissionManagement.Interfaces; @@ -17,6 +16,7 @@ using UniversalAdminSystem.Application.SystemSettings.Interfaces; using UniversalAdminSystem.Application.SystemSettings.Services; using UniversalAdminSystem.Application.FileStorage.Interfaces; using UniversalAdminSystem.Application.FileStorage.Services; +using UniversalAdminSystem.Infrastructure.Configs; namespace UniversalAdminSystem.Infrastructure.DependencyInject; @@ -87,7 +87,7 @@ public static class AddApplicationService // services.AddScoped(); // 注册JWT配置 - services.Configure(configuration.GetSection("Jwt")); + services.Configure(configuration.GetSection("Jwt")); // 注册工作单元 services.AddScoped(); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs index 2026d77..e4aa211 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs @@ -20,6 +20,8 @@ using UniversalAdminSystem.Application.PermissionManagement.Services; using UniversalAdminSystem.Application.Authentication.Interfaces; using UniversalAdminSystem.Infrastructure.FileStorage; using FluentValidation; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Infrastructure.Configs; namespace UniversalAdminSystem.Infrastructure.DependencyInject; @@ -28,10 +30,7 @@ public static class AddInfrastrutureService /// /// 自动扫描并批量注册仓储接口及其实现(支持接口和实现分布在不同程序集,约定接口名为IXXXRepository,实现名为XXXRepository) /// - public static IServiceCollection AddRepositoriesByConvention( - this IServiceCollection services, - Assembly[] interfaceAssemblies, - Assembly[] implementationAssemblies) + public static IServiceCollection AddRepositoriesByConvention(this IServiceCollection services, Assembly[] interfaceAssemblies, Assembly[] implementationAssemblies) { var interfaces = interfaceAssemblies .SelectMany(a => a.GetTypes()) @@ -51,13 +50,68 @@ public static class AddInfrastrutureService return services; } + public static IServiceCollection AddAllConfig(this IServiceCollection services, IConfiguration configuration) + { + var asm = Assembly.GetExecutingAssembly(); + var configTypes = asm.GetTypes().Where(t => t.Name.EndsWith("Config")).ToList(); + + Console.WriteLine($"找到 {configTypes.Count} 个配置类型:"); + foreach (var type in configTypes) + { + Console.WriteLine($" - {type.Name}"); + } + + foreach (var type in configTypes) + { + var sectionName = type.Name.Replace("Config", ""); + Console.WriteLine($"正在注册配置类型: {type.Name} -> 节名: {sectionName}"); + + // 检查配置节是否存在 + var section = configuration.GetSection(sectionName); + Console.WriteLine($" 配置节 {sectionName} 是否存在: {section.Exists()}"); + + if (section.Exists()) + { + // 使用反射调用泛型方法 + var configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions) + .GetMethods() + .FirstOrDefault(m => m.Name == "Configure" && m.IsGenericMethod && m.GetParameters().Length == 2); + + if (configureMethod != null) + { + try + { + var genericMethod = configureMethod.MakeGenericMethod(type); + genericMethod.Invoke(null, new object[] { services, section }); + Console.WriteLine($" 成功注册配置类型: {type.Name}"); + } + catch (Exception ex) + { + Console.WriteLine($" 注册配置类型 {type.Name} 失败: {ex.Message}"); + } + } + else + { + Console.WriteLine($" 未找到 Configure 方法"); + } + } + else + { + Console.WriteLine($" 配置节 {sectionName} 不存在,跳过注册"); + } + } + return services; + } + + public static IServiceCollection AddInfrastruture(this IServiceCollection services, IConfiguration configuration) { + // services.Configure(configuration.GetSection("AllowedFiles")); // 注册数据库上下文 services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"))); - // 注册JWT配置 - services.Configure(configuration.GetSection("Jwt")); + // 注册配置 + services.AddAllConfig(configuration); // 自动注册所有仓储 services.AddRepositoriesByConvention( @@ -83,15 +137,6 @@ public static class AddInfrastrutureService } ); - // 手动注册仓储(确保所有仓储都被注册) - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - // 注册通用IRepository接口到具体实现 services.AddScoped(typeof(IRepository), typeof(LogEntryRepository)); services.AddScoped(typeof(IRepository), typeof(SystemSettingRepository)); @@ -104,7 +149,7 @@ public static class AddInfrastrutureService // 注册K2模型相关服务 services.AddHttpClient(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); // 注册权限相关服务 services.AddScoped(); @@ -113,7 +158,6 @@ public static class AddInfrastrutureService // 注册缓存服务 services.AddMemoryCache(); - // services.AddScoped(); // 注册文件存储服务 services.AddScoped(); @@ -123,9 +167,6 @@ public static class AddInfrastrutureService services.AddHostedService(); services.AddSingleton(); - // 自动注册 Application 层所有 FluentValidation 校验器 - services.AddValidatorsFromAssembly(Assembly.Load("UniversalAdminSystem.Application")); - return services; } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs index 71e29de..99b73ed 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/ServiceCollectionExtensions.cs @@ -5,7 +5,7 @@ namespace UniversalAdminSystem.Infrastructure.DependencyInject; public static class ServiceCollectionExtensions { - public static IServiceCollection AddAllServiceRegistrations(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddAllService(this IServiceCollection services, IConfiguration configuration) { services.AddDomain(); services.AddApplication(configuration); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs new file mode 100644 index 0000000..c749fb3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs @@ -0,0 +1,96 @@ +using FileSignatures; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Infrastructure.Configs; + +namespace UniversalAdminSystem.Infrastructure.FileStorage; + +public class FileValidationService : IFileValidationService +{ + private readonly AllowedFilesConfig _allowedFiles; + private readonly FileFormatInspector _fileFormatInspector; + private readonly ILogger _logger; + + public FileValidationService(IOptions allowedFiles, ILogger logger) + { + _allowedFiles = allowedFiles.Value; + _fileFormatInspector = new FileFormatInspector(); + _logger = logger; + + // 添加调试信息 + Console.WriteLine($"FileValidationService 构造函数 - 配置加载状态:"); + Console.WriteLine($" AllowedFiles 是否为 null: {_allowedFiles == null}"); + Console.WriteLine($" AllowedFiles.AllowedFiles 是否为 null: {_allowedFiles?.AllowedFiles == null}"); + Console.WriteLine($" AllowedFiles.AllowedFiles 数量: {_allowedFiles?.AllowedFiles?.Count() ?? 0}"); + + if (_allowedFiles?.AllowedFiles != null) + { + foreach (var file in _allowedFiles.AllowedFiles) + { + Console.WriteLine($" - {file.Mime} ({file.Ext})"); + } + } + } + + public (bool isValid, string message) ValidateFile(long fileSize, Stream stream) + { + try + { + if (stream == null) return (false, "❌ 文件为空"); + + // 检查文件大小 + if (fileSize > 100 * 1024 * 1024) return (false, $"❌ 文件大小超过限制:100MB"); + _logger.LogInformation($"大小: {fileSize:N0} bytes ({FormatFileSize(fileSize)})"); + + // 检查流状态 + Console.WriteLine($"流状态 - CanRead: {stream.CanRead}, CanSeek: {stream.CanSeek}, Length: {stream.Length}"); + + // 检查MIME类型是否匹配 + Console.WriteLine("开始检查文件格式..."); + var fileFormat = _fileFormatInspector.DetermineFileFormat(stream); + Console.WriteLine($"文件格式检测结果: {fileFormat?.MediaType ?? "未知"}"); + Console.WriteLine($"允许的文件类型数量: {_allowedFiles.AllowedFiles?.Count() ?? 0}"); + + if (_allowedFiles.AllowedFiles?.FirstOrDefault(f => f.Mime == fileFormat?.MediaType) == null) + { + return (false, $"❌ 不允许的文件类型: {fileFormat?.MediaType ?? "未知"}"); + } + + // 重置流位置,以便后续使用 + if (stream.CanSeek) + { + stream.Position = 0; + } + + return (true, $"✅ 文件校验通过: {fileFormat?.MediaType}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "文件验证过程中发生异常"); + Console.WriteLine($"文件验证异常: {ex.Message}"); + return (false, $"❌ 文件验证失败: {ex.Message}"); + } + } + + private string FormatFileSize(long bytes) + { + string[] sizes = { "B", "KB", "MB", "GB" }; + double len = bytes; + int order = 0; + while (len >= 1024 && order < sizes.Length - 1) + { + order++; + len = len / 1024; + } + return $"{len:0.##} {sizes[order]}"; + } + + // public static bool IsPlainText(Stream stream, int maxBytes = 512) + // { + // stream.Position = 0; + // Span buf = stackalloc byte[maxBytes]; + // int read = stream.Read(buf); + // return !buf[..read].ContainsAny(stackalloc byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127 }); + // } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs index b624516..8caeebe 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/LocalFileStorageService.cs @@ -1,51 +1,29 @@ using System.Collections.Concurrent; using Microsoft.AspNetCore.Http; using UniversalAdminSystem.Application.FileStorage.DTOs; -using UniversalAdminSystem.Domian.FileStorage.Interface; +using UniversalAdminSystem.Application.FileStorage.Interfaces; namespace UniversalAdminSystem.Infrastructure.FileStorage; public class LocalFileStorageService : IFileStorageService { - private readonly IFileDomainService _fileDomainService; - private readonly string _basePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads"); + private readonly string _basePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads"); private static readonly ConcurrentDictionary _fileIndex = new(); - public LocalFileStorageService(IFileDomainService fileDomainService) + public LocalFileStorageService() { - _fileDomainService = fileDomainService; - - if (!Directory.Exists(_basePath)) - Directory.CreateDirectory(_basePath); + if (!Directory.Exists(_basePath)) Directory.CreateDirectory(_basePath); } - public async Task UploadAsync(IFormFile file, Stream fileStream) + public async Task SaveAsync(IFormFile file) { try { - if (!_fileDomainService.CheckFileSecurity(out var result, file.Name, file.ContentType)) - throw new InvalidOperationException($"上传失败!{result}"); - - var filePath = Path.Combine(_basePath, file.FileName); - using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - await fileStream.CopyToAsync(fs); - } - var fileInfo = new FileInfo(filePath); - var dto = new FileDto( - Guid.NewGuid(), // Id - file.Name, // Name - $"/uploads/{file.FileName}", // Path - fileInfo.Length, // Size - "application/octet-stream", // Type - Guid.Empty, // OwnerId (临时值) - DateTime.UtcNow, // UploadTime - false, // IsFolder - null, // ParentId - "Private" // AccessLevel - ); - _fileIndex[file.Name] = dto; - return dto.Path; + var uniqueFileName = Guid.NewGuid().ToString() + "_" + file.FileName; + var filePath = Path.Combine(_basePath, uniqueFileName); + using var stream = new FileStream(filePath, FileMode.Create); + await file.CopyToAsync(stream); + return new FileInfo(filePath); } catch { @@ -56,7 +34,7 @@ public class LocalFileStorageService : IFileStorageService public async Task DownloadAsync(string fileName) { var filePath = Path.Combine(_basePath, fileName); - + if (!File.Exists(filePath)) throw new FileNotFoundException(); var ms = new MemoryStream(); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) @@ -79,4 +57,4 @@ public class LocalFileStorageService : IFileStorageService { return Task.FromResult(_fileIndex.Values.AsEnumerable()); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/FileScaner.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/FileScaner.cs new file mode 100644 index 0000000..b2e4c5e --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/FileScaner.cs @@ -0,0 +1,38 @@ +namespace UniversalAdminSystem.Infrastructure.Services; + +public class FileScaner +{ + public static IEnumerable ScanDirectory(string path, string searchPattern) + { + var fileSystemInfos = new List(); + if (!Directory.Exists(path)) throw new DirectoryNotFoundException($"目录不存在: {path}"); + try + { + ScanInternal(path, searchPattern, fileSystemInfos); + } + catch (UnauthorizedAccessException) + { + throw new UnauthorizedAccessException($"无权限访问: {path}"); + } + return fileSystemInfos; + } + + private static void ScanInternal(string path, string searchPattern, List fileSystemInfos) + { + var directoryInfo = new DirectoryInfo(path); + fileSystemInfos.Add(directoryInfo); + var files = directoryInfo.GetFiles(searchPattern); + fileSystemInfos.AddRange(files); + foreach (var subDirectory in directoryInfo.GetDirectories(searchPattern)) + { + try + { + ScanInternal(subDirectory.FullName, searchPattern, fileSystemInfos); + } + catch (UnauthorizedAccessException) + { + continue; + } + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs deleted file mode 100644 index 5b3015f..0000000 --- a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ConfigService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Options; -using UniversalAdminSystem.Infrastructure.Configs; - -namespace UniversalAdminSystem.Infrastructure.Services; - -public class K2ConfigService -{ - private readonly K2Config _k2Config; - - public K2ConfigService(IOptions k2Config) - { - _k2Config = k2Config.Value; - } - - public K2Config GetK2Config() - { - return _k2Config; - } -} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs index 291fdfd..600c365 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/K2ModelService.cs @@ -11,30 +11,27 @@ public class K2ModelService private readonly K2Config _k2Config; private readonly ILogger _logger; private readonly HttpClient _httpClient; + public event OnContentChunkReceivedHandler? OnContentChunkReceived; - public K2ModelService(IOptions k2Config, ILogger logger, HttpClient httpClient) + public delegate void OnContentChunkReceivedHandler(string chunk); + + public K2ModelService(IOptions k2Config, ILogger logger, IHttpClientFactory httpClientFactory) { _k2Config = k2Config.Value; _logger = logger; - _httpClient = httpClient; - - // 设置默认请求头 + _httpClient = httpClientFactory.CreateClient("K2Client"); + _httpClient.BaseAddress = new Uri(_k2Config.BaseUrl); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_k2Config.ApiKey}"); - _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json"); + _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); } - /// - /// 发送聊天请求到K2模型 - /// - /// 聊天消息列表 - /// 温度参数,控制随机性 - /// 最大token数 - /// 模型响应 - public async Task SendChatRequestAsync(List messages, string model = "Moonshot-Kimi-K2-Instruct", float temperature = 0.7f, int maxTokens = 1000) + public async IAsyncEnumerable SendChatRequestAsync(List messages, string model = "Moonshot-Kimi-K2-Instruct", float temperature = 0.7f, int maxTokens = 1000) { + HttpRequestMessage request; + HttpResponseMessage response; try { - _logger.LogInformation("开始发送K2模型请求"); + _logger.LogInformation("开始发送K2模型请求,使用模型: {Model}", model); var k2Request = new K2Request { Model = model, @@ -42,45 +39,82 @@ public class K2ModelService Temperature = temperature, MaxTokens = maxTokens }; - var resquest = new HttpRequestMessage(HttpMethod.Post, $"{_k2Config.BaseUrl}/chat/completions") + request = new HttpRequestMessage(HttpMethod.Post, $"{_k2Config.BaseUrl}/chat/completions") { - Content = new StringContent(JsonSerializer.Serialize(k2Request), Encoding.UTF8, "application/json") + Content = new StringContent(JsonSerializer.Serialize(k2Request, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }), Encoding.UTF8, "application/json") }; - var response = await _httpClient.SendAsync(resquest, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None); + response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None); response.EnsureSuccessStatusCode(); - await using var responseStream = await response.Content.ReadAsStreamAsync(); - using var reader = new StreamReader(responseStream, Encoding.UTF8); - var sb = new StringBuilder(); - while (!reader.EndOfStream) - { - var line = await reader.ReadLineAsync(); - if (string.IsNullOrWhiteSpace(line)) continue; - sb.Append(line); - } - _logger.LogInformation("K2模型请求成功完成"); - var k2Response = JsonSerializer.Deserialize(sb.ToString()); - return k2Response ?? throw new Exception("无法解析K2模型响应"); } catch (Exception ex) { _logger.LogError(ex, "K2模型请求过程中发生错误"); throw; } + await using var responseStream = await response.Content.ReadAsStreamAsync(); + await foreach (var chunk in ParseStreamingResponseAsync(responseStream)) + { + yield return chunk; + } + _logger.LogInformation("K2模型请求成功完成"); } - /// - /// 发送简单的文本请求 - /// - /// 提示文本 - /// 模型响应 - public async Task SendSimpleRequestAsync(string prompt) + private async IAsyncEnumerable ParseStreamingResponseAsync(Stream responseStream) { - var messages = new List + using var reader = new StreamReader(responseStream); + var fullContent = new StringBuilder(); + var responseId = ""; + var responseModel = ""; + long responseCreated = 0; + var responseObject = ""; + + var chunk = new K2StreamResponse(); + while (!reader.EndOfStream) { - new K2Message { Role = "user", Content = prompt } - }; + var line = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(line)) continue; + var trimmedLine = line.Trim(); + if (trimmedLine.StartsWith("data: ")) + { + var jsonData = trimmedLine.Substring(6); + if (jsonData.Trim() == "[DONE]") continue; + try + { + // 解析每个数据块 + chunk = JsonSerializer.Deserialize(jsonData, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + } + catch (JsonException ex) + { + _logger.LogWarning("解析数据块失败: {JsonData}, 错误: {Error}", jsonData, ex.Message); + } + + if (chunk != null) + { + // 保存响应元数据 + if (!string.IsNullOrEmpty(chunk.Id)) responseId = chunk.Id; + if (!string.IsNullOrEmpty(chunk.Model)) responseModel = chunk.Model; + if (chunk.Created > 0) responseCreated = chunk.Created; + if (!string.IsNullOrEmpty(chunk.Object)) responseObject = chunk.Object; - var response = await SendChatRequestAsync(messages); - return response?.Choices?.FirstOrDefault()?.Message?.Content ?? string.Empty; + // 处理choices + if (chunk.Choices != null && chunk.Choices.Count > 0) + { + var choice = chunk.Choices[0]; + if (choice.Delta != null && !string.IsNullOrEmpty(choice.Delta.Content)) + { + fullContent.Append(choice.Delta.Content); + OnContentChunkReceived?.Invoke(choice.Delta.Content); + yield return choice.Delta.Content; + } + } + } + } + } } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Services/TextExtractor.cs b/backend/src/UniversalAdminSystem.Infrastructure/Services/TextExtractor.cs new file mode 100644 index 0000000..6c35810 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Services/TextExtractor.cs @@ -0,0 +1,6 @@ +namespace UniversalAdminSystem.Infrastructure.Services; + +public class TextExtractor +{ + +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj index c7d12bd..307dcce 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj +++ b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj @@ -5,9 +5,11 @@ + + -- Gitee From 9f6ed169baa73898b06d5b8714cbbf0b03516f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=97=AD?= <1619917346@qq.com> Date: Fri, 8 Aug 2025 15:55:47 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=EF=BC=8C=E6=B5=8B=E8=AF=95=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AllowedFiles.json | 166 +++++++++--------- .../UniversalAdminSystem.Api.http | 4 +- .../FileStorage/Services/FileAppService.cs | 8 +- .../FileStorage/FileValidationService.cs | 14 -- 4 files changed, 87 insertions(+), 105 deletions(-) diff --git a/backend/src/UniversalAdminSystem.Api/AllowedFiles.json b/backend/src/UniversalAdminSystem.Api/AllowedFiles.json index 02cda35..34cf87d 100644 --- a/backend/src/UniversalAdminSystem.Api/AllowedFiles.json +++ b/backend/src/UniversalAdminSystem.Api/AllowedFiles.json @@ -1,84 +1,86 @@ { - "AllowedFiles": [ - { - "Mime": "image/jpeg", - "Ext": "jpg", - "Signature": "FF D8" - }, - { - "Mime": "image/png", - "Ext": "png", - "Signature": "89 50 4E 47 0D 0A 1A 0A" - }, - { - "Mime": "image/gif", - "Ext": "gif", - "Signature": "47 49 46 38 37 61" - }, - { - "Mime": "image/gif", - "Ext": "gif", - "Signature": "47 49 46 38 39 61" - }, - { - "Mime": "image/bmp", - "Ext": "bmp", - "Signature": "42 4D" - }, - { - "Mime": "image/webp", - "Ext": "webp", - "Signature": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50" - }, - { - "Mime": "application/pdf", - "Ext": "pdf", - "Signature": "25 50 44 46" - }, - { - "Mime": "application/json", - "Ext": "json", - "Signature": "" - }, - { - "Mime": "text/markdown", - "Ext": "md", - "Signature": "" - }, - { - "Mime": "text/plain", - "Ext": "txt", - "Signature": "" - }, - { - "Mime": "application/xml", - "Ext": "xml", - "Signature": "" - }, - { - "Mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "Ext": "docx", - "Signature": "50 4B 03 04" - }, - { - "Mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Ext": "xlsx", - "Signature": "50 4B 03 04" - }, - { - "Mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "Ext": "pptx", - "Signature": "50 4B 03 04" - }, - { - "Mime": "application/vnd.ms-excel", - "Ext": "xls", - "Signature": "D0 CF 11 E0 A1 B1 1A E1" - }, - { - "Mime": "application/msword", - "Ext": "doc", - "Signature": "D0 CF 11 E0 A1 B1 1A E1" - } - ] + "AllowedFiles": { + "AllowedFiles": [ + { + "Mime": "image/jpeg", + "Ext": "jpg", + "Signature": "FF D8" + }, + { + "Mime": "image/png", + "Ext": "png", + "Signature": "89 50 4E 47 0D 0A 1A 0A" + }, + { + "Mime": "image/gif", + "Ext": "gif", + "Signature": "47 49 46 38 37 61" + }, + { + "Mime": "image/gif", + "Ext": "gif", + "Signature": "47 49 46 38 39 61" + }, + { + "Mime": "image/bmp", + "Ext": "bmp", + "Signature": "42 4D" + }, + { + "Mime": "image/webp", + "Ext": "webp", + "Signature": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50" + }, + { + "Mime": "application/pdf", + "Ext": "pdf", + "Signature": "25 50 44 46" + }, + { + "Mime": "application/json", + "Ext": "json", + "Signature": "" + }, + { + "Mime": "text/markdown", + "Ext": "md", + "Signature": "" + }, + { + "Mime": "text/plain", + "Ext": "txt", + "Signature": "" + }, + { + "Mime": "application/xml", + "Ext": "xml", + "Signature": "" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Ext": "docx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Ext": "xlsx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Ext": "pptx", + "Signature": "50 4B 03 04" + }, + { + "Mime": "application/vnd.ms-excel", + "Ext": "xls", + "Signature": "D0 CF 11 E0 A1 B1 1A E1" + }, + { + "Mime": "application/msword", + "Ext": "doc", + "Signature": "D0 CF 11 E0 A1 B1 1A E1" + } + ] + } } diff --git a/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http index cb7770f..abcc71f 100644 --- a/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http +++ b/backend/src/UniversalAdminSystem.Api/UniversalAdminSystem.Api.http @@ -6,6 +6,4 @@ Content-Type: application/json { "account": "manager", "password": "manager123" -} - -### +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs index c4c82f1..8960df7 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs @@ -33,13 +33,9 @@ public class FileAppService : IFileAppService var (isValid, message) = _fileValidator.ValidateFile(file.Length, fileStream); if (!isValid) throw new Exception($"校验结果: {message}"); var fileInfo = await _localfileStorage.SaveAsync(file); + Console.WriteLine("开启事务"); await _unitOfWork.BeginTransactionAsync(); - // Console.WriteLine("-------------"); - // Console.WriteLine($"{fileInfo.Name}"); - // Console.WriteLine($"{fileInfo.FullName}"); - // Console.WriteLine($"{file.Length}"); - // Console.WriteLine($"{file.ContentType}"); - // Console.WriteLine("-------------"); + Console.WriteLine("创建实体"); var fileEntity = File.Create ( FileName.Create(fileInfo.Name), diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs index c749fb3..1961edc 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs @@ -17,20 +17,6 @@ public class FileValidationService : IFileValidationService _allowedFiles = allowedFiles.Value; _fileFormatInspector = new FileFormatInspector(); _logger = logger; - - // 添加调试信息 - Console.WriteLine($"FileValidationService 构造函数 - 配置加载状态:"); - Console.WriteLine($" AllowedFiles 是否为 null: {_allowedFiles == null}"); - Console.WriteLine($" AllowedFiles.AllowedFiles 是否为 null: {_allowedFiles?.AllowedFiles == null}"); - Console.WriteLine($" AllowedFiles.AllowedFiles 数量: {_allowedFiles?.AllowedFiles?.Count() ?? 0}"); - - if (_allowedFiles?.AllowedFiles != null) - { - foreach (var file in _allowedFiles.AllowedFiles) - { - Console.WriteLine($" - {file.Mime} ({file.Ext})"); - } - } } public (bool isValid, string message) ValidateFile(long fileSize, Stream stream) -- Gitee From 67c593ac6b28d40a64cbaf7418240f065b6f223d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=97=AD?= <1619917346@qq.com> Date: Sat, 9 Aug 2025 05:18:36 +0800 Subject: [PATCH 09/24] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniversalAdminSystem.Api/appsettings.json | 2 +- .../FileStorage/Factory/DocParserFactory.cs | 7 ++++ .../FileStorage/Interfaces/IDocParser.cs | 7 ++++ .../Interfaces/IFileValidationService.cs | 3 +- .../FileStorage/Services/FileAppService.cs | 38 +++++++++++++++++-- .../IFileDomainService.cs | 2 +- .../FileStorage/Services/FileDomainService.cs | 3 +- .../FileStorage/FileValidationService.cs | 16 ++++---- .../FileStorage/MdParseService.cs | 34 +++++++++++++++++ .../FileStorage/TxtParseService.cs | 0 ...UniversalAdminSystem.Infrastructure.csproj | 2 + 11 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs rename backend/src/UniversalAdminSystem.Domian/FileStorage/{Interface => Interfaces}/IFileDomainService.cs (67%) create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs diff --git a/backend/src/UniversalAdminSystem.Api/appsettings.json b/backend/src/UniversalAdminSystem.Api/appsettings.json index 3b56ca8..5a0c1dd 100644 --- a/backend/src/UniversalAdminSystem.Api/appsettings.json +++ b/backend/src/UniversalAdminSystem.Api/appsettings.json @@ -8,7 +8,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "pgSql": "server=127.0.0.1;port=5433;uid=postgres;password=123456;database=universal_admin" + "pgSql": "server=127.0.0.1;port=5432;uid=postgres;password=031028@yue;database=universal_admin" }, "Jwt": { "Key": "YourSuperSecretKey1232347509872093oiqewupori", diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs new file mode 100644 index 0000000..58bbea3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs @@ -0,0 +1,7 @@ +using UniversalAdminSystem.Domian.FileStorage.Interfaces; + +namespace UniversalAdminSystem.Domian.FileStorage.Factory; + +public static class DocParserFactory +{ +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs new file mode 100644 index 0000000..067113c --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs @@ -0,0 +1,7 @@ +namespace UniversalAdminSystem.Application.FileStorage.Interfaces; + +public interface IDocParser +{ + // bool CanParse(); + string Parse(string path); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs index 68d5fa8..95d65cb 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileValidationService.cs @@ -2,6 +2,5 @@ namespace UniversalAdminSystem.Application.FileStorage.Interfaces; public interface IFileValidationService { - (bool isValid, string message) ValidateFile(long fileSize, Stream stream); - + (bool isValid, string message, string? format) ValidateFile(long fileSize, Stream stream); } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs index 8960df7..78eac96 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs @@ -30,12 +30,12 @@ public class FileAppService : IFileAppService try { using var fileStream = file.OpenReadStream(); - var (isValid, message) = _fileValidator.ValidateFile(file.Length, fileStream); + var (isValid, message, format) = _fileValidator.ValidateFile(file.Length, fileStream); if (!isValid) throw new Exception($"校验结果: {message}"); var fileInfo = await _localfileStorage.SaveAsync(file); - Console.WriteLine("开启事务"); + Console.WriteLine("开启事务..."); await _unitOfWork.BeginTransactionAsync(); - Console.WriteLine("创建实体"); + Console.WriteLine("创建实体..."); var fileEntity = File.Create ( FileName.Create(fileInfo.Name), @@ -46,7 +46,30 @@ public class FileAppService : IFileAppService false, null ); + Console.WriteLine($"文件信息打印------------"); + Console.WriteLine($"文件名:{fileEntity.Name}"); + Console.WriteLine($"文件url:{fileEntity.Path}"); + Console.WriteLine($"文件大小:{fileEntity.Size}"); + Console.WriteLine($"文件类型:{fileEntity.Type}"); + Console.WriteLine("添加数据到数据库..."); await _fileRepository.AddAsync(fileEntity); + Console.WriteLine("添加数据到成功!提交事务!"); + await _unitOfWork.CommitAsync(); + + // 后续文档异步处理 + // ... + _ = Task.Run(async () => + { + try + { + await FileProcessingAsync(); + } + catch (Exception) + { + throw; + } + }); + return new FileUploadResultDto ( fileEntity.Id.Value, @@ -59,11 +82,20 @@ public class FileAppService : IFileAppService } catch (Exception ex) { + Console.WriteLine("添加数据到数据库失败!回滚事务!"); await _unitOfWork.RollbackAsync(); return new FileUploadResultDto(Message: ex.Message); } } + private async Task FileProcessingAsync() + { + // 文本提取 + // 文本分片 + // 向量化 + // 向量入库 + } + public async Task> GetList() { var files = await _fileRepository.GetAllAsync(); diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Interfaces/IFileDomainService.cs similarity index 67% rename from backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs rename to backend/src/UniversalAdminSystem.Domian/FileStorage/Interfaces/IFileDomainService.cs index 6e9e172..3872ef9 100644 --- a/backend/src/UniversalAdminSystem.Domian/FileStorage/Interface/IFileDomainService.cs +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Interfaces/IFileDomainService.cs @@ -1,4 +1,4 @@ -namespace UniversalAdminSystem.Domian.FileStorage.Interface; +namespace UniversalAdminSystem.Domian.FileStorage.Interfaces; public interface IFileDomainService { diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs index b4b22d4..835a7cc 100644 --- a/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Services/FileDomainService.cs @@ -1,5 +1,4 @@ -using UniversalAdminSystem.Domian.FileStorage.Interface; -using UniversalAdminSystem.Domian.FileStorage.ValueObjects; +using UniversalAdminSystem.Domian.FileStorage.Interfaces; namespace UniversalAdminSystem.Domian.FileStorage.Services; diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs index 1961edc..13ef65d 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/FileValidationService.cs @@ -19,14 +19,14 @@ public class FileValidationService : IFileValidationService _logger = logger; } - public (bool isValid, string message) ValidateFile(long fileSize, Stream stream) + public (bool isValid, string message, string? format) ValidateFile(long fileSize, Stream stream) { try { - if (stream == null) return (false, "❌ 文件为空"); + if (stream == null) return (false, "❌ 文件为空", string.Empty); // 检查文件大小 - if (fileSize > 100 * 1024 * 1024) return (false, $"❌ 文件大小超过限制:100MB"); + if (fileSize > 100 * 1024 * 1024) return (false, $"❌ 文件大小超过限制:100MB", string.Empty); _logger.LogInformation($"大小: {fileSize:N0} bytes ({FormatFileSize(fileSize)})"); // 检查流状态 @@ -40,7 +40,7 @@ public class FileValidationService : IFileValidationService if (_allowedFiles.AllowedFiles?.FirstOrDefault(f => f.Mime == fileFormat?.MediaType) == null) { - return (false, $"❌ 不允许的文件类型: {fileFormat?.MediaType ?? "未知"}"); + return (false, $"❌ 不允许的文件类型: {fileFormat?.MediaType ?? "未知"}", fileFormat?.MediaType); } // 重置流位置,以便后续使用 @@ -49,13 +49,13 @@ public class FileValidationService : IFileValidationService stream.Position = 0; } - return (true, $"✅ 文件校验通过: {fileFormat?.MediaType}"); + return (true, $"✅ 文件校验通过: {fileFormat?.MediaType}", fileFormat?.MediaType); } catch (Exception ex) { _logger.LogError(ex, "文件验证过程中发生异常"); Console.WriteLine($"文件验证异常: {ex.Message}"); - return (false, $"❌ 文件验证失败: {ex.Message}"); + return (false, $"❌ 文件验证失败: {ex.Message}", string.Empty); } } @@ -79,4 +79,6 @@ public class FileValidationService : IFileValidationService // int read = stream.Read(buf); // return !buf[..read].ContainsAny(stackalloc byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127 }); // } -} \ No newline at end of file +} + +public record ValidationResult(bool IsValid, string Format, string Message); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs new file mode 100644 index 0000000..a363362 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs @@ -0,0 +1,34 @@ +using System.Net; +using System.Text.RegularExpressions; +using HtmlAgilityPack; +using Markdig; +using UniversalAdminSystem.Application.FileStorage.Interfaces; + +namespace UniversalAdminSystem.Infrastructure.FileStorage; + +public sealed partial class MdParseService : IDocParser +{ + private static readonly Regex _blankLines = MyRegex(); + + public string Parse(string path) + { + if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("文件路径不能为空", nameof(path)); + using var reader = new StreamReader(path); + var mdContent = reader.ReadToEnd(); + return Convert(mdContent); + } + + private static string Convert(string markdown) + { + if (string.IsNullOrWhiteSpace(markdown)) return string.Empty; + var html = Markdown.ToHtml(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build()); + var doc = new HtmlDocument(); + doc.LoadHtml(html); + doc.DocumentNode.SelectNodes("//script|//style|//comment()")?.ToList().ForEach(n => n.Remove()); + var text = WebUtility.HtmlDecode(doc.DocumentNode.InnerText); + return _blankLines.Replace(text.Trim(), "\n\n"); + } + + [GeneratedRegex(@"\r?\n{2,}", RegexOptions.Compiled)] + private static partial Regex MyRegex(); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj index bc6fe7f..3302db0 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj +++ b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj @@ -7,6 +7,8 @@ + + -- Gitee From c829e8584fdd62abe682dc47d7b769a1d927c4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=97=AD?= <1619917346@qq.com> Date: Sun, 10 Aug 2025 23:28:52 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E9=98=9F=E5=88=97+=E6=B6=88=E6=81=AF=E9=98=9F=E5=88=97?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniversalAdminSystem.Api/appsettings.json | 12 ++- .../Common/interfaces/IEventBus.cs | 6 ++ .../FileStorage/DTOs/FileProcessingJobDto.cs | 3 + .../FileStorage/Factory/DocParserFactory.cs | 7 -- .../FileStorage/Interfaces/IDocParser.cs | 2 +- .../Interfaces/IFileProcessingQueue.cs | 8 ++ .../FileStorage/Services/FileAppService.cs | 31 +++----- .../FileStorage/Aggregates/File.cs | 6 +- .../Configs/RabbitMqConfig.cs | 27 +++++++ .../AddInfrastructureService.cs | 42 +++++++++- .../FileStorage/Parsers/DocParserFactory.cs | 17 ++++ .../{ => Parsers}/MdParseService.cs | 6 +- .../FileStorage/Parsers/PdfParseService.cs | 30 ++++++++ .../FileStorage/Parsers/TxtParseService.cs | 11 +++ .../FileStorage/TxtParseService.cs | 0 .../UniversalAdminSystemDbContext.cs | 13 +++- .../Consumers/FileProcessingJobConsumer.cs | 77 +++++++++++++++++++ .../RabbitMQ/Jobs/FileProcessingJob.cs | 14 ++++ .../Publishers/FileProcessingJobPublisher.cs | 60 +++++++++++++++ .../RabbitMQ/Queues/EfFileProcessingQueue.cs | 40 ++++++++++ .../RabbitMQ/RabbitMqEventBus.cs | 30 ++++++++ ...UniversalAdminSystem.Infrastructure.csproj | 3 + 22 files changed, 400 insertions(+), 45 deletions(-) create mode 100644 backend/src/UniversalAdminSystem.Application/Common/interfaces/IEventBus.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileProcessingJobDto.cs delete mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs create mode 100644 backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileProcessingQueue.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Configs/RabbitMqConfig.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/DocParserFactory.cs rename backend/src/UniversalAdminSystem.Infrastructure/FileStorage/{ => Parsers}/MdParseService.cs (86%) create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/PdfParseService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/TxtParseService.cs delete mode 100644 backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Consumers/FileProcessingJobConsumer.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Jobs/FileProcessingJob.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Publishers/FileProcessingJobPublisher.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Queues/EfFileProcessingQueue.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/RabbitMqEventBus.cs diff --git a/backend/src/UniversalAdminSystem.Api/appsettings.json b/backend/src/UniversalAdminSystem.Api/appsettings.json index 5a0c1dd..8db1331 100644 --- a/backend/src/UniversalAdminSystem.Api/appsettings.json +++ b/backend/src/UniversalAdminSystem.Api/appsettings.json @@ -8,7 +8,8 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "pgSql": "server=127.0.0.1;port=5432;uid=postgres;password=031028@yue;database=universal_admin" + "pgSql": "server=127.0.0.1;port=5432;uid=postgres;password=031028@yue;database=universal_admin", + "RabbitMq": "amqp://guest:guest@rabbitmq:5672/" }, "Jwt": { "Key": "YourSuperSecretKey1232347509872093oiqewupori", @@ -19,5 +20,14 @@ "K2": { "BaseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1/", "ApiKey": "sk-a31163f6b59e44dcbdb87c668482ce96" + }, + "RabbitMq": { + "Host": "rabbitmq", + "Port": 5672, + "Username": "guest", + "Password": "guest", + "Exchange": "file-processing", + "Queue": "file-processing-queue", + "RoutingKey": "file-processing" } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/Common/interfaces/IEventBus.cs b/backend/src/UniversalAdminSystem.Application/Common/interfaces/IEventBus.cs new file mode 100644 index 0000000..c8646a9 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/Common/interfaces/IEventBus.cs @@ -0,0 +1,6 @@ +namespace UniversalAdminSystem.Application.Common.Interfaces; + +public interface IEventBus +{ + Task PublishAsync(string exchange, string routingKey, string payload, string? messageId = null, CancellationToken ct = default); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileProcessingJobDto.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileProcessingJobDto.cs new file mode 100644 index 0000000..1781585 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/DTOs/FileProcessingJobDto.cs @@ -0,0 +1,3 @@ +namespace UniversalAdminSystem.Application.FileStorage.DTOs; + +public record FileProcessingJobDto(Guid FileId, string FilePath, string ContentType, long Size); \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs deleted file mode 100644 index 58bbea3..0000000 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Factory/DocParserFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using UniversalAdminSystem.Domian.FileStorage.Interfaces; - -namespace UniversalAdminSystem.Domian.FileStorage.Factory; - -public static class DocParserFactory -{ -} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs index 067113c..dc2f7dd 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IDocParser.cs @@ -3,5 +3,5 @@ namespace UniversalAdminSystem.Application.FileStorage.Interfaces; public interface IDocParser { // bool CanParse(); - string Parse(string path); + Task ParseAsync(string path); } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileProcessingQueue.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileProcessingQueue.cs new file mode 100644 index 0000000..1c0b5a6 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Interfaces/IFileProcessingQueue.cs @@ -0,0 +1,8 @@ +using UniversalAdminSystem.Application.FileStorage.DTOs; + +namespace UniversalAdminSystem.Application.FileStorage.Interfaces; + +public interface IFileProcessingQueue +{ + Task EnqueueAsync(FileProcessingJobDto jd, CancellationToken ct = default); +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs index 78eac96..2fad793 100644 --- a/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs +++ b/backend/src/UniversalAdminSystem.Application/FileStorage/Services/FileAppService.cs @@ -15,14 +15,16 @@ public class FileAppService : IFileAppService private readonly IFileValidationService _fileValidator; private readonly IFileStorageService _localfileStorage; private readonly IUnitOfWork _unitOfWork; + private readonly IFileProcessingQueue _fileProcessingQueue; - public FileAppService(IFileRepository fileRepository, IFileValidationService fileValidator, IFileStorageService localfileStorage, IUnitOfWork unitOfWork) + public FileAppService(IFileRepository fileRepository, IFileValidationService fileValidator, IFileStorageService localfileStorage, IUnitOfWork unitOfWork, IFileProcessingQueue fileProcessingQueue) { _fileRepository = fileRepository; _fileValidator = fileValidator; _localfileStorage = localfileStorage; _unitOfWork = unitOfWork; + _fileProcessingQueue = fileProcessingQueue; } public async Task UploadAsync(IFormFile file) @@ -56,19 +58,12 @@ public class FileAppService : IFileAppService Console.WriteLine("添加数据到成功!提交事务!"); await _unitOfWork.CommitAsync(); - // 后续文档异步处理 - // ... - _ = Task.Run(async () => - { - try - { - await FileProcessingAsync(); - } - catch (Exception) - { - throw; - } - }); + // 文件处理任务入队 + await _fileProcessingQueue.EnqueueAsync(new FileProcessingJobDto( + fileEntity.Id.Value, + fileInfo.FullName, + file.ContentType, + file.Length), CancellationToken.None); return new FileUploadResultDto ( @@ -88,14 +83,6 @@ public class FileAppService : IFileAppService } } - private async Task FileProcessingAsync() - { - // 文本提取 - // 文本分片 - // 向量化 - // 向量入库 - } - public async Task> GetList() { var files = await _fileRepository.GetAllAsync(); diff --git a/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs b/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs index b05ab05..f5e30c3 100644 --- a/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs +++ b/backend/src/UniversalAdminSystem.Domian/FileStorage/Aggregates/File.cs @@ -1,7 +1,6 @@ using UniversalAdminSystem.Domian.Core; using UniversalAdminSystem.Domian.Core.ValueObjects; using UniversalAdminSystem.Domian.FileStorage.ValueObjects; -using UniversalAdminSystem.Domian.UserManagement.ValueObj; namespace UniversalAdminSystem.Domian.FileStorage.Aggregates; @@ -32,10 +31,7 @@ public class File : AggregateRoot FileAccessLevel accessLevel = FileAccessLevel.Private ) { - // if (!FileType.AllowedTypes.Contains(type.Value)) - // throw new ArgumentException("不支持的文件类型"); - if (size.Value > FileSize.MaxSize) - throw new ArgumentException($"文件大小不能超过{FileSize.MaxSize / 1024 / 1024}MB"); + if (size.Value > FileSize.MaxSize) throw new ArgumentException($"文件大小不能超过{FileSize.MaxSize / 1024 / 1024}MB"); return new File { Id = FileId.Create(), diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Configs/RabbitMqConfig.cs b/backend/src/UniversalAdminSystem.Infrastructure/Configs/RabbitMqConfig.cs new file mode 100644 index 0000000..edd279c --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Configs/RabbitMqConfig.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace UniversalAdminSystem.Infrastructure.Configs; + +public class RabbitMqConfig +{ + [property: JsonPropertyName("Host")] + public string Host { get; set; } = string.Empty; + + [property: JsonPropertyName("Port")] + public int Port { get; set; } + + [property: JsonPropertyName("Username")] + public string Username { get; set; } = string.Empty; + + [property: JsonPropertyName("Password")] + public string Password { get; set; } = string.Empty; + + [property: JsonPropertyName("Exchange")] + public string Exchange { get; set; } = string.Empty; + + [property: JsonPropertyName("Queue")] + public string Queue { get; set; } = string.Empty; + + [property: JsonPropertyName("RoutingKey")] + public string RoutingKey { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs index 0fdddc8..0d5aba2 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/DependencyInject/AddInfrastructureService.cs @@ -23,7 +23,13 @@ using FluentValidation; using UniversalAdminSystem.Domian.UserConversations.IRepository; using UniversalAdminSystem.Domian.knowledge.IRepository; using UniversalAdminSystem.Application.FileStorage.Interfaces; -using UniversalAdminSystem.Infrastructure.Configs; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Queues; +using UniversalAdminSystem.Infrastructure.RabbitMQ; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Publishers; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Consumers; +using Microsoft.AspNetCore.Connections; +using RabbitMQ.Client; namespace UniversalAdminSystem.Infrastructure.DependencyInject; @@ -110,7 +116,7 @@ public static class AddInfrastrutureService { // services.Configure(configuration.GetSection("AllowedFiles")); // 注册数据库上下文 - services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"),x=>x.UseVector())); + services.AddDbContext(x => x.UseNpgsql(configuration.GetConnectionString("pgSql"), x => x.UseVector())); // 注册配置 services.AddAllConfig(configuration); @@ -128,7 +134,7 @@ public static class AddInfrastrutureService typeof(ISystemSettingRepository).Assembly, typeof(IConversationsRepository).Assembly, typeof(IDocumentChunkRepository).Assembly, - + }, new Assembly[] { @@ -172,6 +178,36 @@ public static class AddInfrastrutureService services.AddHostedService(); services.AddSingleton(); + // 注册文件处理相关服务 + services.AddSingleton(); + services.AddSingleton(sp => + { + var factory = new ConnectionFactory + { + Uri = new Uri(sp.GetRequiredService().GetConnectionString("RabbitMq") ?? throw new InvalidOperationException("Missing RabbitMq connection string")), + DispatchConsumersAsync = true + }; + return factory.CreateConnection(); + }); + services.AddTransient(sp => + { + var conn = sp.GetRequiredService(); + var ch = conn.CreateModel(); + ch.ConfirmSelect(); + var exchange = configuration["RabbitMq:Exchange"] ?? "file-processing"; + var queue = configuration["RabbitMq:Queue"] ?? "file-processing-queue"; + var routingKey = configuration["RabbitMq:RoutingKey"] ?? "file-processing"; + + ch.ExchangeDeclare(exchange, "topic", durable: true, autoDelete: false); + ch.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false, arguments: null); + ch.QueueBind(queue, exchange, routingKey); + return ch; + }); + + services.AddSingleton(); + services.AddHostedService(); + services.AddHostedService(); + return services; } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/DocParserFactory.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/DocParserFactory.cs new file mode 100644 index 0000000..e35f464 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/DocParserFactory.cs @@ -0,0 +1,17 @@ +using UniversalAdminSystem.Application.FileStorage.Interfaces; + +namespace UniversalAdminSystem.Infrastructure.FileStorage.Parsers; + +public class DocParserFactory +{ + public IDocParser GetParser(string mimeType) + { + return mimeType.ToLower() switch + { + "application/pdf" => new PdfParseService(), + "text/markdown" => new MdParseService(), + "text/plain" => new TxtParseService(), + _ => throw new NotImplementedException($"没有为 MIME 类型 '{mimeType}' 实现解析器") + }; + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/MdParseService.cs similarity index 86% rename from backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs rename to backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/MdParseService.cs index a363362..89e9b89 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/MdParseService.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/MdParseService.cs @@ -4,17 +4,17 @@ using HtmlAgilityPack; using Markdig; using UniversalAdminSystem.Application.FileStorage.Interfaces; -namespace UniversalAdminSystem.Infrastructure.FileStorage; +namespace UniversalAdminSystem.Infrastructure.FileStorage.Parsers; public sealed partial class MdParseService : IDocParser { private static readonly Regex _blankLines = MyRegex(); - public string Parse(string path) + public async Task ParseAsync(string path) { if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("文件路径不能为空", nameof(path)); using var reader = new StreamReader(path); - var mdContent = reader.ReadToEnd(); + var mdContent = await reader.ReadToEndAsync(); return Convert(mdContent); } diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/PdfParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/PdfParseService.cs new file mode 100644 index 0000000..8875eff --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/PdfParseService.cs @@ -0,0 +1,30 @@ +using System.Text; +using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Canvas.Parser; +using UniversalAdminSystem.Application.FileStorage.Interfaces; + +namespace UniversalAdminSystem.Infrastructure.FileStorage.Parsers; + +public class PdfParseService : IDocParser +{ + public async Task ParseAsync(string path) + { + return await Task.Run(() => + { + var text = new StringBuilder(); + using (var pdfReader = new PdfReader(path)) + { + using var pdfDocument = new PdfDocument(pdfReader); + int numberOfPages = pdfDocument.GetNumberOfPages(); + for (int i = 1; i <= numberOfPages; i++) + { + var page = pdfDocument.GetPage(i); + var pageText = PdfTextExtractor.GetTextFromPage(page); + text.Append(pageText); + } + + } + return text.ToString(); + }); + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/TxtParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/TxtParseService.cs new file mode 100644 index 0000000..d240da3 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/Parsers/TxtParseService.cs @@ -0,0 +1,11 @@ +using UniversalAdminSystem.Application.FileStorage.Interfaces; + +namespace UniversalAdminSystem.Infrastructure.FileStorage.Parsers; + +public class TxtParseService : IDocParser +{ + public async Task ParseAsync(string path) + { + return await File.ReadAllTextAsync(path); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs b/backend/src/UniversalAdminSystem.Infrastructure/FileStorage/TxtParseService.cs deleted file mode 100644 index e69de29..0000000 diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs index 0173534..e6e001b 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs @@ -16,6 +16,7 @@ using UniversalAdminSystem.Domian.knowledge.ValueObj; using NpgsqlTypes; using Pgvector; using UniversalAdminSystem.Domian.UserConversations.Aggregates; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Jobs; namespace UniversalAdminSystem.Infrastructure.Persistence.DbContexts; @@ -30,17 +31,16 @@ public class UniversalAdminSystemDbContext : DbContext public DbSet LogEntries { get; set; } public DbSet SystemSettings { get; set; } public DbSet Files { get; set; } - public DbSet Conversations { get; set; } - public DbSet Messages { get; set; } - public DbSet Chunks { get; set; } + public DbSet FileProcessingJobs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 启用"vector"扩展 modelBuilder.HasPostgresExtension("vector"); + // 配置UserInfo实体 modelBuilder.Entity(entity => { @@ -289,6 +289,13 @@ public class UniversalAdminSystemDbContext : DbContext ); }); + modelBuilder.Entity(entity => + { + entity.HasKey(fpje => fpje.Id); + entity.HasIndex(fpje => new { fpje.Status, fpje.NextAttemptAt, fpje.CreatedAt }); + entity.Property(fpje => fpje.Status).HasMaxLength(32); + }); + // 忽略值对象 modelBuilder.Ignore(); } diff --git a/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Consumers/FileProcessingJobConsumer.cs b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Consumers/FileProcessingJobConsumer.cs new file mode 100644 index 0000000..e47d80a --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Consumers/FileProcessingJobConsumer.cs @@ -0,0 +1,77 @@ +using System.Text; +using System.Text.Json; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using UniversalAdminSystem.Infrastructure.FileStorage.Parsers; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Jobs; + +namespace UniversalAdminSystem.Infrastructure.RabbitMQ.Consumers; + +public class FileProcessingJobConsumer : BackgroundService +{ + private readonly IModel _ch; + private readonly ILogger _logger; + + public FileProcessingJobConsumer(IModel ch, ILogger logger) + { + _ch = ch; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + var consumer = new EventingBasicConsumer(_ch); + consumer.Received += async (sender, e) => + { + var body = e.Body.ToArray(); + var message = Encoding.UTF8.GetString(body); + _logger.LogInformation("收到文件处理任务: {Message}", message); + try + { + await ProcessMessageAsync(message, stoppingToken); + _ch.BasicAck(e.DeliveryTag, false); + _logger.LogInformation("文件处理任务处理成功: {Message}", message); + } + catch (Exception ex) + { + _ch.BasicNack(e.DeliveryTag, false, true); + _logger.LogError(ex, "文件处理任务处理失败: {Message}", message); + } + }; + _ch.BasicConsume(queue: "file-processing-queue", autoAck: false, consumer: consumer); + await Task.Delay(Timeout.Infinite, stoppingToken); + } + catch (Exception ex) { _logger.LogError(ex, "文件处理任务消费失败"); } + } + + private async Task ProcessMessageAsync(string message, CancellationToken stoppingToken) + { + var job = JsonSerializer.Deserialize(message); + if (job == null) + { + _logger.LogError("文件处理任务反序列化失败: {Message}", message); + return; + } + // 文本提取 + _logger.LogInformation("开始处理文件: {FilePath}", job.FilePath); + var parser = new DocParserFactory().GetParser(job.ContentType); + var text = await parser.ParseAsync(job.FilePath); + _logger.LogInformation("文件处理完成: {FilePath}", job.FilePath); + + // 文本分片 + _logger.LogInformation("开始分片文件: {FilePath}", job.FilePath); + + // Embedding + _logger.LogInformation("开始Embedding文件: {FilePath}", job.FilePath); + + // Vector Store + _logger.LogInformation("开始Vector Store文件: {FilePath}", job.FilePath); + + // 文件处理完成 + _logger.LogInformation("文件处理完成: {FilePath}", job.FilePath); + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Jobs/FileProcessingJob.cs b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Jobs/FileProcessingJob.cs new file mode 100644 index 0000000..c41aaf7 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Jobs/FileProcessingJob.cs @@ -0,0 +1,14 @@ +namespace UniversalAdminSystem.Infrastructure.RabbitMQ.Jobs; + +public class FileProcessingJob +{ + public Guid Id { get; set; } = Guid.NewGuid(); + public Guid FileId { get; set; } + public string FilePath { get; set; } = default!; + public string ContentType { get; set; } = default!; + public long Size { get; set; } + public string Status { get; set; } = "Pending"; // Pending|Processing|Succeeded|Failed + public int RetryCount { get; set; } = 0; + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime? NextAttemptAt { get; set; } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Publishers/FileProcessingJobPublisher.cs b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Publishers/FileProcessingJobPublisher.cs new file mode 100644 index 0000000..645c421 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Publishers/FileProcessingJobPublisher.cs @@ -0,0 +1,60 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using UniversalAdminSystem.Application.Common.Interfaces; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +namespace UniversalAdminSystem.Infrastructure.RabbitMQ.Publishers; + +public class FileProcessingJobPublisher : BackgroundService +{ + private readonly IServiceScopeFactory _sf; + private readonly IEventBus _bus; + private readonly ILogger _logger; + + public FileProcessingJobPublisher(IServiceScopeFactory sf, IEventBus bus, ILogger logger) + { _sf = sf; _bus = bus; _logger = logger; } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + using var scope = _sf.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + var jobs = await db.FileProcessingJobs + .Where(x => x.Status == "Pending" && (x.NextAttemptAt == null || x.NextAttemptAt <= DateTime.UtcNow)) + .OrderBy(x => x.CreatedAt) + .Take(100) + .ToListAsync(stoppingToken); + + foreach (var j in jobs) j.Status = "Processing"; + await db.SaveChangesAsync(stoppingToken); + + foreach (var j in jobs) + { + try + { + var payload = JsonSerializer.Serialize(new { j.Id, j.FileId, j.FilePath, j.ContentType, j.Size }); + await _bus.PublishAsync("file-processing", "file-processing", payload, messageId: j.Id.ToString(), stoppingToken); + j.Status = "Succeeded"; + } + catch (Exception ex) + { + j.RetryCount += 1; + j.Status = "Pending"; + j.NextAttemptAt = DateTime.UtcNow.AddSeconds(Math.Min(300, Math.Pow(2, j.RetryCount))); + _logger.LogError(ex, "发布失败: {JobId}", j.Id); + } + } + await db.SaveChangesAsync(stoppingToken); + } + catch (Exception ex) { _logger.LogError(ex, "文件处理任务发布循环错误"); } + + await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken); + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Queues/EfFileProcessingQueue.cs b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Queues/EfFileProcessingQueue.cs new file mode 100644 index 0000000..2a0bbf0 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/Queues/EfFileProcessingQueue.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Logging; +using UniversalAdminSystem.Application.FileStorage.DTOs; +using UniversalAdminSystem.Application.FileStorage.Interfaces; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; +using UniversalAdminSystem.Infrastructure.RabbitMQ.Jobs; + +namespace UniversalAdminSystem.Infrastructure.RabbitMQ.Queues; + +public class EfFileProcessingQueue : IFileProcessingQueue +{ + private readonly UniversalAdminSystemDbContext _dbContext; + private readonly Logger _logger; + + public EfFileProcessingQueue(UniversalAdminSystemDbContext dbContext, Logger logger) + { + _dbContext = dbContext; + _logger = logger; + } + + public async Task EnqueueAsync(FileProcessingJobDto jd, CancellationToken ct = default) + { + try + { + _dbContext.FileProcessingJobs.Add(new FileProcessingJob + { + FileId = jd.FileId, + FilePath = jd.FilePath, + ContentType = jd.ContentType, + Size = jd.Size + }); + await _dbContext.SaveChangesAsync(ct); + _logger.LogInformation("文件处理任务已入队: {FileId}", jd.FileId); + } + catch (Exception ex) + { + _logger.LogError(ex, "文件处理任务入队失败: {FileId}", jd.FileId); + throw; + } + } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/RabbitMqEventBus.cs b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/RabbitMqEventBus.cs new file mode 100644 index 0000000..2065305 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/RabbitMQ/RabbitMqEventBus.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Configuration; +using UniversalAdminSystem.Application.Common.Interfaces; +using System.Text; +using RabbitMQ.Client; + +namespace UniversalAdminSystem.Infrastructure.RabbitMQ; + +public class RabbitMqEventBus : IEventBus, IDisposable +{ + private readonly IModel _ch; + + public RabbitMqEventBus(IModel ch, IConfiguration cfg) + { + _ch = ch; + _ch.ConfirmSelect(); + } + + public Task PublishAsync(string exchange, string routingKey, string payload, string? messageId = null, CancellationToken ct = default) + { + var props = _ch.CreateBasicProperties(); + props.Persistent = true; + props.MessageId = messageId; + props.ContentType = "application/json"; + _ch.BasicPublish(exchange, routingKey, mandatory: true, basicProperties: props, body: Encoding.UTF8.GetBytes(payload)); + _ch.WaitForConfirmsOrDie(TimeSpan.FromSeconds(5)); + return Task.CompletedTask; + } + + public void Dispose() { _ch?.Dispose(); } +} \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj index 3302db0..9a5269a 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj +++ b/backend/src/UniversalAdminSystem.Infrastructure/UniversalAdminSystem.Infrastructure.csproj @@ -8,10 +8,13 @@ + + + -- Gitee From b79d34603e5cec859b53ee504c6c6bf883b026bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=B4=A4=E5=9B=BD?= <3482108437@qq.com> Date: Mon, 11 Aug 2025 16:17:11 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E7=99=BB=E5=BD=95/=E6=B3=A8=E5=86=8C/ai?= =?UTF-8?q?=E8=81=8A=E5=A4=A9/=E5=8E=86=E5=8F=B2=E5=AF=B9=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages.json | 7 + frontend/pages/chat/chat.vue | 211 +++++++++++++++++++++++---- frontend/pages/document/document.vue | 138 ++++++++++++++++++ frontend/pages/register/register.vue | 3 +- frontend/txt.txt | 0 5 files changed, 329 insertions(+), 30 deletions(-) create mode 100644 frontend/pages/document/document.vue delete mode 100644 frontend/txt.txt diff --git a/frontend/pages.json b/frontend/pages.json index 03cf3d3..b79a08f 100644 --- a/frontend/pages.json +++ b/frontend/pages.json @@ -20,6 +20,13 @@ { "navigationBarTitleText" : "" } + }, + { + "path" : "pages/document/document", + "style" : + { + "navigationBarTitleText" : "" + } } ], "globalStyle": { diff --git a/frontend/pages/chat/chat.vue b/frontend/pages/chat/chat.vue index a660afb..266defa 100644 --- a/frontend/pages/chat/chat.vue +++ b/frontend/pages/chat/chat.vue @@ -1,15 +1,30 @@ @@ -149,10 +170,54 @@ const handleLogin = () => { color: #b0c4fa; } -.title { - font-size: 32px; +/* 登录失败弹窗样式 */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal { + width: 80%; + max-width: 300px; + background: #fff; + border-radius: 10px; + padding: 20px; + text-align: center; +} + +.modal-title { + font-size: 18px; font-weight: bold; - color: #a020f0; /* 调整为图中相似的紫色 */ - margin-bottom: 30px; + margin-bottom: 10px; +} + +.modal-content { + font-size: 14px; + color: #333; + margin-bottom: 20px; +} + +.modal-btn { + width: 100%; + height: 40px; + background: linear-gradient(135deg, #e6e6fa, #b0c4fa); + color: #fff; + border: none; + border-radius: 20px; + font-size: 16px; + cursor: pointer; + transition: background 0.3s; +} + +.modal-btn:hover { + background: linear-gradient(135deg, #b0c4fa, #e6e6fa); } \ No newline at end of file diff --git a/frontend/pages/register/register.vue b/frontend/pages/register/register.vue index d3b9dd0..c5d5b61 100644 --- a/frontend/pages/register/register.vue +++ b/frontend/pages/register/register.vue @@ -9,22 +9,22 @@ 用户名 - + 邮箱 - + 密码 - + 确认密码 - + @@ -40,14 +40,36 @@ \ No newline at end of file diff --git a/frontend/pages/register/register.vue b/frontend/pages/register/register.vue index d3b9dd0..7b4ce64 100644 --- a/frontend/pages/register/register.vue +++ b/frontend/pages/register/register.vue @@ -9,22 +9,22 @@ 用户名 - + 邮箱 - + 密码 - + 确认密码 - + @@ -39,11 +39,33 @@ diff --git a/frontend/stores/User.js b/frontend/stores/User.js new file mode 100644 index 0000000..57f9e3e --- /dev/null +++ b/frontend/stores/User.js @@ -0,0 +1,24 @@ +import {defineStore} from "pinia" +import { ref } from "vue" + +export const useUserStore = defineStore("User",()=>{ + const token = ref(uni.getStorageSync("token") || "") + const userId = ref('') + + const setToken = (newToken) => { + token.value = newToken + uni.setStorageSync('token', newToken) + } + + + const setUserId = (newUserId) => { + userId.value = newUserId + } + + const clearToken = () => { + token.value = '' + uni.removeStorageSync('token') + } + + return {setToken,setUserId,clearToken} +}) \ No newline at end of file diff --git a/frontend/vue.config.js b/frontend/vue.config.js new file mode 100644 index 0000000..5e00501 --- /dev/null +++ b/frontend/vue.config.js @@ -0,0 +1,13 @@ +module.exports = { + devServer: { + proxy: { + '/api': { // 代理所有以/api开头的请求 + target: 'http://localhost:5101', // 后端服务器地址 + changeOrigin: true, + pathRewrite: { + '^/api': '/api' // 将路径中的/api保留 + } + } + } + } +} \ No newline at end of file -- Gitee From a6990038c444e1a2c00f9b3438725dd967f05893 Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Tue, 12 Aug 2025 17:05:26 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=B6=88=E6=81=AFapi=E7=AB=AF=E7=82=B9?= =?UTF-8?q?=EF=BC=8C=E5=9C=A8=E6=95=B0=E6=8D=AE=E5=BA=93=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E4=B8=AD=E4=B8=BA=E5=90=91=E9=87=8F=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/AIQuestionsController.cs | 16 +- .../20250812085226_last.Designer.cs | 394 ++++++++++++++++++ .../Migrations/20250812085226_last.cs | 27 ++ ...versalAdminSystemDbContextModelSnapshot.cs | 2 + .../UniversalAdminSystemDbContext.cs | 1 + 5 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.Designer.cs create mode 100644 backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.cs diff --git a/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs index 913abe9..107d2ec 100644 --- a/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs +++ b/backend/src/UniversalAdminSystem.Api/Controllers/AIQuestionsController.cs @@ -40,7 +40,7 @@ public class AIQuestionsController : ControllerBase [HttpPost("user/{id}")] [RequirePermission("document:Read")] - public Task UserAccessById(Guid id,ContentDto content) + public Task UserAccessById(Guid id, ContentDto content) { throw new NotImplementedException();//尚未实现 } @@ -73,4 +73,18 @@ public class AIQuestionsController : ControllerBase return BadRequest(Result.Failure(e.Message)); } } + + [HttpGet("user/message/{ConversationsId}")] + public async Task GetAllConversationMessage(Guid ConversationsId) + { + try + { + var list = await _AIQusetionAppService.GetConversationMessage(ConversationsId); + return Ok(Result>.Success(list)); + } + catch (System.Exception e) + { + return BadRequest(Result.Failure(e.Message)); + } + } } \ No newline at end of file diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.Designer.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.Designer.cs new file mode 100644 index 0000000..5334287 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.Designer.cs @@ -0,0 +1,394 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Pgvector; +using UniversalAdminSystem.Infrastructure.Persistence.DbContexts; + +#nullable disable + +namespace UniversalAdminSystem.Infrastructure.Migrations +{ + [DbContext(typeof(UniversalAdminSystemDbContext))] + [Migration("20250812085226_last")] + partial class last + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("RolePermissions", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("PermissionId") + .HasColumnType("uuid"); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId"); + + b.ToTable("RolePermissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.FileStorage.Aggregates.File", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessLevel") + .HasColumnType("integer"); + + b.Property("IsFolder") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityCheckResult") + .HasColumnType("text"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UploadTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.LogManagement.Aggregates.LogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Context") + .HasColumnType("text"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("Level") + .IsRequired() + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Source") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("LogEntries"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", b => + { + b.Property("PermissionId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PermissionType") + .HasColumnType("integer"); + + b.Property("Resource") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("PermissionId"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", b => + { + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsSupper") + .HasColumnType("boolean"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RoleId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.SystemSettings.Aggregates.SystemSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Group") + .HasColumnType("text"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SystemSettings"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Conversations", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("UpdateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Conversations"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserConversations.Aggregates.Message", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Account") + .IsRequired() + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.HasKey("UserId"); + + b.HasIndex("Account") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Entities.UserInfo", b => + { + b.Property("UserInfoId") + .HasColumnType("uuid"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.HasKey("UserInfoId"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.knowledge.Aggregates.DocumentChunk", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("Embedding") + .IsRequired() + .HasColumnType("vector(1536)"); + + b.Property("FileId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Embedding"); + + b.ToTable("Chunks"); + }); + + modelBuilder.Entity("RolePermissions", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Permission", null) + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UniversalAdminSystem.Domian.UserManagement.Aggregates.User", b => + { + b.HasOne("UniversalAdminSystem.Domian.PermissionManagement.Aggregate.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.SetNull); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.cs new file mode 100644 index 0000000..d47c9b5 --- /dev/null +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/20250812085226_last.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UniversalAdminSystem.Infrastructure.Migrations +{ + /// + public partial class last : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_Chunks_Embedding", + table: "Chunks", + column: "Embedding"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Chunks_Embedding", + table: "Chunks"); + } + } +} diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs index bde02a3..25bc4ff 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Migrations/UniversalAdminSystemDbContextModelSnapshot.cs @@ -358,6 +358,8 @@ namespace UniversalAdminSystem.Infrastructure.Migrations b.HasKey("Id"); + b.HasIndex("Embedding"); + b.ToTable("Chunks"); }); diff --git a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs index 0173534..c5b88ec 100644 --- a/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs +++ b/backend/src/UniversalAdminSystem.Infrastructure/Persistence/DbContexts/UniversalAdminSystemDbContext.cs @@ -238,6 +238,7 @@ public class UniversalAdminSystemDbContext : DbContext modelBuilder.Entity(entity => { entity.HasKey(d => d.Id); + entity.HasIndex(d => d.Embedding); entity.Property(d => d.FileId) .HasConversion( -- Gitee From 2ee765db458d3bcb47641ac55aa1a5e47e3a3f88 Mon Sep 17 00:00:00 2001 From: xiaoyaolanren2 <18033910316@163.com> Date: Tue, 12 Aug 2025 17:17:46 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E9=80=BB=E8=BE=91=E5=8F=8A=E9=99=A4=E4=BA=86?= =?UTF-8?q?=E6=B5=81=E5=BC=8F=E4=BC=A0=E8=BE=93=E4=BB=A5=E5=A4=96=E7=9A=84?= =?UTF-8?q?api=E5=AF=B9=E6=8E=A5=EF=BC=88=E9=83=A8=E5=88=86=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=8A=9F=E8=83=BD=E5=B0=9A=E6=9C=AA=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/api/chat.js | 74 +- frontend/api/config/index.js | 36 +- frontend/pages/chat/chat.vue | 1047 +++++++++++++++++--------- frontend/pages/login/login.vue | 24 +- frontend/pages/register/register.vue | 13 +- frontend/stores/User.js | 9 +- 6 files changed, 793 insertions(+), 410 deletions(-) diff --git a/frontend/api/chat.js b/frontend/api/chat.js index df44683..36e7f8a 100644 --- a/frontend/api/chat.js +++ b/frontend/api/chat.js @@ -1,16 +1,72 @@ -import { useRequest } from "./config"; -const {request} = useRequest() +import { + useRequest +} from "./config"; -const createChat = async (chatDto)=>{ +const { + request +} = useRequest() + +export function useChatApi() { + + const createChat = async (chatDto) => { + + + var res =await request({ + url: '/AIQuestions/user', + method: 'POST', + data: chatDto, + showLoading: true + }) + + return res + } + + const getChat = async (id) => { + + var res =await request({ + url: `/AIQuestions/user/${id}`, + method: 'GET', + data: null, + showLoading: true + }) + + return res + } + + const getAllMessage = async (id)=> { + var res =await request({ + url: `/AIQuestions/user/message/${id}`, + method: 'GET', + data: null, + showLoading: true + }) + + return res + } + const deleteChat = async (id) => { + var res = await request({ + url:`/AIQuestions/user/${id}`, + method:'DELETE', + data:null, + showLoading:true + }) - var res = request({ - url: '/AIQuestions/user', - method:'POST', - data:chatDto, - showLoading:true - }) + return res + } + + const conversation = async (id) => { + var res = await request({ + url:`/AIQuestions/user/${id}`, + method:'POST', + data:null, + showLoading:true + }) + + return res + } + return {conversation,createChat,deleteChat,getAllMessage,getChat} } \ No newline at end of file diff --git a/frontend/api/config/index.js b/frontend/api/config/index.js index 02d64c4..729354d 100644 --- a/frontend/api/config/index.js +++ b/frontend/api/config/index.js @@ -70,7 +70,7 @@ export function useRequest() { return response.data } catch (error) { // 统一错误处理 - handleRequestError(error, mergedConfig) + // handleRequestError(error, mergedConfig) throw error } finally { if (mergedConfig.showLoading) { @@ -105,24 +105,24 @@ function handleUnauthorized() { // #endif } -// 错误处理 -function handleRequestError(error, config) { - console.error('请求错误:', error) +// // 错误处理 +// function handleRequestError(error, config) { +// console.error('请求错误:', error) - // 不显示静默请求的错误 - if (config.silent) return +// // 不显示静默请求的错误 +// if (config.silent) return - let message = '网络请求失败' +// let message = '网络请求失败' - if (error.errMsg.includes('timeout')) { - message = '请求超时,请检查网络' - } else if (error.errMsg.includes('network')) { - message = '网络不可用,请检查连接' - } +// if (error.errMsg.includes('timeout')) { +// message = '请求超时,请检查网络' +// } else if (error.errMsg.includes('network')) { +// message = '网络不可用,请检查连接' +// } - uni.showToast({ - title: message, - icon: 'none', - duration: 3000 - }) -} \ No newline at end of file +// uni.showToast({ +// title: message, +// icon: 'none', +// duration: 3000 +// }) +// } \ No newline at end of file diff --git a/frontend/pages/chat/chat.vue b/frontend/pages/chat/chat.vue index 266defa..e9d4600 100644 --- a/frontend/pages/chat/chat.vue +++ b/frontend/pages/chat/chat.vue @@ -1,378 +1,685 @@ \ No newline at end of file diff --git a/frontend/pages/login/login.vue b/frontend/pages/login/login.vue index 89db9a4..0686c71 100644 --- a/frontend/pages/login/login.vue +++ b/frontend/pages/login/login.vue @@ -29,14 +29,14 @@ - - + \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/auth.js b/frontend/vite-frontend/src/api/auth.js new file mode 100644 index 0000000..834f328 --- /dev/null +++ b/frontend/vite-frontend/src/api/auth.js @@ -0,0 +1,34 @@ +import api from './config' + +// 认证相关API - 基于后端AuthenticationController +export const authApi = { + // 用户登录 + login: (credentials) => { + const requestData = { + account: credentials.account || credentials.username, + password: credentials.password + } + return api.post('/auth/login', requestData) + }, + + // 用户注册 + register: (userData) => { + return api.post('/auth/register', { + account: userData.account || userData.username, + password: userData.password, + email: userData.email + }) + }, + + // 刷新token + refreshToken: (token) => { + return api.post('/auth/refresh-token', { + token: token + }) + }, + + // 用户登出 + logout: () => { + return api.post('/auth/logout') + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/config.js b/frontend/vite-frontend/src/api/config.js new file mode 100644 index 0000000..4dc2652 --- /dev/null +++ b/frontend/vite-frontend/src/api/config.js @@ -0,0 +1,186 @@ +import axios from 'axios' +import { ElMessage } from 'element-plus' +// 使用当前域名和端口,如果没有则使用默认值 +const protocol = window.location.protocol; +const hostname = window.location.hostname; +const port = window.location.port=='5173'?'5256':window.location.port || (protocol === 'https:' ? '443' : '80'); + +// 创建axios实例 +const api = axios.create({ + // baseURL: `${protocol}//${hostname}:${port}/api`, // 后端API基础URL + baseURL: `http://localhost:5101/api`, // 后端API基础URL + timeout: 10000, // 请求超时时间 + headers: { + 'Content-Type': 'application/json' + } +}) + +// 不需要token的API路径 +const publicApis = [ + '/auth/login', + '/auth/register', + '/auth/refresh-token' +] + +// 请求拦截器 +api.interceptors.request.use( + (config) => { + console.log('发送请求:', config.method?.toUpperCase(), config.url, config.data) + + // 检查是否是公开API(不需要token) + const isPublicApi = publicApis.some(api => config.url?.includes(api)) + + if (isPublicApi) { + console.log('公开API,不需要token:', config.url) + return config + } + + // 从localStorage获取token + const token = localStorage.getItem('token') + console.log('当前token:', token ? '存在' : '不存在') + + if (token) { + config.headers.Authorization = `Bearer ${token}` + console.log('设置Authorization头:', `Bearer ${token.substring(0, 20)}...`) + } else { + console.log('未找到token,请求将不包含Authorization头') + } + + return config + }, + (error) => { + console.error('请求错误:', error) + return Promise.reject(error) + } +) + +// 响应拦截器 +api.interceptors.response.use( + (response) => { + console.log('收到响应:', response.status, response.data) + + // 对于文件下载,直接返回响应 + if (response.config.responseType === 'blob') { + console.log('文件下载响应,直接返回') + return response + } + + // 如果响应成功,检查后端统一响应格式 + const data = response.data + console.log('响应数据格式检查:', { + isObject: typeof data === 'object', + hasIsSuccess: data && 'IsSuccess' in data, + isSuccess: data?.IsSuccess, + message: data?.Message + }) + + if (data && typeof data === 'object' && ('IsSuccess' in data || 'Success' in data)) { + // 后端使用统一响应格式 + const isSuccess = data.IsSuccess !== undefined ? data.IsSuccess : data.Success + if (isSuccess) { + console.log('后端返回成功,数据:', data.Data) + return data.Data || data + } else { + // 如果后端返回失败,抛出错误 + console.log('后端返回失败:', data.Message) + const error = new Error(data.Message || '请求失败') + error.response = { data: data } + throw error + } + } + // 如果不是统一格式,直接返回数据 + console.log('非统一格式响应,直接返回数据') + return data + }, + (error) => { + console.error('响应错误:', error.response) + // 处理错误响应 + if (error.response) { + const { status, data } = error.response + + switch (status) { + case 400: + ElMessage.error(data?.message || '请求参数错误') + break + case 401: + console.log('收到401错误,当前路径:', window.location.pathname) + console.log('401错误详情:', { + url: error.config?.url, + method: error.config?.method, + headers: error.config?.headers, + responseData: error.response?.data + }) + + // 分析401错误的原因 + const responseData = error.response?.data + if (responseData && typeof responseData === 'object' && 'Message' in responseData) { + console.log('后端返回的401错误信息:', responseData.Message) + } + + // 检查是否是用户管理相关的API调用 + const isUserManagementApi = error.config?.url?.includes('/user/'); + const isAuthApi = error.config?.url?.includes('/auth/'); + + if (isUserManagementApi) { + // 对于用户管理API的401错误,可能是权限不足或用户状态问题 + console.log('用户管理API 401错误,可能是权限问题或用户状态问题'); + ElMessage.error('操作失败:权限不足或账户状态异常'); + return Promise.reject(error); + } + + if (isAuthApi) { + // 对于认证API的401错误,可能是登录失败 + console.log('认证API 401错误,登录失败'); + ElMessage.error('登录失败:用户名或密码错误'); + return Promise.reject(error); + } + + // 其他API的401错误,可能是token过期或无效 + console.log('其他API 401错误,可能是token过期'); + ElMessage.error('登录已过期,请重新登录'); + localStorage.removeItem('token') + localStorage.removeItem('userInfo') + + // 只有在非登录页面才跳转,避免在登录页面时重复跳转 + if (window.location.pathname !== '/login' && window.location.pathname !== '/register') { + console.log('跳转到登录页面') + window.location.href = '/login' + } else { + console.log('当前已在登录页面,不进行跳转') + } + break + case 403: + ElMessage.error('权限不足,无法执行此操作') + break + case 404: + ElMessage.error('请求的资源不存在') + break + case 409: + ElMessage.error('数据冲突:' + (data?.message || '资源已存在')) + break + case 422: + ElMessage.error('数据验证失败:' + (data?.message || '输入数据格式不正确')) + break + case 500: + ElMessage.error('服务器内部错误,请稍后重试') + break + case 502: + ElMessage.error('网关错误,请稍后重试') + break + case 503: + ElMessage.error('服务暂时不可用,请稍后重试') + break + default: + ElMessage.error(data?.message || `请求失败 (${status})`) + } + } else if (error.request) { + ElMessage.error('网络连接失败') + } else { + ElMessage.error('请求配置错误') + } + + return Promise.reject(error) + } +) + +export default api \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/example.js b/frontend/vite-frontend/src/api/example.js new file mode 100644 index 0000000..9efdedc --- /dev/null +++ b/frontend/vite-frontend/src/api/example.js @@ -0,0 +1,289 @@ +// API使用示例 +import { + authApi, + userApi, + roleApi, + permissionApi, + menuApi, + fileApi, + logApi, + systemApi +} from './index' + +// 认证相关示例 +export const authExamples = { + // 用户登录 + async loginExample() { + try { + const credentials = { + username: 'admin', + password: '123456' + } + const response = await authApi.login(credentials) + console.log('登录成功:', response) + return response + } catch (error) { + console.error('登录失败:', error) + throw error + } + }, + + // 获取当前用户信息 + async getCurrentUserExample() { + try { + const response = await authApi.getCurrentUser() + console.log('当前用户信息:', response) + return response + } catch (error) { + console.error('获取用户信息失败:', error) + throw error + } + } +} + +// 用户管理示例 +export const userExamples = { + // 获取用户列表 + async getUsersExample() { + try { + const params = { + page: 1, + pageSize: 10, + keyword: '', + status: 'active' + } + const response = await userApi.getUsers(params) + console.log('用户列表:', response) + return response + } catch (error) { + console.error('获取用户列表失败:', error) + throw error + } + }, + + // 创建用户 + async createUserExample() { + try { + const userData = { + username: 'newuser', + email: 'newuser@example.com', + password: '123456', + roleIds: [1, 2] + } + const response = await userApi.createUser(userData) + console.log('创建用户成功:', response) + return response + } catch (error) { + console.error('创建用户失败:', error) + throw error + } + }, + + // 更新用户 + async updateUserExample(userId) { + try { + const userData = { + email: 'updated@example.com', + status: 'active' + } + const response = await userApi.updateUser(userId, userData) + console.log('更新用户成功:', response) + return response + } catch (error) { + console.error('更新用户失败:', error) + throw error + } + } +} + +// 角色管理示例 +export const roleExamples = { + // 获取角色列表 + async getRolesExample() { + try { + const response = await roleApi.getRoles() + console.log('角色列表:', response) + return response + } catch (error) { + console.error('获取角色列表失败:', error) + throw error + } + }, + + // 分配角色权限 + async assignRolePermissionsExample(roleId) { + try { + const permissionIds = [1, 2, 3, 4] + const response = await roleApi.assignRolePermissions(roleId, permissionIds) + console.log('分配权限成功:', response) + return response + } catch (error) { + console.error('分配权限失败:', error) + throw error + } + } +} + +// 权限管理示例 +export const permissionExamples = { + // 获取权限树 + async getPermissionTreeExample() { + try { + const response = await permissionApi.getPermissionTree() + console.log('权限树:', response) + return response + } catch (error) { + console.error('获取权限树失败:', error) + throw error + } + }, + + // 检查权限 + async checkPermissionExample() { + try { + const permissionCode = 'user:create' + const response = await permissionApi.checkPermission(permissionCode) + console.log('权限检查结果:', response) + return response + } catch (error) { + console.error('权限检查失败:', error) + throw error + } + } +} + +// 菜单管理示例 +export const menuExamples = { + // 获取用户菜单 + async getUserMenusExample() { + try { + const response = await menuApi.getUserMenus() + console.log('用户菜单:', response) + return response + } catch (error) { + console.error('获取用户菜单失败:', error) + throw error + } + } +} + +// 文件管理示例 +export const fileExamples = { + // 上传文件 + async uploadFileExample(file) { + try { + const onProgress = (progressEvent) => { + const percentCompleted = Math.round( + (progressEvent.loaded * 100) / progressEvent.total + ) + console.log('上传进度:', percentCompleted + '%') + } + + const response = await fileApi.uploadFile(file, onProgress) + console.log('文件上传成功:', response) + return response + } catch (error) { + console.error('文件上传失败:', error) + throw error + } + }, + + // 获取文件列表 + async getFilesExample() { + try { + const params = { + page: 1, + pageSize: 20, + keyword: '', + fileType: 'all' + } + const response = await fileApi.getFiles(params) + console.log('文件列表:', response) + return response + } catch (error) { + console.error('获取文件列表失败:', error) + throw error + } + } +} + +// 日志管理示例 +export const logExamples = { + // 获取日志列表 + async getLogsExample() { + try { + const params = { + page: 1, + pageSize: 50, + startDate: '2024-01-01', + endDate: '2024-12-31', + level: 'info', + operationType: 'create' + } + const response = await logApi.getLogs(params) + console.log('日志列表:', response) + return response + } catch (error) { + console.error('获取日志列表失败:', error) + throw error + } + }, + + // 导出日志 + async exportLogsExample() { + try { + const params = { + startDate: '2024-01-01', + endDate: '2024-12-31', + format: 'excel' + } + const response = await logApi.exportLogs(params) + + // 创建下载链接 + const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = 'logs.xlsx' + link.click() + window.URL.revokeObjectURL(url) + + console.log('日志导出成功') + } catch (error) { + console.error('日志导出失败:', error) + throw error + } + } +} + +// 系统设置示例 +export const systemExamples = { + // 获取系统信息 + async getSystemInfoExample() { + try { + const response = await systemApi.getSystemInfo() + console.log('系统信息:', response) + return response + } catch (error) { + console.error('获取系统信息失败:', error) + throw error + } + }, + + // 更新系统设置 + async updateSystemSettingsExample() { + try { + const settings = [ + { key: 'site_name', value: '通用管理系统' }, + { key: 'site_description', value: '一个功能强大的管理系统' }, + { key: 'max_file_size', value: '10MB' } + ] + const response = await systemApi.batchUpdateSettings(settings) + console.log('系统设置更新成功:', response) + return response + } catch (error) { + console.error('系统设置更新失败:', error) + throw error + } + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/file.js b/frontend/vite-frontend/src/api/file.js new file mode 100644 index 0000000..40022de --- /dev/null +++ b/frontend/vite-frontend/src/api/file.js @@ -0,0 +1,41 @@ +import api from './config' + +// 文件管理相关API - 基于后端FileController +export const fileApi = { + // 上传文件 + uploadFile: (file, parentId = null) => { + const formData = new FormData() + formData.append('file', file) + if (parentId) { + formData.append('parentId', parentId) + } + + return api.post('/files/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + }, + + // 获取文件列表 + getFiles: () => { + return api.get('/files/list') + }, + + // 下载文件 + downloadFile: (fileId) => { + return api.get(`/files/${fileId}`, { + responseType: 'blob' + }) + }, + + // 删除文件 + deleteFile: (fileId) => { + return api.delete(`/files/${fileId}`) + }, + + // 测试权限 + testPermission: () => { + return api.get('/files/test-permission') + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/index.js b/frontend/vite-frontend/src/api/index.js new file mode 100644 index 0000000..acff7ac --- /dev/null +++ b/frontend/vite-frontend/src/api/index.js @@ -0,0 +1,11 @@ +// 统一导出所有API模块 +export { authApi } from './auth' +export { userApi } from './user' +export { roleApi } from './role' +export { permissionApi } from './permission' +export { fileApi } from './file' +export { logApi } from './log' +// export { systemApi } from './system' + +// 导出API配置 +export { default as api } from './config' \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/log.js b/frontend/vite-frontend/src/api/log.js new file mode 100644 index 0000000..74b29d3 --- /dev/null +++ b/frontend/vite-frontend/src/api/log.js @@ -0,0 +1,41 @@ +import api from './config' + +// 日志管理相关API - 基于后端LogController +export const logApi = { + // 获取所有日志 + getLogs: () => { + return api.get('/logs') + }, + + // 获取单个日志 + getLog: (id) => { + return api.get(`/logs/${id}`) + }, + + // 创建日志 + createLog: (logData) => { + return api.post('/logs', logData) + }, + + // 根据级别获取日志 + getLogsByLevel: (level) => { + return api.get(`/logs/level/${level}`) + }, + + // 根据用户获取日志 + getLogsByUser: (userId) => { + return api.get(`/logs/user/${userId}`) + }, + + // 根据日期范围获取日志 + getLogsByDateRange: (start, end) => { + return api.get('/logs/date', { + params: { start, end } + }) + }, + + // 根据来源获取日志 + getLogsBySource: (source) => { + return api.get(`/logs/source/${source}`) + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/permission.js b/frontend/vite-frontend/src/api/permission.js new file mode 100644 index 0000000..174c18f --- /dev/null +++ b/frontend/vite-frontend/src/api/permission.js @@ -0,0 +1,33 @@ +import api from './config' + +export const permissionApi = { + // 获取所有权限列表 + getPermissions: () => { + return api.get('/permissions') + }, + + // 创建新权限 + createPermission: (permissionData) => { + return api.post('/permissions/create', permissionData) + }, + + // 删除权限 + deletePermission: (permissionId) => { + return api.delete(`/permissions/${permissionId}`) + }, + + // 获取权限规则配置 + getPermissionRules: () => { + return api.get('/permissions/rules') + }, + + // 更新权限规则配置 + updatePermissionRules: (rules) => { + return api.put('/permissions/rules', rules) + }, + + // 刷新权限规则配置 + refreshPermissionRules: () => { + return api.post('/permissions/rules/refresh') + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/role.js b/frontend/vite-frontend/src/api/role.js new file mode 100644 index 0000000..860d8c3 --- /dev/null +++ b/frontend/vite-frontend/src/api/role.js @@ -0,0 +1,49 @@ +import api from './config' + +// 角色管理相关API - 基于后端RoleController +export const roleApi = { + // 获取角色列表 + getRoles: () => { + return api.get('/roles') + }, + + // 获取单个角色 + getRole: (id) => { + return api.get(`/roles/${id}`) + }, + + // 创建角色 + createRole: (roleData) => { + return api.post('/roles', roleData) + }, + + // 更新角色 + updateRole: (id, roleData) => { + return api.put(`/roles/${id}`, roleData) + }, + + // 删除角色 + deleteRole: (id) => { + return api.delete(`/roles/${id}`) + }, + + // 为角色分配权限(使用权限编码) + assignPermissionsToRole: (id, permissionCodes) => { + return api.post(`/roles/${id}/permissions`, permissionCodes) + }, + + // 为角色分配权限(使用权限ID) + assignPermissionsToRoleByIds: (id, permissionIds) => { + return api.post(`/roles/${id}/permissions/ids`, permissionIds) + }, + + // 移除角色权限 + removePermissionsFromRole: (id, permissionIds) => { + return api.delete(`/roles/${id}/permissions`, { data: permissionIds }) + }, + + // 获取角色权限 + getRolePermissions: (id) => { + return api.get(`/roles/${id}/permissions`) + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/api/user.js b/frontend/vite-frontend/src/api/user.js new file mode 100644 index 0000000..301cf02 --- /dev/null +++ b/frontend/vite-frontend/src/api/user.js @@ -0,0 +1,54 @@ +import api from './config' + +// 用户管理相关API - 基于后端UserManagementController +export const userApi = { + // 获取用户列表 + getUsers: () => { + return api.get('/user') + }, + + // 创建用户 + createUser: (userData) => { + return api.post('/user/create', userData) + }, + + // 获取单个用户 + getUser: (id) => { + return api.get(`/user/${id}`) + }, + + // 更新用户 + updateUser: (id, userData) => { + return api.put(`/user/${id}`, userData) + }, + + // 删除用户 + deleteUser: (id) => { + return api.delete(`/user/${id}`) + }, + + // 恢复用户(软删除恢复) + restoreUser: (id) => { + return api.post(`/user/${id}/restore`) + }, + + // 分配用户角色 + assignUserRoles: (id, roleIds) => { + return api.post(`/user/${id}/role`, roleIds) + }, + + // 移除用户角色 + removeUserRole: (id, roleId) => { + return api.delete(`/user/${id}/roles/${roleId}`) + }, + + // 获取用户权限 + getUserPermissions: (id) => { + return api.get(`/user/${id}/permissions`) + }, + + // 检查用户权限 + checkUserPermission: (id, permissionCode) => { + return api.post(`/user/${id}/permissions/check`, { permissionCode }) + } +} \ No newline at end of file diff --git a/frontend/vite-frontend/src/assets/image/bd26f78c344b3ad6afef7b12b1421227.jpg b/frontend/vite-frontend/src/assets/image/bd26f78c344b3ad6afef7b12b1421227.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f4ae368623fee0b542355016ba92a8a6392070bb GIT binary patch literal 618701 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�uW<+98&9- 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

6btSpOO3I(?0i3!P%>?gaa`uB%|p1X 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