Files
taxbaik/TaxBaik.Web/Controllers/AdminDashboardController.cs
T
kjh2064 5053245575 feat: implement API-first architecture Phase 1 - Dashboard API
**Architecture Refactor (SOLID Principles):**
- Implement AdminDashboardController (REST API)
- Add dashboard summary endpoint
- Add upcoming filings endpoint
- Add recent inquiries endpoint
- Add monthly statistics endpoint

**Database Layer (Repository Pattern):**
- Extend IInquiryRepository with date range queries
- Implement CountByDateRangeAsync
- Implement CountByStatusAndDateAsync
- Extend InquiryRepository with new methods

**Service Layer (Single Responsibility):**
- Extend AdminDashboardService with API methods
- Add GetRecentInquiriesAsync
- Add GetMonthlyStatsAsync with caching

**Test Coverage:**
- Update FakeInquiryRepository mock with new methods

**SOLID Application:**
✓ Single Responsibility: Each class has one reason to change
✓ Open/Closed: Dashboard API can be extended without modifying existing code
✓ Dependency Inversion: Service depends on Repository abstraction
✓ Interface Segregation: API endpoints are focused and specific

Status: ✓ Compiles successfully (0 errors, 0 warnings)

Next phases:
- Add remaining API controllers (Announcement, Client, FAQ, TaxFiling)
- Refactor Blazor components to use API instead of services
- Implement JWT token refresh mechanism
- Add SignalR for change notifications

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 10:41:33 +09:00

123 lines
3.6 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TaxBaik.Application.Services;
namespace TaxBaik.Web.Controllers;
/// <summary>
/// 관리자 대시보드 API
/// SOLID: Single Responsibility - 대시보드 데이터만 담당
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class AdminDashboardController : ControllerBase
{
private readonly AdminDashboardService _dashboardService;
private readonly TaxFilingService _taxFilingService;
public AdminDashboardController(
AdminDashboardService dashboardService,
TaxFilingService taxFilingService)
{
_dashboardService = dashboardService;
_taxFilingService = taxFilingService;
}
/// <summary>
/// 대시보드 요약 정보 조회
/// GET /api/admin-dashboard/summary
/// </summary>
[HttpGet("summary")]
public async Task<IActionResult> GetSummary()
{
try
{
var summary = await _dashboardService.GetSummaryAsync();
return Ok(summary);
}
catch (Exception ex)
{
return StatusCode(500, new ProblemDetails
{
Title = "대시보드 요약 조회 실패",
Detail = ex.Message,
Status = StatusCodes.Status500InternalServerError
});
}
}
/// <summary>
/// 30일 이내 마감 임박 신고 조회
/// GET /api/admin-dashboard/upcoming-filings?days=30
/// </summary>
[HttpGet("upcoming-filings")]
public async Task<IActionResult> GetUpcomingFilings([FromQuery] int days = 30)
{
try
{
if (days <= 0) days = 30;
var filings = await _taxFilingService.GetUpcomingAsync(days);
return Ok(new { data = filings, days });
}
catch (Exception ex)
{
return StatusCode(500, new ProblemDetails
{
Title = "마감 임박 신고 조회 실패",
Detail = ex.Message,
Status = StatusCodes.Status500InternalServerError
});
}
}
/// <summary>
/// 최근 문의 조회
/// GET /api/admin-dashboard/recent-inquiries?limit=10
/// </summary>
[HttpGet("recent-inquiries")]
public async Task<IActionResult> GetRecentInquiries([FromQuery] int limit = 10)
{
try
{
if (limit <= 0) limit = 10;
if (limit > 100) limit = 100; // 보안: 최대 100개
var inquiries = await _dashboardService.GetRecentInquiriesAsync(limit);
return Ok(new { data = inquiries, limit });
}
catch (Exception ex)
{
return StatusCode(500, new ProblemDetails
{
Title = "최근 문의 조회 실패",
Detail = ex.Message,
Status = StatusCodes.Status500InternalServerError
});
}
}
/// <summary>
/// 월별 통계
/// GET /api/admin-dashboard/monthly-stats?month=2026-06
/// </summary>
[HttpGet("monthly-stats")]
public async Task<IActionResult> GetMonthlyStats([FromQuery] string? month = null)
{
try
{
var stats = await _dashboardService.GetMonthlyStatsAsync(month);
return Ok(stats);
}
catch (Exception ex)
{
return StatusCode(500, new ProblemDetails
{
Title = "월별 통계 조회 실패",
Detail = ex.Message,
Status = StatusCodes.Status500InternalServerError
});
}
}
}