/************************************************************ * * @packageName : io.company.localhost.controller.api * @fileName : BoardController.java * @author : 서지희 * @date : 25.01.07 * @description : 게시판 * * =========================================================== * DATE AUTHOR NOTE * ----------------------------------------------------------- * 24.01.02 서지희 최초 생성 * *************************************************************/ package io.company.localhost.controller.api; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.sql.Blob; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageSerializable; import io.company.localhost.common.annotation.ReqMap; import io.company.localhost.common.dto.ApiResponse; import io.company.localhost.common.dto.MapDto; import io.company.localhost.service.LocalBordService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @RestController @RequestMapping("/api/board") @RequiredArgsConstructor @Slf4j public class BoardController { private final LocalBordService boardService; /** * 공지사항 목록 조회 * @ReqMap map 요청 파라미터 (searchKeyword) * @return 페이징된 공지사항 목록 */ @GetMapping("/notices") public ApiResponse> getNotices(@ReqMap MapDto map) { List posts = boardService.getNotices(map); // Blob 데이터를 처리 (문자열로 변환) for (MapDto post : posts) { Object content = post.get("content"); if (content instanceof Blob) { Blob blob = (Blob) content; post.put("content", safeBlobToString(blob)); } } return ApiResponse.ok(posts); } /** * 자유/익명 게시판 목록 조회 * @ReqMap map 요청 파라미터 (page, size, searchKeyword 포함) * @return 페이징된 자유/익명 게시판 목록 */ @GetMapping("/general") public ApiResponse> getGeneralPosts(@ReqMap MapDto map) { PageInfo posts = boardService.getGeneralPosts(map); // Blob 데이터를 처리 (문자열로 변환) for (MapDto post : posts.getList()) { Object content = post.get("content"); if (content instanceof Blob) { Blob blob = (Blob) content; post.put("content", safeBlobToString(blob)); } } return ApiResponse.ok(posts); } /** * 안전하게 Blob 데이터를 문자열로 변환하는 메서드 * @ReqMap * @return 변환된 문자열 또는 null */ private String safeBlobToString(Blob blob) { if (blob == null) { return null; // Blob이 null이면 null 반환 } try { long blobLength = blob.length(); if (blobLength > Integer.MAX_VALUE) { throw new IllegalArgumentException("Blob is too large to process."); } return new String(blob.getBytes(1, (int) blobLength), StandardCharsets.UTF_8); } catch (Exception e) { System.err.println("Failed to convert Blob to String: " + e.getMessage()); e.printStackTrace(); return null; // 변환 실패 시 null 반환 } } /** * 게시물 작성 * @ReqMap map 요청 파라미터 (page, size, searchKeyword 포함) * @return 작성된 게시물의 ID */ @PostMapping public ApiResponse createBoard(@ReqMap MapDto map) { BigInteger createdIdx = boardService.createBoard(map); // 작성된 게시물의 idx를 반환 Map responseData = new HashMap<>(); responseData.put("boardId", createdIdx); responseData.put("message", "게시물이 작성되었습니다."); return ApiResponse.ok(responseData); } /** * 첨부파일 추가 * @param boardId 게시물 ID * @param file 업로드된 파일 * @param filePath 파일 저장 경로 * @param originalFileName 원본 파일명 * @param fileExtension 파일 확장자 * @param fileSize 파일 크기 * @param registrantId 등록자 ID * @param fileName 저장될 파일명 * @return 첨부파일 저장 결과 메시지 */ @PostMapping("/{boardId}/attachments") public ApiResponse uploadAttachment( @PathVariable("boardId") Long boardId, @RequestParam("file") MultipartFile file, @RequestParam("CMNFLEPAT") String filePath, @RequestParam("CMNFLEORG") String originalFileName, @RequestParam("CMNFLEEXT") String fileExtension, @RequestParam("CMNFLESIZ") Long fileSize, @RequestParam("CMNFLEREG") Long registrantId, @RequestParam("CMNFLENAM") String fileName ) { // 데이터 준비 MapDto fileData = new MapDto(); fileData.put("CMNBRDSEQ", boardId); fileData.put("CMNFLENAM", fileName); // 업로드된 파일 이름 fileData.put("CMNFLEORG", originalFileName); fileData.put("CMNFLEPAT", filePath); fileData.put("CMNFLEEXT", fileExtension); fileData.put("CMNFLESIZ", fileSize); fileData.put("CMNFLEREG", registrantId); fileData.put("CMNFLERDT", new Date()); // 등록일 현재 시간 log.info("Uploading attachment for board ID: {}", boardId); // 파일 데이터 저장 boardService.addAttachment(fileData); return ApiResponse.ok("첨부파일이 저장되었습니다."); } /** * 게시물 상세보기 * @param boardId 게시물 ID * @return 게시물 상세정보, 첨부파일 목록, 댓글 목록 */ @GetMapping("/{boardId}") public ApiResponse getBoardDetail(@PathVariable("boardId") Long boardId) { log.info("Fetching details for board ID: {}", boardId); // 게시물 상세정보 조회 MapDto boardDetail = boardService.getBoardDetail(boardId); if (boardDetail == null) { throw new IllegalArgumentException("Board not found for ID: " + boardId); } // Blob 데이터를 문자열로 변환 if (boardDetail.get("content") instanceof Blob) { try { Blob blob = (Blob) boardDetail.get("content"); String contentString = new String(blob.getBytes(1, (int) blob.length()), StandardCharsets.UTF_8); boardDetail.put("content", contentString); } catch (Exception e) { throw new RuntimeException("Failed to process Blob content", e); } } // 첨부파일 및 댓글 조회 List attachments = boardService.getAttachments(boardId); List comments = boardService.getComments(boardId.intValue()); // 결과 조합 MapDto result = new MapDto(); result.put("boardDetail", boardDetail); result.put("attachments", attachments); result.put("comments", comments); return ApiResponse.ok(result); } /** * 게시물 삭제 * @param boardId 게시물 ID * @return 삭제 결과 메시지 */ @DeleteMapping("/{boardId}") public ApiResponse deleteBoard(@PathVariable("boardId") Long boardId, @ReqMap MapDto map) { map.put("LOCBRDSEQ", boardId); log.info("Deleting board with ID: {}", boardId); boardService.deleteBoard(map); return ApiResponse.ok("게시물이 삭제되었습니다."); } /** * 게시물 수정 * @param boardId 게시물 ID * @ReqMap map 수정 데이터 (제목, 내용 등) * @return 수정 결과 메시지 */ @PutMapping("/{boardId}") public ApiResponse updateBoard(@PathVariable("boardId") Long boardId,@ReqMap MapDto map) { map.put("LOCBRDSEQ", boardId); boardService.updateBoard(map); return ApiResponse.ok("게시물이 수정되었습니다."); } /** * 게시물 좋아요/싫어요 추가 * @param boardId 게시물 ID * @ReqMap map 좋아요/싫어요 데이터 * @return 반응 추가 결과 메시지 */ @PostMapping("/{boardId}/reaction") public ApiResponse reactToBoard(@PathVariable("boardId") Long boardId, @ReqMap MapDto map) { map.put("LOCBRDSEQ", boardId); boardService.reactToBoard(map); return ApiResponse.ok("반응이 추가되었습니다."); } /** * 댓글/대댓글 조회 * @param boardId 게시물 ID * @return 댓글과 대댓글의 계층 구조 데이터 */ @GetMapping("/{boardId}/comments") public ApiResponse>> getComments(@PathVariable("boardId") int boardId) { // 모든 댓글과 대댓글 조회 List comments = boardService.getComments(boardId); // 댓글과 대댓글을 계층 구조로 정렬 Map> commentMap = new HashMap<>(); for (MapDto comment : comments) { int commentId = (int) comment.get("LOCCMTSEQ"); Integer parentId = (Integer) comment.get("LOCCMTRPY"); Map commentData = new HashMap<>(comment); commentData.put("replies", new ArrayList<>()); // 대댓글 리스트 초기화 if (parentId == null) { // 댓글인 경우 commentMap.put(commentId, commentData); } else { // 대댓글인 경우, 부모 댓글의 "replies"에 추가 Map parentComment = commentMap.get(parentId); if (parentComment != null) { List> replies = (List>) parentComment.get("replies"); replies.add(commentData); } } } // 최상위 댓글 리스트 반환 List> result = new ArrayList<>(commentMap.values()); return ApiResponse.ok(result); } /** * 댓글/대댓글 작성 * @param boardId 게시물 ID * @ReqMap map 댓글 데이터 (내용, 부모 ID 등) * @return 작성 결과 메시지 */ @PostMapping("/{boardId}/comment") public ApiResponse addCommentOrReply(@PathVariable("boardId") int boardId, @ReqMap MapDto map) { map.put("LOCBRDSEQ", boardId); // 부모 댓글 확인 Integer parentCommentId = (Integer) map.get("LOCCMTRPY"); if (parentCommentId != null) { MapDto parentComment = boardService.getCommentById(parentCommentId); if (parentComment == null) { throw new IllegalArgumentException("Invalid parent comment ID: " + parentCommentId); } } boardService.addCommentOrReply(map); return ApiResponse.ok("댓글 또는 대댓글이 작성되었습니다."); } /** * 댓글/대댓글 수정 * @param commentId 댓글 ID * @ReqMap map 수정 데이터 (내용, 비밀번호 등) * @return 수정 결과 메시지 */ @PutMapping("/comment/{commentId}") public ApiResponse updateComment(@PathVariable("commentId") int commentId, @ReqMap MapDto map) { // 기존 댓글 조회 MapDto existingComment = boardService.getCommentById(commentId); if (existingComment == null) { throw new IllegalArgumentException("Comment not found for ID: " + commentId); } // 부모 댓글 ID는 수정 불가 map.remove("LOCCMTRPY"); map.put("LOCCMTSEQ", commentId); boardService.updateComment(map); return ApiResponse.ok("댓글이 수정되었습니다."); } /** * 댓글/대댓글 삭제 * @param commentId 댓글 ID * @ReqMap map 요청 데이터 (비밀번호 등) * @return 삭제 결과 메시지 */ @DeleteMapping("/comment/{commentId}") public ApiResponse deleteComment(@PathVariable("commentId") int commentId, @ReqMap MapDto map) { map.put("LOCCMTSEQ", commentId); boardService.deleteComment(map); return ApiResponse.ok("댓글이 삭제되었습니다."); } /** * 댓글 비밀번호 확인 * @param commentId 댓글 ID * @ReqMap map 비밀번호 데이터 * @return 비밀번호 확인 결과 */ @PostMapping("/comment/{commentId}/password") public ApiResponse checkCommentPassword(@PathVariable("commentId") int commentId, @ReqMap MapDto map) { map.put("LOCCMTSEQ", commentId); return ApiResponse.ok(boardService.checkCommentPassword(map)); } /** * 게시물 비밀번호 확인 * @param boardId 게시물 ID * @ReqMap map 비밀번호 데이터 * @return 비밀번호 확인 결과 */ @PostMapping("/{boardId}/password") public ApiResponse checkBoardPassword(@PathVariable("boardId") int boardId, @ReqMap MapDto map) { map.put("LOCBRDSEQ", boardId); return ApiResponse.ok(boardService.checkBoardPassword(map)); } /** * 비밀게시판 여부 확인 * @param boardId 게시물 ID * @return 비밀게시판 여부 */ @GetMapping("/{boardId}/isSecret") public ApiResponse isSecretBoard(@PathVariable("boardId") Long boardId) { log.info("Checking if board ID {} is secret", boardId); return ApiResponse.ok(boardService.isSecretBoard(boardId)); } /** * 댓글 좋아요/싫어요 추가 * @param commentId 댓글 ID * @ReqMap map 좋아요/싫어요 데이터 * @return 반응 추가 결과 메시지 */ @PostMapping("/comment/{commentId}/reaction") public ApiResponse reactToComment(@PathVariable("commentId") int commentId, @ReqMap MapDto map) { map.put("LOCCMTSEQ", commentId); boardService.reactToComment(map); return ApiResponse.ok("Comment reaction added."); } }