feat: migrate InquiryController to FastEndpoints Endpoints (Phase 3)
TaxBaik CI/CD / build-and-deploy (push) Successful in 1m28s
TaxBaik CI/CD / build-and-deploy (push) Successful in 1m28s
IMPLEMENTATION:
- Create 7 FastEndpoints Endpoint classes for Inquiry API:
- SubmitEndpoint: POST /api/inquiry (public)
- GetPagedEndpoint: GET /api/inquiry (auth, paginated)
- GetByIdEndpoint: GET /api/inquiry/{id} (auth)
- UpdateStatusEndpoint: PUT /api/inquiry/{id}/status (auth)
- UpdateAdminMemoEndpoint: PUT /api/inquiry/{id}/memo (auth)
- UpdateEndpoint: PUT /api/inquiry/{id} (auth)
- ConvertToClientEndpoint: POST /api/inquiry/{id}/convert-to-client (auth)
- Create InquiryDtos.cs with shared response types:
- InquiryQuery (query parameters)
- InquiryPagedResponse (paginated response)
- UpdateStatusRequest, UpdateAdminMemoRequest, ConvertToClientRequest
- ConvertToClientResponse, MessageResponse
- Backup InquiryController.cs (no longer active)
VERIFICATION:
✅ dotnet build: 0 errors, 0 warnings
✅ dotnet test: 26/26 passed
✅ Local service publish successful
✅ FastEndpoints auto-discovery working
✅ All 21 endpoints verified (Auth 4 + Blog 10 + Inquiry 7)
MIGRATION PROGRESS:
✅ Phase 1: Auth (4 endpoints) - DEPLOYED
✅ Phase 2: Blog (10 endpoints) - DEPLOYED
✅ Phase 3: Inquiry (7 endpoints) - READY FOR DEPLOYMENT
Next: Deploy Phase 3, then continue with remaining 13 Controllers
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System.Security.Claims;
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class ConvertToClientEndpoint : Endpoint<ConvertToClientRequest, ConvertToClientResponse>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
private readonly ClientService _clientService;
|
||||
|
||||
public ConvertToClientEndpoint(InquiryService inquiryService, ClientService clientService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
_clientService = clientService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/api/inquiry/{id}/convert-to-client");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(ConvertToClientRequest request, CancellationToken ct)
|
||||
{
|
||||
var id = Route<int>("id");
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
{
|
||||
ThrowError("문의를 찾을 수 없습니다.", statusCode: 404);
|
||||
}
|
||||
|
||||
if (inquiry.ClientId != null)
|
||||
{
|
||||
ThrowError("이미 고객 카드가 연결되어 있습니다.", statusCode: 400);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var clientId = await _clientService.CreateFromInquiryAsync(
|
||||
request.Name ?? inquiry.Name,
|
||||
request.Phone ?? inquiry.Phone,
|
||||
request.ServiceType ?? inquiry.ServiceType);
|
||||
|
||||
await _inquiryService.LinkClientAsync(inquiry.Id, clientId);
|
||||
await _inquiryService.UpdateStatusAsync(inquiry.Id, "consulting", User.FindFirstValue(ClaimTypes.Name) ?? "system");
|
||||
|
||||
await SendAsync(new ConvertToClientResponse
|
||||
{
|
||||
ClientId = clientId,
|
||||
Message = "고객 카드가 생성되었습니다."
|
||||
}, 200, cancellation: ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ThrowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class GetByIdEndpoint : Endpoint<EmptyRequest, object>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public GetByIdEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/api/inquiry/{id}");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(EmptyRequest _, CancellationToken ct)
|
||||
{
|
||||
var id = Route<int>("id");
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
{
|
||||
ThrowError("문의를 찾을 수 없습니다.", statusCode: 404);
|
||||
}
|
||||
|
||||
await SendAsync(inquiry, 200, cancellation: ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class GetPagedEndpoint : Endpoint<InquiryQuery, InquiryPagedResponse>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public GetPagedEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/api/inquiry");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(InquiryQuery request, CancellationToken ct)
|
||||
{
|
||||
var (inquiries, total) = await _inquiryService.GetPagedAsync(request.Page, request.PageSize);
|
||||
await SendAsync(new InquiryPagedResponse
|
||||
{
|
||||
Data = inquiries.Cast<object>().ToList(),
|
||||
Total = total,
|
||||
Page = request.Page,
|
||||
PageSize = request.PageSize
|
||||
}, 200, cancellation: ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using TaxBaik.Application.DTOs;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class InquiryPagedResponse
|
||||
{
|
||||
public List<object> Data { get; set; } = [];
|
||||
public int Total { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class InquiryQuery
|
||||
{
|
||||
public int Page { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
|
||||
public class UpdateStatusRequest
|
||||
{
|
||||
public string Status { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UpdateAdminMemoRequest
|
||||
{
|
||||
public string? AdminMemo { get; set; }
|
||||
}
|
||||
|
||||
public class ConvertToClientRequest
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? ServiceType { get; set; }
|
||||
}
|
||||
|
||||
public class ConvertToClientResponse
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class MessageResponse
|
||||
{
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class SubmitEndpoint : Endpoint<SubmitInquiryDto, MessageResponse>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public SubmitEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/api/inquiry");
|
||||
AllowAnonymous();
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(SubmitInquiryDto request, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.Phone))
|
||||
{
|
||||
ThrowError("이름과 전화번호를 입력하세요.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _inquiryService.SubmitAsync(
|
||||
request.Name,
|
||||
request.Phone,
|
||||
request.ServiceType,
|
||||
request.Message,
|
||||
request.Email,
|
||||
HttpContext.Connection.RemoteIpAddress?.ToString(),
|
||||
request.SuppressNotification);
|
||||
await SendAsync(new MessageResponse { Message = "상담 신청이 접수되었습니다." }, 200, cancellation: ct);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
ThrowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class UpdateAdminMemoEndpoint : Endpoint<UpdateAdminMemoRequest, MessageResponse>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public UpdateAdminMemoEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Put("/api/inquiry/{id}/memo");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UpdateAdminMemoRequest request, CancellationToken ct)
|
||||
{
|
||||
var id = Route<int>("id");
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
{
|
||||
ThrowError("문의를 찾을 수 없습니다.", statusCode: 404);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _inquiryService.UpdateAdminMemoAsync(id, request.AdminMemo);
|
||||
await SendAsync(new MessageResponse { Message = "메모가 저장되었습니다." }, 200, cancellation: ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ThrowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class UpdateEndpoint : Endpoint<UpdateInquiryDto, object>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public UpdateEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Put("/api/inquiry/{id}");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UpdateInquiryDto request, CancellationToken ct)
|
||||
{
|
||||
var id = Route<int>("id");
|
||||
try
|
||||
{
|
||||
var result = await _inquiryService.UpdateAsync(id, request);
|
||||
if (result == null)
|
||||
{
|
||||
ThrowError("문의를 찾을 수 없습니다.", statusCode: 404);
|
||||
}
|
||||
|
||||
await SendAsync(result, 200, cancellation: ct);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
ThrowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Security.Claims;
|
||||
using FastEndpoints;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.Inquiry;
|
||||
|
||||
public class UpdateStatusEndpoint : Endpoint<UpdateStatusRequest, MessageResponse>
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public UpdateStatusEndpoint(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Put("/api/inquiry/{id}/status");
|
||||
Policies("Bearer");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UpdateStatusRequest request, CancellationToken ct)
|
||||
{
|
||||
var id = Route<int>("id");
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
{
|
||||
ThrowError("문의를 찾을 수 없습니다.", statusCode: 404);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var changedBy = User.FindFirstValue(ClaimTypes.Name) ?? User.Identity?.Name;
|
||||
await _inquiryService.UpdateStatusAsync(id, request.Status, changedBy);
|
||||
await SendAsync(new MessageResponse { Message = "상태가 변경되었습니다." }, 200, cancellation: ct);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
ThrowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user