Report App Service

This commit is contained in:
Sedat ÖZTÜRK 2025-08-15 14:26:03 +03:00
parent b6efa5c3fd
commit cd51347f3b
5 changed files with 233 additions and 87 deletions

View file

@ -4,23 +4,24 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kurs.Platform.Entities; using Kurs.Platform.Entities;
using Kurs.Platform.Repositories;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using System.Linq.Dynamic.Core;
using Microsoft.EntityFrameworkCore;
namespace Kurs.Platform.Reports; namespace Kurs.Platform.Reports;
[Authorize()] [Authorize()]
public class ReportAppService : PlatformAppService, IReportAppService public class ReportAppService : PlatformAppService, IReportAppService
{ {
private readonly IReportTemplateRepository _reportTemplateRepository; private readonly IRepository<ReportTemplate, Guid> _reportTemplateRepository;
private readonly IGeneratedReportRepository _generatedReportRepository; private readonly IRepository<GeneratedReport, Guid> _generatedReportRepository;
private readonly IRepository<ReportParameter, Guid> _reportParameterRepository; private readonly IRepository<ReportParameter, Guid> _reportParameterRepository;
public ReportAppService( public ReportAppService(
IReportTemplateRepository reportTemplateRepository, IRepository<ReportTemplate, Guid> reportTemplateRepository,
IGeneratedReportRepository generatedReportRepository, IRepository<GeneratedReport, Guid> generatedReportRepository,
IRepository<ReportParameter, Guid> reportParameterRepository) IRepository<ReportParameter, Guid> reportParameterRepository)
{ {
_reportTemplateRepository = reportTemplateRepository; _reportTemplateRepository = reportTemplateRepository;
@ -30,22 +31,50 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<PagedResultDto<ReportTemplateDto>> GetTemplatesAsync(GetReportTemplatesInput input) public async Task<PagedResultDto<ReportTemplateDto>> GetTemplatesAsync(GetReportTemplatesInput input)
{ {
var totalCount = await _reportTemplateRepository.GetCountAsync(input.Filter, input.Category); // IQueryable başlat
var templates = await _reportTemplateRepository.GetListAsync( var query = await _reportTemplateRepository.GetQueryableAsync();
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.Category);
// Filtreleme
if (!string.IsNullOrWhiteSpace(input.Filter))
{
query = query.Where(x =>
x.Name.Contains(input.Filter) ||
x.Description.Contains(input.Filter) ||
x.Category.Contains(input.Filter)
);
}
if (!string.IsNullOrWhiteSpace(input.Category))
{
query = query.Where(x => x.Category == input.Category);
}
// Toplam kayıt sayısı
var totalCount = await AsyncExecuter.CountAsync(query);
// Sıralama (ABP default olarak sorting null ise Id'ye göre sıralar)
query = query.OrderBy(input.Sorting ?? nameof(ReportTemplate.Name));
// Sayfalama
var templates = await AsyncExecuter.ToListAsync(
query
.Skip(input.SkipCount)
.Take(input.MaxResultCount)
);
// DTO dönüşümü
var templateDtos = templates.Select(MapToReportTemplateDto).ToList(); var templateDtos = templates.Select(MapToReportTemplateDto).ToList();
return new PagedResultDto<ReportTemplateDto>(totalCount, templateDtos); return new PagedResultDto<ReportTemplateDto>(
totalCount,
templateDtos
);
} }
public async Task<ReportTemplateDto> GetTemplateAsync(Guid id) public async Task<ReportTemplateDto> GetTemplateAsync(Guid id)
{ {
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(id); var template = await _reportTemplateRepository.GetAsync(id);
return MapToReportTemplateDto(template); return MapToReportTemplateDto(template);
} }
@ -56,9 +85,10 @@ public class ReportAppService : PlatformAppService, IReportAppService
input.Name, input.Name,
input.Description, input.Description,
input.HtmlContent, input.HtmlContent,
input.Category ?? "Genel"); input.Category ?? "Genel")
{
template.Tags = JsonSerializer.Serialize(input.Tags); Tags = JsonSerializer.Serialize(input.Tags)
};
template = await _reportTemplateRepository.InsertAsync(template, true); template = await _reportTemplateRepository.InsertAsync(template, true);
@ -71,10 +101,11 @@ public class ReportAppService : PlatformAppService, IReportAppService
paramDto.Name, paramDto.Name,
paramDto.Placeholder, paramDto.Placeholder,
(Entities.ReportParameterType)paramDto.Type, (Entities.ReportParameterType)paramDto.Type,
paramDto.Required); paramDto.Required)
{
parameter.DefaultValue = paramDto.DefaultValue; DefaultValue = paramDto.DefaultValue,
parameter.Description = paramDto.Description; Description = paramDto.Description
};
await _reportParameterRepository.InsertAsync(parameter); await _reportParameterRepository.InsertAsync(parameter);
} }
@ -84,7 +115,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<ReportTemplateDto> UpdateTemplateAsync(Guid id, UpdateReportTemplateDto input) public async Task<ReportTemplateDto> UpdateTemplateAsync(Guid id, UpdateReportTemplateDto input)
{ {
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(id); var template = await _reportTemplateRepository.GetAsync(id);
template.Name = input.Name; template.Name = input.Name;
template.Description = input.Description; template.Description = input.Description;
@ -128,14 +159,41 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input) public async Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input)
{ {
var totalCount = await _generatedReportRepository.GetCountAsync(input.Filter, input.TemplateId); var query = await _generatedReportRepository.GetQueryableAsync();
var reports = await _generatedReportRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.TemplateId);
// Okuma senaryosu: tracking gerekmiyor + Template'ı eager load edelim
query = query.AsNoTracking()
.Include(x => x.Template);
// Filtre
if (!string.IsNullOrWhiteSpace(input.Filter))
{
query = query.Where(x =>
x.TemplateName.Contains(input.Filter) ||
x.GeneratedContent.Contains(input.Filter)
);
}
if (input.TemplateId.HasValue)
{
query = query.Where(x => x.TemplateId == input.TemplateId.Value);
}
// Toplam kayıt
var totalCount = await AsyncExecuter.CountAsync(query);
// Sıralama
if (!string.IsNullOrWhiteSpace(input.Sorting))
query = query.OrderBy(input.Sorting); // ör. "generatedAt DESC" veya "templateName"
else
query = query.OrderByDescending(x => x.GeneratedAt);
// Sayfalama
var reports = await AsyncExecuter.ToListAsync(
query.Skip(input.SkipCount).Take(input.MaxResultCount)
);
// DTO map
var reportDtos = reports.Select(MapToGeneratedReportDto).ToList(); var reportDtos = reports.Select(MapToGeneratedReportDto).ToList();
return new PagedResultDto<GeneratedReportDto>(totalCount, reportDtos); return new PagedResultDto<GeneratedReportDto>(totalCount, reportDtos);
@ -143,13 +201,14 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id) public async Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id)
{ {
var report = await _generatedReportRepository.GetByIdWithTemplateAsync(id); var report = await _generatedReportRepository.GetAsync(id);
return MapToGeneratedReportDto(report); return MapToGeneratedReportDto(report);
} }
public async Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input) public async Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input)
{ {
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(input.TemplateId); var template = await _reportTemplateRepository.GetAsync(input.TemplateId);
if (template == null) if (template == null)
{ {
throw new ArgumentException("Template not found"); throw new ArgumentException("Template not found");
@ -214,7 +273,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
try try
{ {
dto.Tags = string.IsNullOrEmpty(template.Tags) dto.Tags = string.IsNullOrEmpty(template.Tags)
? new List<string>() ? []
: JsonSerializer.Deserialize<List<string>>(template.Tags); : JsonSerializer.Deserialize<List<string>>(template.Tags);
} }
catch catch
@ -233,7 +292,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
DefaultValue = p.DefaultValue, DefaultValue = p.DefaultValue,
Required = p.Required, Required = p.Required,
Description = p.Description Description = p.Description
}).ToList() ?? new List<ReportParameterDto>(); }).ToList() ?? [];
return dto; return dto;
} }
@ -262,7 +321,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
} }
catch catch
{ {
dto.Parameters = new Dictionary<string, string>(); dto.Parameters = [];
} }
// Template mapping // Template mapping

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20250815090839_Reports")] [Migration("20250815110914_Reports")]
partial class Reports partial class Reports
{ {
/// <inheritdoc /> /// <inheritdoc />

View file

@ -1,32 +1,118 @@
export interface ReportTemplateDto { // reports.models.ts
id: string;
name: string; /** Enum, backend ile birebir (0..4) */
description: string; export enum ReportParameterType {
htmlContent: string; Text = 0,
parameters: ReportParameterDto[]; Number = 1,
creationTime: Date; Date = 2,
lastModificationTime: Date; Email = 3,
category: string; Url = 4,
tags: string[];
} }
export type ReportParameterType = "text" | "number" | "date" | "email" | "url"; /** ---- API MODELLERİ (Raw JSON) ---- */
/** Not: Tarihler APIden ISO string olarak gelir (Date değil) */
export interface ReportParameterDto { export interface ReportParameterDto {
id: string; id: string
name: string; reportTemplateId: string
placeholder: string; name: string
type: ReportParameterType; placeholder?: string
defaultValue?: string; type: ReportParameterType // enum (0..4)
required: boolean; defaultValue?: string
description?: string; required: boolean
description?: string
}
export interface ReportTemplateDto {
id: string
name: string
description?: string
htmlContent: string
category?: string
tags: string[]
parameters: ReportParameterDto[]
// FullAuditedEntityDto alanları
creationTime: string // ISO
lastModificationTime?: string // ISO | undefined
creatorId?: string
lastModifierId?: string
} }
export interface GeneratedReportDto { export interface GeneratedReportDto {
id: string; id: string
templateId: string; templateId?: string | null
templateName: string; templateName: string
generatedContent: string; generatedContent: string
parameters: Record<string, string>; parameters: Record<string, string>
generatedAt: Date; generatedAt: string // ISO
// FullAuditedEntityDto alanları
creationTime: string // ISO
lastModificationTime?: string // ISO | undefined
creatorId?: string
lastModifierId?: string
template?: ReportTemplateDto // dolu gelebilir
}
/** Create / Update inputları */
export interface CreateReportParameterDto {
name: string
placeholder?: string
type: ReportParameterType
defaultValue?: string
required: boolean
description?: string
}
export interface UpdateReportParameterDto extends CreateReportParameterDto {
id?: string // opsiyonel
}
export interface CreateReportTemplateDto {
name: string
description?: string
htmlContent: string
category?: string
tags?: string[]
parameters: CreateReportParameterDto[]
}
export interface UpdateReportTemplateDto {
name: string
description?: string
htmlContent: string
category?: string
tags?: string[]
parameters: UpdateReportParameterDto[]
}
/** Generate inputu */
export interface GenerateReportDto {
templateId: string
parameters: Record<string, string>
}
/** List inputları (query string) */
export interface GetReportTemplatesInput {
skipCount?: number
maxResultCount?: number
sorting?: string
filter?: string
category?: string
}
export interface GetGeneratedReportsInput {
skipCount?: number
maxResultCount?: number
sorting?: string
filter?: string
templateId?: string
}
/** (Opsiyonel) Paged wrapper — projende zaten varsa bunu kullanmana gerek yok */
export interface PagedResultDto<T> {
items: T[]
totalCount: number
} }

View file

@ -1,5 +1,11 @@
import { GeneratedReportDto, ReportTemplateDto } from '@/proxy/reports/models' import {
import apiService, { Config } from './api.service' ReportTemplateDto,
GeneratedReportDto,
CreateReportTemplateDto,
UpdateReportTemplateDto,
GenerateReportDto, // backend'deki GenerateReportDto (templateId + parameters)
} from '@/proxy/reports/models'
import apiService from './api.service'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy' import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
export interface ReportsData { export interface ReportsData {
@ -7,20 +13,15 @@ export interface ReportsData {
generatedReports: GeneratedReportDto[] generatedReports: GeneratedReportDto[]
} }
export interface GenerateReportRequestDto {
templateId: string
parameters: Record<string, string>
}
export class ReportsService { export class ReportsService {
apiName = 'Default' apiName = 'Default'
// Template operations // TEMPLATES
getTemplates = (input: PagedAndSortedResultRequestDto) => getTemplates = (input: PagedAndSortedResultRequestDto) =>
apiService.fetchData<PagedResultDto<ReportTemplateDto>, PagedAndSortedResultRequestDto>( apiService.fetchData<PagedResultDto<ReportTemplateDto>, PagedAndSortedResultRequestDto>(
{ {
method: 'GET', method: 'GET',
url: '/api/app/reports/templates', url: '/api/app/report/templates', // ✔ Swagger: GET /api/app/report/templates
params: { params: {
sorting: input.sorting, sorting: input.sorting,
skipCount: input.skipCount, skipCount: input.skipCount,
@ -34,27 +35,27 @@ export class ReportsService {
apiService.fetchData<ReportTemplateDto>( apiService.fetchData<ReportTemplateDto>(
{ {
method: 'GET', method: 'GET',
url: `/api/app/reports/templates/${id}`, url: `/api/app/report/${id}/template`, // ✔ Swagger: GET /api/app/report/{id}/template
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
createTemplate = (input: ReportTemplateDto) => createTemplate = (input: CreateReportTemplateDto) =>
apiService.fetchData( apiService.fetchData<ReportTemplateDto, CreateReportTemplateDto>(
{ {
method: 'POST', method: 'POST',
url: '/api/app/reports/templates', url: '/api/app/report/template', // ✔ Swagger: POST /api/app/report/template
data: input, data: input,
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
updateTemplate = (id: string, input: ReportTemplateDto) => updateTemplate = (id: string, input: UpdateReportTemplateDto) =>
apiService.fetchData<ReportTemplateDto, ReportTemplateDto>( apiService.fetchData<ReportTemplateDto, UpdateReportTemplateDto>(
{ {
method: 'PUT', method: 'PUT',
url: `/api/app/reports/templates/${id}`, url: `/api/app/report/${id}/template`, // ✔ Swagger: PUT /api/app/report/{id}/template
data: input as any, data: input,
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
@ -63,17 +64,17 @@ export class ReportsService {
apiService.fetchData( apiService.fetchData(
{ {
method: 'DELETE', method: 'DELETE',
url: `/api/app/reports/templates/${id}`, url: `/api/app/report/${id}/template`, // ✔ Swagger: DELETE /api/app/report/{id}/template
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
// Generated Reports operations // GENERATED REPORTS
getGeneratedReports = (input: PagedAndSortedResultRequestDto) => getGeneratedReports = (input: PagedAndSortedResultRequestDto) =>
apiService.fetchData<PagedResultDto<GeneratedReportDto>, PagedAndSortedResultRequestDto>( apiService.fetchData<PagedResultDto<GeneratedReportDto>, PagedAndSortedResultRequestDto>(
{ {
method: 'GET', method: 'GET',
url: '/api/app/reports/generated', url: '/api/app/report/generated-reports', // ✔ Swagger: GET /api/app/report/generated-reports
params: { params: {
sorting: input.sorting, sorting: input.sorting,
skipCount: input.skipCount, skipCount: input.skipCount,
@ -87,17 +88,17 @@ export class ReportsService {
apiService.fetchData<GeneratedReportDto>( apiService.fetchData<GeneratedReportDto>(
{ {
method: 'GET', method: 'GET',
url: `/api/app/reports/generated/${id}`, url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: GET /api/app/report/{id}/generated-report
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
generateReport = (input: GeneratedReportDto) => generateReport = (input: GenerateReportDto) =>
apiService.fetchData<GeneratedReportDto>( apiService.fetchData<GeneratedReportDto, GenerateReportDto>(
{ {
method: 'POST', method: 'POST',
url: '/api/app/reports/generate', url: '/api/app/report/generate-report', // ✔ Swagger: POST /api/app/report/generate-report
data: input as any, data: input,
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
@ -106,17 +107,17 @@ export class ReportsService {
apiService.fetchData( apiService.fetchData(
{ {
method: 'DELETE', method: 'DELETE',
url: `/api/app/reports/generated/${id}`, url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: DELETE /api/app/report/{id}/generated-report
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )
// Bulk operations // BULK
getAllData = () => getAllData = () =>
apiService.fetchData<ReportsData>( apiService.fetchData<ReportsData>(
{ {
method: 'GET', method: 'GET',
url: '/api/app/reports/all', url: '/api/app/report/data', // ✔ Swagger: GET /api/app/report/data
}, },
{ apiName: this.apiName }, { apiName: this.apiName },
) )