using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sozsoft.Platform.DynamicServices; using Sozsoft.Platform.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; using static Sozsoft.Platform.PlatformConsts; namespace Sozsoft.Platform.DeveloperKit; [Authorize] public class DynamicAppServiceAppService : PlatformAppService, IDynamicServiceAppService { private readonly IRepository _dynamicAppServiceRepository; private readonly DynamicServiceCompiler _compiler; public DynamicAppServiceAppService( IRepository dynamicAppServiceRepository, DynamicServiceCompiler compiler ) { _dynamicAppServiceRepository = dynamicAppServiceRepository; _compiler = compiler; } [Authorize(AppCodes.DeveloperKits.DynamicServices.TestCompile)] public virtual async Task TestCompileAsync(TestCompileRequestDto request) { try { return await _compiler.CompileAndValidateAsync(request.Code, CurrentTenant.Id); } catch (Exception ex) { Logger.LogError(ex, "Test compile sırasında hata. Tenant: {TenantId}", CurrentTenant.Id); throw; } } [Authorize(AppCodes.DeveloperKits.DynamicServices.Publish)] public virtual async Task PublishAsync(PublishAppServiceRequestDto request) { try { // Önce kodu test compile et var compileResult = await _compiler.CompileAndValidateAsync(request.Code, CurrentTenant.Id); if (!compileResult.Success) { return new PublishResultDto { Success = false, ErrorMessage = compileResult.ErrorMessage }; } // Aynı isimde AppService var mı kontrol et var existingService = await _dynamicAppServiceRepository .FirstOrDefaultAsync(x => x.Name == request.Name && x.TenantId == CurrentTenant.Id); DynamicService appService; if (existingService != null) { // Mevcut servisi güncelle existingService.UpdateCode(request.Code); existingService.DisplayName = request.DisplayName; existingService.Description = request.Description; existingService.PrimaryEntityType = request.PrimaryEntityType; existingService.IsActive = request.IsActive; appService = await _dynamicAppServiceRepository.UpdateAsync(existingService); } else { appService = new DynamicService( GuidGenerator.Create(), request.Name, request.Code, CurrentTenant.Id) { DisplayName = request.DisplayName, Description = request.Description, PrimaryEntityType = request.PrimaryEntityType, ControllerName = GenerateControllerName(request.Name), IsActive = request.IsActive }; appService = await _dynamicAppServiceRepository.InsertAsync(appService); } var assemblyName = $"{appService.Name}_{appService.Version}"; // Pasif olarak yayınlanıyorsa mevcut kaydı kaldır, assembly yükleme if (!request.IsActive) { DynamicServiceCompiler.NotifyAssemblyUnregistration?.Invoke( CurrentTenant.Id ?? Guid.Empty, appService.Name); appService.MarkCompilationSuccess(); await _dynamicAppServiceRepository.UpdateAsync(appService); return new PublishResultDto { Success = true, AppServiceId = appService.Id, ControllerName = appService.ControllerName, GeneratedEndpoints = new List() }; } var loadResult = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, request.Code, assemblyName); if (loadResult.Success) { appService.MarkCompilationSuccess(); await _dynamicAppServiceRepository.UpdateAsync(appService); var endpoints = loadResult.LoadedAssembly != null ? _compiler.ExtractEndpointsFromAssembly(loadResult.LoadedAssembly, request.Name) : new List(); return new PublishResultDto { Success = true, AppServiceId = appService.Id, ControllerName = appService.ControllerName, SwaggerUrl = "/swagger/index.html", GeneratedEndpoints = endpoints }; } else { // Derleme hatası durumunu kaydet appService.MarkCompilationError(loadResult.ErrorMessage); await _dynamicAppServiceRepository.UpdateAsync(appService); return new PublishResultDto { Success = false, ErrorMessage = loadResult.ErrorMessage, AppServiceId = appService.Id }; } } catch (Exception ex) { Logger.LogError(ex, "AppService yayınlama sırasında hata. Ad: {Name}, Tenant: {TenantId}", request.Name, CurrentTenant.Id); return new PublishResultDto { Success = false, ErrorMessage = $"Yayınlama sırasında beklenmeyen hata: {ex.Message}" }; } } [Authorize(AppCodes.DeveloperKits.DynamicServices.DynamicService)] public virtual async Task> GetListAsync(PagedAndSortedResultRequestDto input) { Logger.LogDebug("AppService listesi istendi. Tenant: {TenantId}", CurrentTenant.Id); var queryable = await _dynamicAppServiceRepository.GetQueryableAsync(); // Tenant filtresi otomatik uygulanır (IMultiTenant) var query = queryable.WhereIf(!string.IsNullOrEmpty(input.Sorting), x => x.CreationTime.ToString().Contains(input.Sorting ?? "")); var totalCount = await AsyncExecuter.CountAsync(query); var items = await AsyncExecuter.ToListAsync( query.OrderByDescending(x => x.CreationTime) .Skip(input.SkipCount) .Take(input.MaxResultCount) ); var dtos = ObjectMapper.Map, List>(items); return new PagedResultDto(totalCount, dtos); } [Authorize(AppCodes.DeveloperKits.DynamicServices.ViewCode)] public virtual async Task GetAsync(Guid id) { var appService = await _dynamicAppServiceRepository.GetAsync(id); return ObjectMapper.Map(appService); } [Authorize(AppCodes.DeveloperKits.DynamicServices.Delete)] public virtual async Task DeleteAsync(Guid id) { var appService = await _dynamicAppServiceRepository.GetAsync(id); // Runtime'dan assembly ve Swagger endpoint'ini kaldır DynamicServiceCompiler.NotifyAssemblyUnregistration?.Invoke( CurrentTenant.Id ?? Guid.Empty, appService.Name); await _dynamicAppServiceRepository.DeleteAsync(id); } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task SetActiveAsync(Guid id, bool isActive) { var appService = await _dynamicAppServiceRepository.GetAsync(id); appService.IsActive = isActive; await _dynamicAppServiceRepository.UpdateAsync(appService); if (!isActive) { // Pasif yapılınca Swagger/MVC'den endpoint'i kaldır DynamicServiceCompiler.NotifyAssemblyUnregistration?.Invoke( CurrentTenant.Id ?? Guid.Empty, appService.Name); } else if (appService.CompilationStatus == CompilationStatus.Success) { // Aktif yapılınca yeniden derle ve yayınla var assemblyName = $"{appService.Name}_{appService.Version}"; var result = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, appService.Code, assemblyName); if (!result.Success) { Logger.LogWarning( "Servis aktif edildi ancak yeniden derleme başarısız. Ad: {Name}, Hata: {Error}", appService.Name, result.ErrorMessage); } } } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task ReloadAllActiveServicesAsync() { var activeServices = await _dynamicAppServiceRepository .GetListAsync(x => x.IsActive && x.CompilationStatus == CompilationStatus.Success); var successCount = 0; var errorCount = 0; foreach (var service in activeServices) { try { // Service.Name üzerinden assembly adı oluştur var assemblyName = $"{service.Name}_{service.Version}"; var result = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, service.Code, assemblyName); if (result.Success) { successCount++; } else { errorCount++; service.MarkCompilationError(result.ErrorMessage); await _dynamicAppServiceRepository.UpdateAsync(service); } } catch (Exception ex) { errorCount++; Logger.LogError(ex, "AppService yeniden yükleme hatası. ID: {Id}", service.Id); service.MarkCompilationError(ex.Message); await _dynamicAppServiceRepository.UpdateAsync(service); } } } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task RecompileAsync(Guid id) { var appService = await _dynamicAppServiceRepository.GetAsync(id); var assemblyName = $"{appService.Name}_{appService.Version + 1}"; var result = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, appService.Code, assemblyName); if (result.Success) { appService.MarkCompilationSuccess(); appService.Version++; } else { appService.MarkCompilationError(result.ErrorMessage); } await _dynamicAppServiceRepository.UpdateAsync(appService); return result; } private string GenerateControllerName(string serviceName) { // DynamicCustomerAppService -> DynamicCustomer var controllerName = serviceName; if (controllerName.EndsWith("AppService")) { controllerName = controllerName.Substring(0, controllerName.Length - "AppService".Length); } else if (controllerName.EndsWith("ApplicationService")) { controllerName = controllerName.Substring(0, controllerName.Length - "ApplicationService".Length); } return controllerName; } }