Extend validation to announcement and faq
TaxBaik CI/CD / build-and-deploy (push) Successful in 3m10s

This commit is contained in:
2026-07-07 16:35:36 +09:00
parent 227ed21c1c
commit 94a6803345
6 changed files with 61 additions and 11 deletions
@@ -53,3 +53,13 @@ public sealed class CreateClientDtoValidator : AbstractValidator<CreateClientDto
RuleFor(x => x.Memo).MaximumLength(ValidationRules.MemoMaxLength); RuleFor(x => x.Memo).MaximumLength(ValidationRules.MemoMaxLength);
} }
} }
public sealed class FaqValidator : AbstractValidator<TaxBaik.Domain.Entities.Faq>
{
public FaqValidator()
{
RuleFor(x => x.Question).NotEmpty().MaximumLength(300);
RuleFor(x => x.Answer).NotEmpty().MinimumLength(10).MaximumLength(5000);
RuleFor(x => x.Category).MaximumLength(50);
}
}
@@ -1,10 +1,11 @@
namespace TaxBaik.Application.Services; namespace TaxBaik.Application.Services;
using FluentValidation;
using TaxBaik.Application.DTOs; using TaxBaik.Application.DTOs;
using TaxBaik.Domain.Entities; using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces; using TaxBaik.Domain.Interfaces;
public class AnnouncementService(IAnnouncementRepository repository) public class AnnouncementService(IAnnouncementRepository repository, IValidator<AnnouncementDto> validator)
{ {
public Task<IEnumerable<Announcement>> GetActiveAsync(CancellationToken ct = default) public Task<IEnumerable<Announcement>> GetActiveAsync(CancellationToken ct = default)
=> repository.GetActiveAsync(ct); => repository.GetActiveAsync(ct);
@@ -17,12 +18,14 @@ public class AnnouncementService(IAnnouncementRepository repository)
public Task<int> CreateAsync(AnnouncementDto dto, CancellationToken ct = default) public Task<int> CreateAsync(AnnouncementDto dto, CancellationToken ct = default)
{ {
validator.ValidateAndThrow(dto);
var entity = MapToEntity(dto); var entity = MapToEntity(dto);
return repository.CreateAsync(entity, ct); return repository.CreateAsync(entity, ct);
} }
public Task UpdateAsync(AnnouncementDto dto, CancellationToken ct = default) public Task UpdateAsync(AnnouncementDto dto, CancellationToken ct = default)
{ {
validator.ValidateAndThrow(dto);
var entity = MapToEntity(dto); var entity = MapToEntity(dto);
return repository.UpdateAsync(entity, ct); return repository.UpdateAsync(entity, ct);
} }
+4 -10
View File
@@ -1,9 +1,10 @@
namespace TaxBaik.Application.Services; namespace TaxBaik.Application.Services;
using FluentValidation;
using TaxBaik.Domain.Entities; using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces; using TaxBaik.Domain.Interfaces;
public class FaqService(IFaqRepository repository) public class FaqService(IFaqRepository repository, IValidator<Faq> validator)
{ {
public static readonly string[] Categories = public static readonly string[] Categories =
["기장세금신고", "부동산", "증여상속", "기타"]; ["기장세금신고", "부동산", "증여상속", "기타"];
@@ -19,24 +20,17 @@ public class FaqService(IFaqRepository repository)
public async Task<int> CreateAsync(Faq faq, CancellationToken ct = default) public async Task<int> CreateAsync(Faq faq, CancellationToken ct = default)
{ {
Validate(faq); validator.ValidateAndThrow(faq);
return await repository.CreateAsync(faq, ct); return await repository.CreateAsync(faq, ct);
} }
public async Task UpdateAsync(Faq faq, CancellationToken ct = default) public async Task UpdateAsync(Faq faq, CancellationToken ct = default)
{ {
Validate(faq); validator.ValidateAndThrow(faq);
await repository.UpdateAsync(faq, ct); await repository.UpdateAsync(faq, ct);
} }
public async Task DeleteAsync(int id, CancellationToken ct = default) => public async Task DeleteAsync(int id, CancellationToken ct = default) =>
await repository.DeleteAsync(id, ct); await repository.DeleteAsync(id, ct);
private static void Validate(Faq faq)
{
if (string.IsNullOrWhiteSpace(faq.Question))
throw new ValidationException("질문을 입력하세요.");
if (string.IsNullOrWhiteSpace(faq.Answer))
throw new ValidationException("답변을 입력하세요.");
}
} }
@@ -0,0 +1,19 @@
namespace TaxBaik.Web.Endpoints.Announcement;
using FluentValidation;
using TaxBaik.Application.DTOs;
public sealed class AnnouncementDtoValidator : AbstractValidator<AnnouncementDto>
{
public AnnouncementDtoValidator()
{
RuleFor(x => x.Title)
.NotEmpty().WithMessage("제목을 입력하세요.")
.MaximumLength(ValidationRules.TitleMaxLength).WithMessage("제목은 최대 100자까지 입력 가능합니다.");
RuleFor(x => x.Content).MaximumLength(2000).WithMessage("상세 내용은 최대 2000자까지 입력 가능합니다.");
RuleFor(x => x.DisplayType)
.NotEmpty().WithMessage("표시 유형을 선택하세요.")
.MaximumLength(20).WithMessage("표시 유형은 최대 20자까지 입력 가능합니다.");
}
}
@@ -0,0 +1,23 @@
namespace TaxBaik.Web.Endpoints.Faq;
using FluentValidation;
public sealed class CreateFaqRequestValidator : AbstractValidator<CreateFaqRequest>
{
public CreateFaqRequestValidator()
{
RuleFor(x => x.Question).NotEmpty().MaximumLength(300);
RuleFor(x => x.Answer).NotEmpty().MinimumLength(10).MaximumLength(5000);
RuleFor(x => x.Category).MaximumLength(50);
}
}
public sealed class UpdateFaqRequestValidator : AbstractValidator<UpdateFaqRequest>
{
public UpdateFaqRequestValidator()
{
RuleFor(x => x.Question).NotEmpty().MaximumLength(300);
RuleFor(x => x.Answer).NotEmpty().MinimumLength(10).MaximumLength(5000);
RuleFor(x => x.Category).MaximumLength(50);
}
}
+1
View File
@@ -359,6 +359,7 @@ builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
builder.Services.AddInfrastructure(); builder.Services.AddInfrastructure();
builder.Services.AddApplication(); builder.Services.AddApplication();
builder.Services.AddValidatorsFromAssemblyContaining<TaxBaik.Application.DTOs.CreateBlogPostDtoValidator>(); builder.Services.AddValidatorsFromAssemblyContaining<TaxBaik.Application.DTOs.CreateBlogPostDtoValidator>();
builder.Services.AddValidatorsFromAssemblyContaining<TaxBaik.Web.Endpoints.Announcement.AnnouncementDtoValidator>();
builder.Services.AddScoped<IInquiryNotificationService, TelegramInquiryNotificationService>(); builder.Services.AddScoped<IInquiryNotificationService, TelegramInquiryNotificationService>();
builder.Services.AddScoped<TaxBaik.Web.Services.SitemapValidationService>(); builder.Services.AddScoped<TaxBaik.Web.Services.SitemapValidationService>();