ItemController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. using System.Diagnostics;
  2. using Microsoft.AspNetCore.Authorization;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.EntityFrameworkCore;
  5. using Microsoft.AspNetCore.Mvc.Rendering;
  6. using Microsoft.AspNetCore.Mvc.Filters;
  7. using Microsoft.AspNetCore.WebUtilities;
  8. using bitforum.Models;
  9. using bitforum.Models.Page.Faq;
  10. using bitforum.Helpers;
  11. using bitforum.Services;
  12. using bitforum.Repository;
  13. using bitforum.Constants;
  14. namespace bitforum.Controllers.Page.Faq
  15. {
  16. [Authorize]
  17. [Route("Page/Faq")]
  18. public class ItemController : Controller
  19. {
  20. private readonly ILogger<ItemController> _logger;
  21. private readonly IFileUploadService _fileUploadService;
  22. private readonly IRedisRepository _redisRepository;
  23. private readonly DefaultDbContext _db;
  24. private readonly string _IndexViewPath = "~/Views/Page/Faq/Item/Index.cshtml";
  25. private readonly string _WriteViewPath = "~/Views/Page/Faq/Item/Write.cshtml";
  26. private readonly string _EditViewPath = "~/Views/Page/Faq/Item/Edit.cshtml";
  27. private string? _queryString = null;
  28. public ItemController(ILogger<ItemController> logger, IFileUploadService fileUploadService, IRedisRepository redisRepository, DefaultDbContext db)
  29. {
  30. _logger = logger;
  31. _fileUploadService = fileUploadService;
  32. _redisRepository = redisRepository;
  33. _db = db;
  34. }
  35. [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
  36. public IActionResult Error()
  37. {
  38. return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
  39. }
  40. public override void OnActionExecuting(ActionExecutingContext context)
  41. {
  42. ViewBag.QueryString = _queryString = string.Join("&", QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).Select(r => $"{r.Key}={r.Value.FirstOrDefault()}"));
  43. base.OnActionExecuting(context);
  44. }
  45. [HttpGet("Item/{categoryID?}")]
  46. public IActionResult Index(int? categoryID, [FromQuery] int page = 1)
  47. {
  48. ViewBag.CategoryID = categoryID;
  49. // 분류 목록
  50. ViewBag.FaqCategories = _db.FaqCategory.Include(c => c.FaqItem).Where(c => c.IsActive).OrderBy(c => c.Order).ToList();
  51. // FAQ 목록
  52. var faqItemQuery = _db.FaqItem.Include(c => c.FaqCategory).OrderBy(c => c.Order).AsQueryable();
  53. if (categoryID.HasValue)
  54. {
  55. faqItemQuery = faqItemQuery.Where(c => c.CategoryID == categoryID);
  56. }
  57. var faqItems = faqItemQuery.ToList();
  58. var data = new List<object>();
  59. if (faqItems.Count > 0)
  60. {
  61. foreach (var row in faqItems)
  62. {
  63. var entry = new
  64. {
  65. row.ID,
  66. CategoryName = row.FaqCategory.Subject,
  67. row.Question,
  68. row.Answer,
  69. row.Order,
  70. IsActive = (row.IsActive ? 'Y' : 'N'),
  71. Views = row.Views.ToString("N0"),
  72. UpdatedAt = row.UpdatedAt.GetDateAt(),
  73. CreatedAt = row.CreatedAt.GetDateAt(),
  74. EditURL = $"/Page/Faq/Item/{row.ID}/Edit?{_queryString}",
  75. DeleteURL = $"/Page/Faq/Item/{row.ID}/Delete?{_queryString}"
  76. };
  77. data.Add(entry);
  78. }
  79. }
  80. ViewBag.Data = data;
  81. ViewBag.Total = (data?.Count ?? 0);
  82. ViewBag.Pagination = new Pagination(ViewBag.Total, page, 20, null);
  83. return View(_IndexViewPath);
  84. }
  85. [HttpGet("Item/Write")]
  86. public IActionResult Write()
  87. {
  88. ViewBag.FaqCategory = _db.FaqCategory.Where(c => c.IsActive).OrderBy(c => c.Order).ToList();
  89. return View(_WriteViewPath);
  90. }
  91. [HttpPost("Item/Create")]
  92. public async Task<IActionResult> Create(FaqItem request)
  93. {
  94. try
  95. {
  96. if (!ModelState.IsValid)
  97. {
  98. throw new Exception("유효성 검사에 실패하였습니다.");
  99. }
  100. if (request.CategoryID <= 0 || !_db.FaqCategory.Any(c => c.ID == request.CategoryID))
  101. {
  102. throw new Exception("유효한 분류를 선택하세요.");
  103. }
  104. request.Answer = _fileUploadService.UploadEditorAsync(request.Answer, null, UploadFolder.Faq).Result;
  105. request.UpdatedAt = null;
  106. request.CreatedAt = DateTime.UtcNow;
  107. _db.FaqItem.Add(request);
  108. int affectedRows = await _db.SaveChangesAsync();
  109. if (affectedRows <= 0)
  110. {
  111. throw new Exception("FAQ 등록 중 오류가 발생했습니다.");
  112. }
  113. await _redisRepository.DeleteAsync(RedisConst.FaqKey);
  114. string message = "FAQ가 등록되었습니다.";
  115. TempData["SuccessMessage"] = message;
  116. _logger.LogInformation(message);
  117. return Redirect($"/Page/Faq/Item?{_queryString}");
  118. }
  119. catch (Exception e)
  120. {
  121. TempData["ErrorMessages"] = e.Message;
  122. _logger.LogError(e, e.Message);
  123. return Write();
  124. }
  125. }
  126. [HttpGet("Item/{id}/Edit")]
  127. public async Task<IActionResult> Edit(int id)
  128. {
  129. try
  130. {
  131. if (id <= 0)
  132. {
  133. throw new Exception("유효하지 않은 접근입니다.");
  134. }
  135. var faqItem = await _db.FaqItem.FirstAsync(c => c.ID == id);
  136. if (faqItem is null)
  137. {
  138. throw new Exception("FAQ 정보를 찾을 수 없습니다.");
  139. }
  140. // 분류 목록
  141. ViewBag.FaqCategory = new SelectList(_db.FaqCategory.Where(c => c.IsActive).OrderBy(c => c.Order).ToList(), "ID", "Subject", faqItem.CategoryID);
  142. return View(_EditViewPath, faqItem);
  143. }
  144. catch (Exception e)
  145. {
  146. TempData["ErrorMessages"] = e.Message;
  147. _logger.LogError(e, e.Message);
  148. return Redirect($"/Page/Faq/Item?{_queryString}");
  149. }
  150. }
  151. [HttpPost("Item/Update")]
  152. public async Task<IActionResult> Update(FaqItem request)
  153. {
  154. try
  155. {
  156. if (!ModelState.IsValid)
  157. {
  158. throw new Exception("유효성 검사에 실패하였습니다.");
  159. }
  160. if (request.CategoryID <= 0 || !_db.FaqCategory.Any(c => c.ID == request.CategoryID))
  161. {
  162. throw new Exception("유효한 분류를 선택하세요.");
  163. }
  164. var faqItem = await _db.FaqItem.FirstAsync(c => c.ID == request.ID);
  165. if (faqItem is null)
  166. {
  167. throw new Exception("FAQ 정보를 찾을 수 없습니다.");
  168. }
  169. faqItem.CategoryID = request.CategoryID;
  170. faqItem.Question = request.Question;
  171. faqItem.Answer = _fileUploadService.UploadEditorAsync(request.Answer, faqItem.Answer, UploadFolder.Faq).Result;
  172. faqItem.IsActive = request.IsActive;
  173. faqItem.UpdatedAt = DateTime.UtcNow;
  174. int affectedRows = await _db.SaveChangesAsync();
  175. if (affectedRows <= 0)
  176. {
  177. throw new Exception("FAQ 수정 중 오류가 발생했습니다.");
  178. }
  179. await _redisRepository.DeleteAsync(RedisConst.FaqKey);
  180. string message = "FAQ가 수정되었습니다.";
  181. TempData["SuccessMessage"] = message;
  182. _logger.LogInformation(message);
  183. }
  184. catch (Exception e)
  185. {
  186. TempData["ErrorMessages"] = e.Message;
  187. _logger.LogError(e, e.Message);
  188. }
  189. return await Edit(request.ID);
  190. }
  191. [HttpGet("Item/{id}/Delete")]
  192. public async Task<IActionResult> Delete(int id)
  193. {
  194. try
  195. {
  196. if (id <= 0)
  197. {
  198. throw new Exception("유효하지 않은 접근입니다.");
  199. }
  200. var faqItem = await _db.FaqItem.FindAsync(id);
  201. if (faqItem == null)
  202. {
  203. throw new Exception("FAQ 정보를 찾을 수 없습니다.");
  204. }
  205. _db.FaqItem.Remove(faqItem);
  206. int affectedRows = await _db.SaveChangesAsync();
  207. if (affectedRows <= 0)
  208. {
  209. throw new Exception("FAQ 삭제 중 오류가 발생했습니다.");
  210. }
  211. await _fileUploadService.CleanUpEditorImagesAsync(faqItem.Answer);
  212. await _redisRepository.DeleteAsync(RedisConst.FaqKey);
  213. string message = "FAQ가 삭제되었습니다.";
  214. TempData["SuccessMessage"] = message;
  215. _logger.LogInformation(message);
  216. }
  217. catch (Exception e)
  218. {
  219. TempData["ErrorMessages"] = e.Message;
  220. _logger.LogError(e, e.Message);
  221. }
  222. return Redirect($"/Page/Faq/Item?{_queryString}");
  223. }
  224. [HttpPost("Item/Delete")]
  225. public async Task<IActionResult> Delete([FromForm] int[] ids)
  226. {
  227. try
  228. {
  229. if (ids == null || ids.Length <= 0)
  230. {
  231. throw new Exception("유효하지 않은 접근입니다.");
  232. }
  233. foreach (var id in ids)
  234. {
  235. var faqItem = await _db.FaqItem.FindAsync(id);
  236. if (faqItem == null)
  237. {
  238. throw new Exception($"{id}번 FAQ 정보를 찾을 수 없습니다.");
  239. }
  240. _db.FaqItem.Remove(faqItem);
  241. int affectedRows = await _db.SaveChangesAsync();
  242. if (affectedRows <= 0)
  243. {
  244. throw new Exception($"{id}번 FAQ 삭제 중 오류가 발생했습니다.");
  245. }
  246. await _fileUploadService.CleanUpEditorImagesAsync(faqItem.Answer);
  247. await _redisRepository.DeleteAsync(RedisConst.FaqKey);
  248. }
  249. string message = $"{ids.Length}건의 FAQ가 삭제되었습니다.";
  250. TempData["SuccessMessage"] = message;
  251. _logger.LogInformation(message);
  252. }
  253. catch (Exception e)
  254. {
  255. TempData["ErrorMessages"] = e.Message;
  256. _logger.LogError(e, e.Message);
  257. }
  258. return Redirect($"/Page/Faq/Item?{_queryString}");
  259. }
  260. }
  261. }