sozsoft-platform/api/src/Sozsoft.Platform.Application/DeveloperKit/DynamicServiceAppService.cs
2026-03-02 10:36:38 +03:00

327 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<DynamicService, Guid> _dynamicAppServiceRepository;
private readonly DynamicServiceCompiler _compiler;
public DynamicAppServiceAppService(
IRepository<DynamicService, Guid> dynamicAppServiceRepository,
DynamicServiceCompiler compiler
)
{
_dynamicAppServiceRepository = dynamicAppServiceRepository;
_compiler = compiler;
}
[Authorize(AppCodes.DeveloperKits.DynamicServices.TestCompile)]
public virtual async Task<CompileResultDto> 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<PublishResultDto> 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<string>()
};
}
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<string>();
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<PagedResultDto<DynamicServiceDto>> 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<DynamicService>, List<DynamicServiceDto>>(items);
return new PagedResultDto<DynamicServiceDto>(totalCount, dtos);
}
[Authorize(AppCodes.DeveloperKits.DynamicServices.ViewCode)]
public virtual async Task<DynamicServiceDto> GetAsync(Guid id)
{
var appService = await _dynamicAppServiceRepository.GetAsync(id);
return ObjectMapper.Map<DynamicService, DynamicServiceDto>(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<CompileResultDto> 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;
}
}