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.Threading.Tasks;
using Kurs.Platform.Entities;
using Kurs.Platform.Repositories;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using System.Linq.Dynamic.Core;
using Microsoft.EntityFrameworkCore;
namespace Kurs.Platform.Reports;
[Authorize()]
public class ReportAppService : PlatformAppService, IReportAppService
{
private readonly IReportTemplateRepository _reportTemplateRepository;
private readonly IGeneratedReportRepository _generatedReportRepository;
private readonly IRepository<ReportTemplate, Guid> _reportTemplateRepository;
private readonly IRepository<GeneratedReport, Guid> _generatedReportRepository;
private readonly IRepository<ReportParameter, Guid> _reportParameterRepository;
public ReportAppService(
IReportTemplateRepository reportTemplateRepository,
IGeneratedReportRepository generatedReportRepository,
IRepository<ReportTemplate, Guid> reportTemplateRepository,
IRepository<GeneratedReport, Guid> generatedReportRepository,
IRepository<ReportParameter, Guid> reportParameterRepository)
{
_reportTemplateRepository = reportTemplateRepository;
@ -30,22 +31,50 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<PagedResultDto<ReportTemplateDto>> GetTemplatesAsync(GetReportTemplatesInput input)
{
var totalCount = await _reportTemplateRepository.GetCountAsync(input.Filter, input.Category);
var templates = await _reportTemplateRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.Category);
// IQueryable başlat
var query = await _reportTemplateRepository.GetQueryableAsync();
// 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();
return new PagedResultDto<ReportTemplateDto>(totalCount, templateDtos);
return new PagedResultDto<ReportTemplateDto>(
totalCount,
templateDtos
);
}
public async Task<ReportTemplateDto> GetTemplateAsync(Guid id)
{
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(id);
var template = await _reportTemplateRepository.GetAsync(id);
return MapToReportTemplateDto(template);
}
@ -56,9 +85,10 @@ public class ReportAppService : PlatformAppService, IReportAppService
input.Name,
input.Description,
input.HtmlContent,
input.Category ?? "Genel");
template.Tags = JsonSerializer.Serialize(input.Tags);
input.Category ?? "Genel")
{
Tags = JsonSerializer.Serialize(input.Tags)
};
template = await _reportTemplateRepository.InsertAsync(template, true);
@ -71,10 +101,11 @@ public class ReportAppService : PlatformAppService, IReportAppService
paramDto.Name,
paramDto.Placeholder,
(Entities.ReportParameterType)paramDto.Type,
paramDto.Required);
parameter.DefaultValue = paramDto.DefaultValue;
parameter.Description = paramDto.Description;
paramDto.Required)
{
DefaultValue = paramDto.DefaultValue,
Description = paramDto.Description
};
await _reportParameterRepository.InsertAsync(parameter);
}
@ -84,7 +115,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
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.Description = input.Description;
@ -128,14 +159,41 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<PagedResultDto<GeneratedReportDto>> GetGeneratedReportsAsync(GetGeneratedReportsInput input)
{
var totalCount = await _generatedReportRepository.GetCountAsync(input.Filter, input.TemplateId);
var reports = await _generatedReportRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter,
input.TemplateId);
var query = await _generatedReportRepository.GetQueryableAsync();
// 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();
return new PagedResultDto<GeneratedReportDto>(totalCount, reportDtos);
@ -143,13 +201,14 @@ public class ReportAppService : PlatformAppService, IReportAppService
public async Task<GeneratedReportDto> GetGeneratedReportAsync(Guid id)
{
var report = await _generatedReportRepository.GetByIdWithTemplateAsync(id);
var report = await _generatedReportRepository.GetAsync(id);
return MapToGeneratedReportDto(report);
}
public async Task<GeneratedReportDto> GenerateReportAsync(GenerateReportDto input)
{
var template = await _reportTemplateRepository.GetByIdWithParametersAsync(input.TemplateId);
var template = await _reportTemplateRepository.GetAsync(input.TemplateId);
if (template == null)
{
throw new ArgumentException("Template not found");
@ -214,7 +273,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
try
{
dto.Tags = string.IsNullOrEmpty(template.Tags)
? new List<string>()
? []
: JsonSerializer.Deserialize<List<string>>(template.Tags);
}
catch
@ -233,7 +292,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
DefaultValue = p.DefaultValue,
Required = p.Required,
Description = p.Description
}).ToList() ?? new List<ReportParameterDto>();
}).ToList() ?? [];
return dto;
}
@ -262,7 +321,7 @@ public class ReportAppService : PlatformAppService, IReportAppService
}
catch
{
dto.Parameters = new Dictionary<string, string>();
dto.Parameters = [];
}
// Template mapping

View file

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

View file

@ -1,32 +1,118 @@
export interface ReportTemplateDto {
id: string;
name: string;
description: string;
htmlContent: string;
parameters: ReportParameterDto[];
creationTime: Date;
lastModificationTime: Date;
category: string;
tags: string[];
// reports.models.ts
/** Enum, backend ile birebir (0..4) */
export enum ReportParameterType {
Text = 0,
Number = 1,
Date = 2,
Email = 3,
Url = 4,
}
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 {
id: string;
name: string;
placeholder: string;
type: ReportParameterType;
defaultValue?: string;
required: boolean;
description?: string;
id: string
reportTemplateId: string
name: string
placeholder?: string
type: ReportParameterType // enum (0..4)
defaultValue?: 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 {
id: string;
templateId: string;
templateName: string;
generatedContent: string;
parameters: Record<string, string>;
generatedAt: Date;
id: string
templateId?: string | null
templateName: string
generatedContent: string
parameters: Record<string, string>
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 apiService, { Config } from './api.service'
import {
ReportTemplateDto,
GeneratedReportDto,
CreateReportTemplateDto,
UpdateReportTemplateDto,
GenerateReportDto, // backend'deki GenerateReportDto (templateId + parameters)
} from '@/proxy/reports/models'
import apiService from './api.service'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
export interface ReportsData {
@ -7,20 +13,15 @@ export interface ReportsData {
generatedReports: GeneratedReportDto[]
}
export interface GenerateReportRequestDto {
templateId: string
parameters: Record<string, string>
}
export class ReportsService {
apiName = 'Default'
// Template operations
// TEMPLATES
getTemplates = (input: PagedAndSortedResultRequestDto) =>
apiService.fetchData<PagedResultDto<ReportTemplateDto>, PagedAndSortedResultRequestDto>(
{
method: 'GET',
url: '/api/app/reports/templates',
url: '/api/app/report/templates', // ✔ Swagger: GET /api/app/report/templates
params: {
sorting: input.sorting,
skipCount: input.skipCount,
@ -34,27 +35,27 @@ export class ReportsService {
apiService.fetchData<ReportTemplateDto>(
{
method: 'GET',
url: `/api/app/reports/templates/${id}`,
url: `/api/app/report/${id}/template`, // ✔ Swagger: GET /api/app/report/{id}/template
},
{ apiName: this.apiName },
)
createTemplate = (input: ReportTemplateDto) =>
apiService.fetchData(
createTemplate = (input: CreateReportTemplateDto) =>
apiService.fetchData<ReportTemplateDto, CreateReportTemplateDto>(
{
method: 'POST',
url: '/api/app/reports/templates',
url: '/api/app/report/template', // ✔ Swagger: POST /api/app/report/template
data: input,
},
{ apiName: this.apiName },
)
updateTemplate = (id: string, input: ReportTemplateDto) =>
apiService.fetchData<ReportTemplateDto, ReportTemplateDto>(
updateTemplate = (id: string, input: UpdateReportTemplateDto) =>
apiService.fetchData<ReportTemplateDto, UpdateReportTemplateDto>(
{
method: 'PUT',
url: `/api/app/reports/templates/${id}`,
data: input as any,
url: `/api/app/report/${id}/template`, // ✔ Swagger: PUT /api/app/report/{id}/template
data: input,
},
{ apiName: this.apiName },
)
@ -63,17 +64,17 @@ export class ReportsService {
apiService.fetchData(
{
method: 'DELETE',
url: `/api/app/reports/templates/${id}`,
url: `/api/app/report/${id}/template`, // ✔ Swagger: DELETE /api/app/report/{id}/template
},
{ apiName: this.apiName },
)
// Generated Reports operations
// GENERATED REPORTS
getGeneratedReports = (input: PagedAndSortedResultRequestDto) =>
apiService.fetchData<PagedResultDto<GeneratedReportDto>, PagedAndSortedResultRequestDto>(
{
method: 'GET',
url: '/api/app/reports/generated',
url: '/api/app/report/generated-reports', // ✔ Swagger: GET /api/app/report/generated-reports
params: {
sorting: input.sorting,
skipCount: input.skipCount,
@ -87,17 +88,17 @@ export class ReportsService {
apiService.fetchData<GeneratedReportDto>(
{
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 },
)
generateReport = (input: GeneratedReportDto) =>
apiService.fetchData<GeneratedReportDto>(
generateReport = (input: GenerateReportDto) =>
apiService.fetchData<GeneratedReportDto, GenerateReportDto>(
{
method: 'POST',
url: '/api/app/reports/generate',
data: input as any,
url: '/api/app/report/generate-report', // ✔ Swagger: POST /api/app/report/generate-report
data: input,
},
{ apiName: this.apiName },
)
@ -106,17 +107,17 @@ export class ReportsService {
apiService.fetchData(
{
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 },
)
// Bulk operations
// BULK
getAllData = () =>
apiService.fetchData<ReportsData>(
{
method: 'GET',
url: '/api/app/reports/all',
url: '/api/app/report/data', // ✔ Swagger: GET /api/app/report/data
},
{ apiName: this.apiName },
)