using System.Diagnostics; using bitforum.Models; using bitforum.Models.Page.Faq; using bitforum.Models.Views; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace bitforum.Controllers.Page { [Authorize] [Route("Page")] public class CategoryController : Controller { private readonly ILogger _logger; private readonly DefaultDbContext _db; private readonly string _ViewPath = "~/Views/Page/Faq/Index.cshtml"; public CategoryController(ILogger logger, DefaultDbContext db) { _logger = logger; _db = db; } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } [HttpGet("Faq")] public IActionResult Index() { var viewModel = new FaqCategoryViewModel { FaqCategories = _db.FaqCategory .Include(c => c.FaqItem).OrderByDescending(c => c.Order).ToList() }; ViewBag.ItemCounts = viewModel.FaqCategories.ToDictionary( category => category.ID, category => category.FaqItem.Count ); return View(_ViewPath, viewModel); } [HttpPost] public async Task Save(FaqCategoryViewModel request) { using var transaction = await _db.Database.BeginTransactionAsync(); try { if (request.FaqCategories == null || !request.FaqCategories.Any()) { throw new Exception("저장할 데이터가 없습니다."); } if (!ModelState.IsValid) { throw new Exception("유효성 검사에 실패하였습니다."); } var requestIds = request.FaqCategories.Select(x => x.ID).ToList(); // 요청 데이터의 ID 목록 var existingIds = await _db.FaqCategory.Select(c => c.ID).ToListAsync(); // 데이터베이스에 존재하는 ID 목록 var idsToDelete = existingIds.Except(requestIds).ToList(); // 삭제 대상 ID: 요청 데이터에 없는 항목 // 삭제 대상 항목 제거 if (idsToDelete.Any()) { var rr = await _db.FaqCategory.Where(c => idsToDelete.Contains(c.ID)).ToListAsync(); _db.FaqCategory.RemoveRange( rr ); } foreach (var row in request.FaqCategories) { // 중복 확인 if (await _db.FaqCategory.AnyAsync(c => c.Code == row.Code && c.ID != row.ID)) { throw new Exception($"이미 존재하는 Code 주소입니다: {row.Code}"); } if (row.ID == 0) { row.UpdatedAt = null; row.CreatedAt = DateTime.Now; await _db.FaqCategory.AddAsync(row); } else { var existing = await _db.FaqCategory.FirstOrDefaultAsync(c => c.ID == row.ID); if (existing == null) { throw new Exception($"ID {row.ID}에 해당하는 데이터가 없습니다."); } // 기존 엔터티 업데이트 existing.Code = row.Code; existing.Subject = row.Subject; existing.Order = row.Order; existing.IsActive = row.IsActive; existing.UpdatedAt = DateTime.Now; _db.FaqCategory.Update(existing); } } int affectedRows = await _db.SaveChangesAsync(); if (affectedRows <= 0) { throw new Exception("FAQ 저장 중 오류가 발생했습니다."); } await transaction.CommitAsync(); string message = "FAQ 분류가 정상적으로 저장되었습니다."; TempData["SuccessMessage"] = message; _logger.LogInformation(message); return RedirectToAction("Index"); } catch (DbUpdateException ex) { await transaction.RollbackAsync(); _logger.LogError(ex, ex.InnerException?.Message ?? ex.Message); throw new Exception("데이터 저장 중 오류 발생: " + (ex.InnerException?.Message ?? ex.Message)); } catch (Exception e) { await transaction.RollbackAsync(); _logger.LogError(e, e.Message); TempData["ErrorMessages"] = e.Message; return View(_ViewPath, request); } } } }