Refactor: Rename DocumentOperator to DocumentService

Updated project and solution files to rename and restructure
`DocumentOperator` to `DocumentService`. Modified project
references in `DocumentService.API.csproj`, `DocumentService.Application.csproj`,
and `DocumentService.Infrastructure.csproj` to reflect the new
naming convention. Updated `DocumentService.sln` to remove
old project references, add new ones, and adjust configuration
and nested project mappings accordingly. This change aligns
the project structure with the new naming convention.
This commit is contained in:
2026-08-30 21:33:25 +02:00
parent 050cb55bf9
commit 207c778f3a
108 changed files with 37 additions and 37 deletions

View File

@@ -0,0 +1,125 @@
using FluentValidation;
using MediatR;
namespace DocumentService.Application.AddStamp;
/// <summary>
/// Command to add a stamp (text, image, or predefined) to PDF pages.
/// Combines Command, Handler, and Validator in a single file (Vertical Slice pattern).
/// </summary>
public record AddStampCommand : IRequest<byte[]>
{
public required Stream PdfStream { get; init; }
public required Domain.Models.ValueObjects.StampType StampType { get; init; }
public int[]? PageNumbers { get; init; } // null = all pages
public required (double X, double Y) Position { get; init; }
public (double Width, double Height)? Size { get; init; }
public Domain.Models.ValueObjects.AnnotationOrigin Origin { get; init; } = Domain.Models.ValueObjects.AnnotationOrigin.BottomLeft;
public string? Text { get; init; }
public string? FontName { get; init; }
public double? FontSize { get; init; }
public string? Color { get; init; }
public double? Opacity { get; init; }
public double? Rotation { get; init; }
public Domain.Models.ValueObjects.StampPlacement Placement { get; init; } = Domain.Models.ValueObjects.StampPlacement.Foreground;
public byte[]? ImageBytes { get; init; }
public Domain.Models.ValueObjects.PredefinedStampType? PredefinedType { get; init; }
}
/// <summary>
/// Handler for AddStampCommand.
/// </summary>
public class AddStampHandler(Common.Interfaces.IPdfProcessor pdfProcessor) : IRequestHandler<AddStampCommand, byte[]>
{
public async Task<byte[]> Handle(AddStampCommand command, CancellationToken cancellationToken)
{
return await pdfProcessor.AddStampAsync(
command.PdfStream,
command.StampType,
command.PageNumbers,
command.Position,
command.Size,
command.Origin,
command.Text,
command.FontName,
command.FontSize,
command.Color,
command.Opacity,
command.Rotation,
command.Placement,
command.ImageBytes,
command.PredefinedType
);
}
}
/// <summary>
/// Validator for AddStampCommand.
/// </summary>
public class AddStampValidator : AbstractValidator<AddStampCommand>
{
public AddStampValidator()
{
RuleFor(x => x.PdfStream)
.NotNull().WithMessage("PDF stream is required");
RuleFor(x => x.Position.X)
.GreaterThanOrEqualTo(0).WithMessage("Position X must be >= 0");
RuleFor(x => x.Position.Y)
.GreaterThanOrEqualTo(0).WithMessage("Position Y must be >= 0");
// Text stamp validation
When(x => x.StampType == Domain.Models.ValueObjects.StampType.Text, () =>
{
RuleFor(x => x.Text)
.NotEmpty().WithMessage("Text is required for Text stamp type");
});
// Image stamp validation
When(x => x.StampType == Domain.Models.ValueObjects.StampType.Image, () =>
{
RuleFor(x => x.ImageBytes)
.NotNull().WithMessage("Image bytes are required for Image stamp type")
.Must(bytes => bytes != null && bytes.Length > 0).WithMessage("Image bytes cannot be empty");
});
// Predefined stamp validation
When(x => x.StampType == Domain.Models.ValueObjects.StampType.Predefined, () =>
{
RuleFor(x => x.PredefinedType)
.NotNull().WithMessage("Predefined type is required for Predefined stamp type");
});
// Optional parameter validations
When(x => x.Opacity.HasValue, () =>
{
RuleFor(x => x.Opacity!.Value)
.InclusiveBetween(0.0, 1.0).WithMessage("Opacity must be between 0.0 and 1.0");
});
When(x => x.Rotation.HasValue, () =>
{
RuleFor(x => x.Rotation!.Value)
.InclusiveBetween(0.0, 360.0).WithMessage("Rotation must be between 0 and 360 degrees");
});
When(x => x.FontSize.HasValue, () =>
{
RuleFor(x => x.FontSize!.Value)
.GreaterThan(0).WithMessage("Font size must be positive");
});
When(x => !string.IsNullOrWhiteSpace(x.Color), () =>
{
RuleFor(x => x.Color!)
.Matches(@"^[0-9A-Fa-f]{6}$").WithMessage("Color must be 6-digit hex (e.g., 'FF0000')");
});
When(x => x.PageNumbers != null, () =>
{
RuleFor(x => x.PageNumbers!)
.Must(pages => pages.All(p => p > 0)).WithMessage("All page numbers must be >= 1");
});
}
}