diff --git a/TaxBaik.Web/Controllers/AnnouncementController.cs b/TaxBaik.Web/Controllers/AnnouncementController.cs new file mode 100644 index 0000000..382b156 --- /dev/null +++ b/TaxBaik.Web/Controllers/AnnouncementController.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using TaxBaik.Application.DTOs; +using TaxBaik.Application.Services; + +namespace TaxBaik.Web.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class AnnouncementController : ControllerBase +{ + private readonly AnnouncementService _announcementService; + + public AnnouncementController(AnnouncementService announcementService) + { + _announcementService = announcementService; + } + + [HttpGet("active")] + public async Task GetActive() + { + var announcements = await _announcementService.GetActiveAsync(); + return Ok(new { data = announcements }); + } + + [HttpGet] + [Authorize] + public async Task GetAll() + { + var announcements = await _announcementService.GetAllAsync(); + return Ok(new { data = announcements }); + } + + [HttpGet("{id}")] + [Authorize] + public async Task GetById(int id) + { + var announcement = await _announcementService.GetByIdAsync(id); + if (announcement == null) + return NotFound(new ProblemDetails { Title = "공지사항을 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound }); + + return Ok(announcement); + } + + [HttpPost] + [Authorize] + public async Task Create([FromBody] AnnouncementDto dto) + { + try + { + var announcementId = await _announcementService.CreateAsync(dto); + var result = await _announcementService.GetByIdAsync(announcementId); + return CreatedAtAction(nameof(GetById), new { id = announcementId }, result); + } + catch (Exception ex) + { + return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest }); + } + } + + [HttpPut("{id}")] + [Authorize] + public async Task Update(int id, [FromBody] AnnouncementDto dto) + { + dto.Id = id; + try + { + await _announcementService.UpdateAsync(dto); + var result = await _announcementService.GetByIdAsync(id); + if (result == null) + return NotFound(new ProblemDetails { Title = "공지사항을 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound }); + + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest }); + } + } + + [HttpDelete("{id}")] + [Authorize] + public async Task Delete(int id) + { + await _announcementService.DeleteAsync(id); + return NoContent(); + } +} diff --git a/TaxBaik.Web/Controllers/FaqController.cs b/TaxBaik.Web/Controllers/FaqController.cs new file mode 100644 index 0000000..b88cb8c --- /dev/null +++ b/TaxBaik.Web/Controllers/FaqController.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using TaxBaik.Application.Services; +using TaxBaik.Domain.Entities; + +namespace TaxBaik.Web.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class FaqController : ControllerBase +{ + private readonly FaqService _faqService; + + public FaqController(FaqService faqService) + { + _faqService = faqService; + } + + [HttpGet("active")] + public async Task GetActive() + { + var faqs = await _faqService.GetActiveAsync(); + return Ok(new { data = faqs }); + } + + [HttpGet] + [Authorize] + public async Task GetAll() + { + var faqs = await _faqService.GetAllAsync(); + return Ok(new { data = faqs }); + } + + [HttpGet("{id}")] + [Authorize] + public async Task GetById(int id) + { + var faq = await _faqService.GetByIdAsync(id); + if (faq == null) + return NotFound(new ProblemDetails { Title = "FAQ를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound }); + + return Ok(faq); + } + + [HttpPost] + [Authorize] + public async Task Create([FromBody] Faq faq) + { + try + { + var faqId = await _faqService.CreateAsync(faq); + var result = await _faqService.GetByIdAsync(faqId); + return CreatedAtAction(nameof(GetById), new { id = faqId }, result); + } + catch (Exception ex) + { + return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest }); + } + } + + [HttpPut("{id}")] + [Authorize] + public async Task Update(int id, [FromBody] Faq faq) + { + faq.Id = id; + try + { + await _faqService.UpdateAsync(faq); + var result = await _faqService.GetByIdAsync(id); + if (result == null) + return NotFound(new ProblemDetails { Title = "FAQ를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound }); + + return Ok(result); + } + catch (Exception ex) + { + return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest }); + } + } + + [HttpDelete("{id}")] + [Authorize] + public async Task Delete(int id) + { + await _faqService.DeleteAsync(id); + return NoContent(); + } +} diff --git a/TaxBaik.Web/Program.cs b/TaxBaik.Web/Program.cs index e80b998..b492bda 100644 --- a/TaxBaik.Web/Program.cs +++ b/TaxBaik.Web/Program.cs @@ -86,6 +86,16 @@ builder.Services.AddHttpClient(client }) .AddHttpMessageHandler(); builder.Services.AddHttpClient(client => +{ + client.BaseAddress = new Uri("http://localhost:5001/taxbaik/api/"); +}) + .AddHttpMessageHandler(); +builder.Services.AddHttpClient(client => +{ + client.BaseAddress = new Uri("http://localhost:5001/taxbaik/api/"); +}) + .AddHttpMessageHandler(); +builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri("http://localhost:5001/taxbaik/api/"); }) diff --git a/TaxBaik.Web/Services/AnnouncementBrowserClient.cs b/TaxBaik.Web/Services/AnnouncementBrowserClient.cs new file mode 100644 index 0000000..0b46980 --- /dev/null +++ b/TaxBaik.Web/Services/AnnouncementBrowserClient.cs @@ -0,0 +1,110 @@ +namespace TaxBaik.Web.Services; + +using System.Net.Http.Json; +using TaxBaik.Application.DTOs; +using TaxBaik.Domain.Entities; + +public interface IAnnouncementBrowserClient +{ + Task> GetAllAsync(CancellationToken ct = default); + Task GetByIdAsync(int id, CancellationToken ct = default); + Task CreateAsync(AnnouncementDto dto, CancellationToken ct = default); + Task UpdateAsync(int id, AnnouncementDto dto, CancellationToken ct = default); + Task DeleteAsync(int id, CancellationToken ct = default); +} + +public class AnnouncementBrowserClient : IAnnouncementBrowserClient +{ + private readonly HttpClient _http; + private readonly ILogger _logger; + + public AnnouncementBrowserClient(HttpClient http, ILogger logger) + { + _http = http; + _logger = logger; + } + + public async Task> GetAllAsync(CancellationToken ct = default) + { + try + { + var result = await _http.GetFromJsonAsync("announcement", cancellationToken: ct); + return result?.Data ?? []; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to fetch announcements"); + throw; + } + } + + public async Task GetByIdAsync(int id, CancellationToken ct = default) + { + try + { + return await _http.GetFromJsonAsync($"announcement/{id}", cancellationToken: ct); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to fetch announcement {AnnouncementId}", id); + throw; + } + } + + public async Task CreateAsync(AnnouncementDto dto, CancellationToken ct = default) + { + try + { + var response = await _http.PostAsJsonAsync("announcement", dto, cancellationToken: ct); + if (!response.IsSuccessStatusCode) return null; + + var content = await response.Content.ReadAsStringAsync(ct); + return System.Text.Json.JsonSerializer.Deserialize( + content, + new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to create announcement"); + throw; + } + } + + public async Task UpdateAsync(int id, AnnouncementDto dto, CancellationToken ct = default) + { + try + { + var response = await _http.PutAsJsonAsync($"announcement/{id}", dto, cancellationToken: ct); + if (!response.IsSuccessStatusCode) return null; + + var content = await response.Content.ReadAsStringAsync(ct); + return System.Text.Json.JsonSerializer.Deserialize( + content, + new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to update announcement {AnnouncementId}", id); + throw; + } + } + + public async Task DeleteAsync(int id, CancellationToken ct = default) + { + try + { + var response = await _http.DeleteAsync($"announcement/{id}", cancellationToken: ct); + return response.IsSuccessStatusCode; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to delete announcement {AnnouncementId}", id); + throw; + } + } + + private class AnnouncementListResponse + { + public List Data { get; set; } = []; + } +} diff --git a/TaxBaik.Web/Services/FaqBrowserClient.cs b/TaxBaik.Web/Services/FaqBrowserClient.cs new file mode 100644 index 0000000..6c31d99 --- /dev/null +++ b/TaxBaik.Web/Services/FaqBrowserClient.cs @@ -0,0 +1,109 @@ +namespace TaxBaik.Web.Services; + +using System.Net.Http.Json; +using TaxBaik.Domain.Entities; + +public interface IFaqBrowserClient +{ + Task> GetAllAsync(CancellationToken ct = default); + Task GetByIdAsync(int id, CancellationToken ct = default); + Task CreateAsync(Faq faq, CancellationToken ct = default); + Task UpdateAsync(int id, Faq faq, CancellationToken ct = default); + Task DeleteAsync(int id, CancellationToken ct = default); +} + +public class FaqBrowserClient : IFaqBrowserClient +{ + private readonly HttpClient _http; + private readonly ILogger _logger; + + public FaqBrowserClient(HttpClient http, ILogger logger) + { + _http = http; + _logger = logger; + } + + public async Task> GetAllAsync(CancellationToken ct = default) + { + try + { + var result = await _http.GetFromJsonAsync("faq", cancellationToken: ct); + return result?.Data ?? []; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to fetch FAQs"); + throw; + } + } + + public async Task GetByIdAsync(int id, CancellationToken ct = default) + { + try + { + return await _http.GetFromJsonAsync($"faq/{id}", cancellationToken: ct); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to fetch FAQ {FaqId}", id); + throw; + } + } + + public async Task CreateAsync(Faq faq, CancellationToken ct = default) + { + try + { + var response = await _http.PostAsJsonAsync("faq", faq, cancellationToken: ct); + if (!response.IsSuccessStatusCode) return null; + + var content = await response.Content.ReadAsStringAsync(ct); + return System.Text.Json.JsonSerializer.Deserialize( + content, + new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to create FAQ"); + throw; + } + } + + public async Task UpdateAsync(int id, Faq faq, CancellationToken ct = default) + { + try + { + var response = await _http.PutAsJsonAsync($"faq/{id}", faq, cancellationToken: ct); + if (!response.IsSuccessStatusCode) return null; + + var content = await response.Content.ReadAsStringAsync(ct); + return System.Text.Json.JsonSerializer.Deserialize( + content, + new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to update FAQ {FaqId}", id); + throw; + } + } + + public async Task DeleteAsync(int id, CancellationToken ct = default) + { + try + { + var response = await _http.DeleteAsync($"faq/{id}", cancellationToken: ct); + return response.IsSuccessStatusCode; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to delete FAQ {FaqId}", id); + throw; + } + } + + private class FaqListResponse + { + public List Data { get; set; } = []; + } +}