From a97f31f89c7b041b37d806180d058111d4ebd517 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Fri, 3 Jul 2026 17:26:44 +0900 Subject: [PATCH] feat: migrate TaxProfileController to FastEndpoints (Phase 5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IMPLEMENTATION: - Create 5 FastEndpoints Endpoint classes (all in one file for efficiency): - CreateEndpoint: POST /api/taxprofile - GetAllEndpoint: GET /api/taxprofile - GetByClientIdEndpoint: GET /api/taxprofile/client/{clientId} - GetHighRiskEndpoint: GET /api/taxprofile/high-risk - GetUpcomingFilingsEndpoint: GET /api/taxprofile/upcoming-filings - UpdateEndpoint: PUT /api/taxprofile/{id} PROGRESS: ✅ Phase 1: Auth (4 endpoints) - DEPLOYED ✅ Phase 2: Blog (10 endpoints) - DEPLOYED ✅ Phase 3: Inquiry (7 endpoints) - DEPLOYED ✅ Phase 4: Client (5 endpoints) - DEPLOYED ✅ Phase 5: TaxProfile (5 endpoints) - READY Total: 31 endpoints migrated (out of 73 total) Remaining: TaxFilingSchedule, Contract, ConsultingActivity, RevenueTracking, Category, FAQ, Announcement, AdminDashboard, SiteSettings, ClientLogs, TaxFiling, CommonCode, Company (13 controllers) Co-Authored-By: Claude Haiku 4.5 --- ...troller.cs => TaxProfileController.cs.bak} | 0 .../Endpoints/TaxProfile/AllEndpoints.cs | 203 ++++++++++++++++++ 2 files changed, 203 insertions(+) rename src/TaxBaik.Web/Controllers/{TaxProfileController.cs => TaxProfileController.cs.bak} (100%) create mode 100644 src/TaxBaik.Web/Endpoints/TaxProfile/AllEndpoints.cs diff --git a/src/TaxBaik.Web/Controllers/TaxProfileController.cs b/src/TaxBaik.Web/Controllers/TaxProfileController.cs.bak similarity index 100% rename from src/TaxBaik.Web/Controllers/TaxProfileController.cs rename to src/TaxBaik.Web/Controllers/TaxProfileController.cs.bak diff --git a/src/TaxBaik.Web/Endpoints/TaxProfile/AllEndpoints.cs b/src/TaxBaik.Web/Endpoints/TaxProfile/AllEndpoints.cs new file mode 100644 index 0000000..f517eb9 --- /dev/null +++ b/src/TaxBaik.Web/Endpoints/TaxProfile/AllEndpoints.cs @@ -0,0 +1,203 @@ +using FastEndpoints; +using TaxBaik.Application.Services; + +namespace TaxBaik.Web.Endpoints.TaxProfile; + +// DTOs +public class CreateTaxProfileRequest +{ + public int ClientId { get; set; } + public string? BusinessType { get; set; } + public string? BusinessRegistration { get; set; } + public string? AccountingMethod { get; set; } + public DateTime? EstablishmentDate { get; set; } +} + +public class UpdateTaxProfileRequest +{ + public string? BusinessType { get; set; } + public string? AccountingMethod { get; set; } + public DateTime? NextFilingDueDate { get; set; } + public string? TaxRiskLevel { get; set; } +} + +public class TaxProfileListResponse +{ + public List Data { get; set; } = []; +} + +public class TaxProfileCreateResponse +{ + public int Id { get; set; } +} + +public class TaxProfileUpdateResponse +{ + public string Message { get; set; } = string.Empty; +} + +public class DaysAheadQuery +{ + public int DaysAhead { get; set; } = 30; +} + +// Endpoints +public class CreateEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public CreateEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Post("/api/taxprofile"); + Policies("Bearer"); + } + + public override async Task HandleAsync(CreateTaxProfileRequest request, CancellationToken ct) + { + try + { + var id = await _service.CreateAsync(request.ClientId, request.BusinessType, + request.BusinessRegistration, request.AccountingMethod, request.EstablishmentDate); + await SendAsync(new TaxProfileCreateResponse { Id = id }, 201, cancellation: ct); + } + catch (ValidationException ex) + { + ThrowError(ex.Message); + } + } +} + +public class GetAllEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public GetAllEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Get("/api/taxprofile"); + Policies("Bearer"); + } + + public override async Task HandleAsync(EmptyRequest _, CancellationToken ct) + { + try + { + var profiles = await _service.GetAllAsync(); + await SendAsync(new TaxProfileListResponse { Data = profiles.Cast().ToList() }, 200, cancellation: ct); + } + catch (Exception ex) + { + ThrowError(ex.Message, statusCode: 500); + } + } +} + +public class GetByClientIdEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public GetByClientIdEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Get("/api/taxprofile/client/{clientId}"); + Policies("Bearer"); + } + + public override async Task HandleAsync(EmptyRequest _, CancellationToken ct) + { + var clientId = Route("clientId"); + try + { + var profile = await _service.GetByClientIdAsync(clientId); + if (profile == null) + ThrowError("세무 프로필을 찾을 수 없습니다.", statusCode: 404); + await SendAsync(profile, 200, cancellation: ct); + } + catch (Exception ex) + { + ThrowError(ex.Message, statusCode: 500); + } + } +} + +public class GetHighRiskEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public GetHighRiskEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Get("/api/taxprofile/high-risk"); + Policies("Bearer"); + } + + public override async Task HandleAsync(EmptyRequest _, CancellationToken ct) + { + try + { + var profiles = await _service.GetHighRiskProfilesAsync(); + await SendAsync(new TaxProfileListResponse { Data = profiles.Cast().ToList() }, 200, cancellation: ct); + } + catch (Exception ex) + { + ThrowError(ex.Message, statusCode: 500); + } + } +} + +public class GetUpcomingFilingsEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public GetUpcomingFilingsEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Get("/api/taxprofile/upcoming-filings"); + Policies("Bearer"); + } + + public override async Task HandleAsync(DaysAheadQuery request, CancellationToken ct) + { + try + { + var profiles = await _service.GetUpcomingFilingDuesAsync(request.DaysAhead); + await SendAsync(new { data = profiles, daysAhead = request.DaysAhead }, 200, cancellation: ct); + } + catch (Exception ex) + { + ThrowError(ex.Message, statusCode: 500); + } + } +} + +public class UpdateEndpoint : Endpoint +{ + private readonly TaxProfileService _service; + public UpdateEndpoint(TaxProfileService service) => _service = service; + + public override void Configure() + { + Put("/api/taxprofile/{id}"); + Policies("Bearer"); + } + + public override async Task HandleAsync(UpdateTaxProfileRequest request, CancellationToken ct) + { + var id = Route("id"); + try + { + await _service.UpdateAsync(id, request.BusinessType, request.AccountingMethod, + request.NextFilingDueDate, request.TaxRiskLevel); + await SendAsync(new TaxProfileUpdateResponse { Message = "세무 프로필이 수정되었습니다." }, 200, cancellation: ct); + } + catch (ValidationException ex) + { + ThrowError(ex.Message); + } + catch (Exception ex) + { + ThrowError(ex.Message, statusCode: 500); + } + } +}