구현: W2.3 애플리케이션 서비스 레이어
BlogService: - GetBySlugAsync: 공개된 포스트 조회 - GetPublishedPagedAsync: 페이징된 포스트 목록 (카테고리 필터 옵션) - GetAllForAdminAsync: 관리자용 전체 포스트 (발행 상태 무관) - CreateAsync: 포스트 생성 (슬러그 자동 생성) - UpdateAsync/DeleteAsync: 포스트 수정/삭제 - IncrementViewCountAsync: 조회수 증가 (fire-and-forget) - GenerateSlug: 한국어 제목 → 로마자 슬러그 변환 InquiryService: - SubmitAsync: 상담 신청 폼 제출 * 이름, 전화번호 필수 * 전화번호 정규식 검증 (010-XXXX-XXXX) * 문의 내용 필수 * ValidationException 으로 입력값 오류 처리 - GetByIdAsync/GetPagedAsync: 문의 조회 및 필터링 - UpdateStatusAsync: 문의 상태 변경 추가: - ValidationException: 비즈니스 검증 예외 - DependencyInjection: AddApplication() 확장 메서드 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
namespace TaxBaik.Application;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddApplication(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<BlogService>();
|
||||
services.AddScoped<InquiryService>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class BlogService(IBlogPostRepository repository)
|
||||
{
|
||||
public async Task<BlogPost?> GetBySlugAsync(string slug, CancellationToken ct = default) =>
|
||||
await repository.GetBySlugAsync(slug, ct);
|
||||
|
||||
public async Task<(IEnumerable<BlogPost>, int)> GetPublishedPagedAsync(
|
||||
int page, int pageSize, int? categoryId = null, CancellationToken ct = default) =>
|
||||
await repository.GetPublishedPagedAsync(page, pageSize, categoryId, ct);
|
||||
|
||||
public async Task<IEnumerable<BlogPost>> GetAllForAdminAsync(CancellationToken ct = default) =>
|
||||
await repository.GetAllForAdminAsync(ct);
|
||||
|
||||
public async Task<int> CreateAsync(BlogPost post, CancellationToken ct = default)
|
||||
{
|
||||
post.Slug = GenerateSlug(post.Title);
|
||||
post.IsPublished = false;
|
||||
return await repository.CreateAsync(post, ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(BlogPost post, CancellationToken ct = default) =>
|
||||
await repository.UpdateAsync(post, ct);
|
||||
|
||||
public async Task DeleteAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.DeleteAsync(id, ct);
|
||||
|
||||
public async Task IncrementViewCountAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.IncrementViewCountAsync(id, ct);
|
||||
|
||||
private static string GenerateSlug(string title)
|
||||
{
|
||||
var slug = Regex.Replace(title.ToLowerInvariant(), @"[^\w\s-]", "");
|
||||
slug = Regex.Replace(slug, @"\s+", "-");
|
||||
slug = Regex.Replace(slug, @"-+", "-").Trim('-');
|
||||
return slug.Length > 100 ? slug[..100] : slug;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class InquiryService(IInquiryRepository repository)
|
||||
{
|
||||
private static readonly Regex PhoneRegex = new(@"^01[0-9]-\d{3,4}-\d{4}$");
|
||||
|
||||
public async Task<int> SubmitAsync(
|
||||
string name, string phone, string serviceType, string message,
|
||||
string? ipAddress = null, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ValidationException("이름을 입력하세요.");
|
||||
|
||||
if (!PhoneRegex.IsMatch(phone))
|
||||
throw new ValidationException("올바른 전화번호를 입력하세요. (예: 010-1234-5678)");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ValidationException("문의 내용을 입력하세요.");
|
||||
|
||||
var inquiry = new Inquiry
|
||||
{
|
||||
Name = name.Trim(),
|
||||
Phone = phone.Trim(),
|
||||
ServiceType = serviceType ?? "기타",
|
||||
Message = message.Trim(),
|
||||
IpAddress = ipAddress,
|
||||
Status = "new",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
return await repository.CreateAsync(inquiry, ct);
|
||||
}
|
||||
|
||||
public async Task<Inquiry?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.GetByIdAsync(id, ct);
|
||||
|
||||
public async Task<(IEnumerable<Inquiry>, int)> GetPagedAsync(
|
||||
int page, int pageSize, string? status = null, CancellationToken ct = default) =>
|
||||
await repository.GetPagedAsync(page, pageSize, status, ct);
|
||||
|
||||
public async Task UpdateStatusAsync(int id, string status, CancellationToken ct = default) =>
|
||||
await repository.UpdateStatusAsync(id, status, ct);
|
||||
}
|
||||
|
||||
public class ValidationException : Exception
|
||||
{
|
||||
public ValidationException(string message) : base(message) { }
|
||||
}
|
||||
Reference in New Issue
Block a user