feat: 블로그 시즌 연동 — 홈페이지 세무 정보 섹션 시즌화

- TaxSeason / CurrentSeasonDto에 RelatedCategorySlug 추가
- TaxSeasonCalendar 각 시즌에 카테고리 슬러그 매핑
  (income-tax→income-tax, vat-1st/2nd→vat, 종부세→real-estate-tax 등)
- IBlogPostRepository.GetByCategorySlugAsync 추가
- BlogService.GetSeasonalPostsAsync: 시즌 관련 글 2개 우선 + 나머지 최신 글로 채움
- IndexModel: SeasonalPosts / RecentPosts 분리 로드
- Index.cshtml 블로그 섹션: 시즌 중 "이번 시즌 추천" 배지 + 시즌별 전체보기 버튼
- site.css: blog-card--seasonal, seasonal-blog-tag, btn-seasonal 스타일

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 23:11:45 +09:00
parent 1ad720afe6
commit 46951d871a
11 changed files with 187 additions and 24 deletions
@@ -12,6 +12,19 @@ public class BlogService(IBlogPostRepository repository, IMemoryCache memoryCach
public async Task<BlogPost?> GetBySlugAsync(string slug, CancellationToken ct = default) =>
await repository.GetBySlugAsync(slug, ct);
/// <summary>카테고리 슬러그 기준 시즌 관련 글 조회. 부족 시 최신 글로 채워 total개 반환.</summary>
public async Task<(IEnumerable<BlogPost> Seasonal, IEnumerable<BlogPost> Latest)> GetSeasonalPostsAsync(
string categorySlug, int seasonalCount, int totalCount, CancellationToken ct = default)
{
var seasonal = (await repository.GetByCategorySlugAsync(categorySlug, seasonalCount, ct)).ToList();
var seasonalIds = seasonal.Select(p => p.Id).ToHashSet();
var (latestAll, _) = await repository.GetPublishedPagedAsync(1, totalCount + seasonalCount, null, ct);
var latest = latestAll.Where(p => !seasonalIds.Contains(p.Id)).Take(totalCount - seasonal.Count).ToList();
return (seasonal, latest);
}
public async Task<(IEnumerable<BlogPost>, int)> GetPublishedPagedAsync(
int page, int pageSize, int? categoryId = null, CancellationToken ct = default) =>
await repository.GetPublishedPagedAsync(NormalizePage(page), NormalizePageSize(pageSize), categoryId, ct);