/************************************************************ * * @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.util.List; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import com.github.pagehelper.PageInfo; import io.company.localhost.common.annotation.Member; import io.company.localhost.common.annotation.ParameterCheck; 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.common.exception.InvalidPasswordException; import io.company.localhost.common.exception.NotFoundHandler; import io.company.localhost.service.commoncodService; import io.company.localhost.service.localbordService; import io.company.localhost.utils.AuthUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @RestController @RequestMapping("/api/board") @RequiredArgsConstructor @Slf4j public class BoardController { private final localbordService boardService; private final commoncodService commoncodService; private final PasswordEncoder passwordEncoder; /** * 공지사항 목록 조회 * @ReqMap map 요청 파라미터 (searchKeyword) * @return 전체 공지사항 목록 */ @Member @ParameterCheck @GetMapping("/notices") public ApiResponse> getNotices(@ReqMap MapDto map) { return ApiResponse.ok(boardService.selectNotices(map)); } /** * 자유/익명 게시판 목록 조회 * @ReqMap map 요청 파라미터 (page, searchKeyword, orderBy, size) * @return 페이징된 자유/익명 게시판 목록 */ @Member @ParameterCheck @GetMapping("/general") public ApiResponse> getGeneralPosts(@ReqMap MapDto map) { return ApiResponse.ok(boardService.selectGeneralPosts(map)); } /** * 게시물 작성 * @ReqMap map 요청 파라미터 (LOCBRDTTL, LOCBRDCON, MEMBERSEQ, LOCBRDTYP, * LOCBRDPWD(익명일 때만), LOCBRDCAT(지식커뮤니티만)) * @return 작성된 게시물의 ID */ @Member @ParameterCheck @PostMapping public ApiResponse createBoard(@ReqMap MapDto map) { Long userId = AuthUtil.getUser().getId(); map.put("MEMBERSEQ", userId); if (map.containsKey("LOCBRDPWD") && !map.getString("LOCBRDPWD").trim().isEmpty()) { // 빈 값 체크 String rawPassword = map.getString("LOCBRDPWD"); String hashedPassword = passwordEncoder.encode(rawPassword); map.put("LOCBRDPWD", hashedPassword); } return ApiResponse.ok(boardService.insertBoard(map)); } /** * 게시물 상세보기 * @param boardId 게시물 ID * @return 게시물 상세정보 */ @Member @ParameterCheck @GetMapping("/{boardId}") public ApiResponse getBoardDetail(@PathVariable("boardId") Long boardId) { MapDto board = boardService.selectBoardDetail(boardId); if (board == null) { throw new NotFoundHandler("게시물 ID " + boardId + "을(를) 찾을 수 없습니다."); } return ApiResponse.ok(board); } /** * 게시물 삭제 * @ReqMap map 수정 데이터 (LOCBRDSEQ) * @return 삭제 결과 메시지 */ @Member @ParameterCheck @DeleteMapping("/{boardId}") public ApiResponse deleteBoard(@ReqMap MapDto map) { boardService.deleteBoard(map); return ApiResponse.ok("게시물이 삭제되었습니다."); } /** * 게시물 수정 * @ReqMap map 수정 데이터 (LOCBRDTTL, LOCBRDCON, LOCBRDSEQ) * @return 수정 결과 메시지 */ @Member @ParameterCheck @PutMapping("/{boardId}") public ApiResponse updateBoard(@ReqMap MapDto map) { boardService.updateBoard(map); return ApiResponse.ok("게시물이 수정되었습니다."); } /** * 첨부파일 추가 * @ReqMap map 요청 파라미터 (CMNFLEREG, CMNFLESIZ, CMNFLEEXT, CMNFLEORG, CMNFLENAM, CMNFLEPAT, CMNBRDSEQ) * @return 첨부파일 저장 결과 메시지 */ @Member @ParameterCheck @PostMapping("/{CMNBRDSEQ}/attachments") public ApiResponse uploadAttachment(@ReqMap MapDto map) { Long userId = AuthUtil.getUser().getId(); map.put("CMNFLEREG", userId); boardService.insertAttachment(map); return ApiResponse.ok("첨부파일이 저장되었습니다."); } /** * 게시물, 댓글 좋아요/싫어요 추가 * @ReqMap map 데이터 (LOCCMTSEQ, MEMBERSEQ, LOCGOBGOD, LOCGOBBAD, LOCBRDSEQ) * @return 반응 추가 결과 메시지 */ @Member @ParameterCheck @PostMapping("/{LOCBRDSEQ}/{LOCCMTSEQ}/reaction") public ApiResponse reactToBoard(@ReqMap MapDto map) { Long userId = AuthUtil.getUser().getId(); map.put("MEMBERSEQ", userId); boardService.procReactToBoard(map); return ApiResponse.ok("반응이 성공적으로 처리되었습니다."); } /** * 댓글/대댓글 조회 * @ReqMap map 수정 데이터 (LOCBRDSEQ) * @return 댓글과 대댓글의 계층 구조 데이터 */ @Member @ParameterCheck @GetMapping("/{boardId}/comments") public ApiResponse> getComments(@ReqMap MapDto map) { return ApiResponse.ok(boardService.selectComments(map)); } /** * 댓글/대댓글 작성 * @param boardId 게시물 ID * @ReqMap map 댓글 데이터 (LOCBRDSEQ, LOCCMTRPY, LOCCMTPNT, LOCCMTPWD, MEMBERSEQ ) * @return 작성 결과 메시지 */ @Member @ParameterCheck @PostMapping("/{LOCBRDSEQ}/comment") public ApiResponse addCommentOrReply(@ReqMap MapDto map) { Long userId = AuthUtil.getUser().getId(); map.put("MEMBERSEQ", userId); if (map.containsKey("LOCCMTPWD") && !map.getString("LOCCMTPWD").trim().isEmpty()) { // 빈 값 체크 String rawPassword = map.getString("LOCCMTPWD"); String hashedPassword = passwordEncoder.encode(rawPassword); map.put("LOCCMTPWD", hashedPassword); } boardService.insertCommentOrReply(map); return ApiResponse.ok("댓글 또는 대댓글이 작성되었습니다."); } /** * 댓글/대댓글 수정 * @param commentId 댓글 ID * @ReqMap map 수정 데이터 (LOCCMTSEQ, LOCCMTRPY) * @return 수정 결과 메시지 */ @Member @ParameterCheck @PutMapping("/comment/{commentId}") public ApiResponse updateComment(@ReqMap MapDto map) { boardService.updateComment(map); return ApiResponse.ok("댓글이 수정되었습니다."); } /** * 댓글/대댓글 삭제 * @ReqMap map 수정 데이터 (LOCCMTSEQ) * @return 삭제 결과 메시지 */ @Member @ParameterCheck @DeleteMapping("/comment/{commentId}") public ApiResponse deleteComment(@ReqMap MapDto map) { boardService.deleteComment(map); return ApiResponse.ok("댓글이 삭제되었습니다."); } /** * 게시물 비밀번호 확인 (해싱된 비밀번호 비교 적용) * @ReqMap map 수정 데이터 (LOCBRDSEQ, LOCBRDPWD) * @return 비밀번호 확인 결과 */ @Member @ParameterCheck @PostMapping("/{boardId}/password") public ApiResponse checkBoardPassword(@ReqMap MapDto map) { // boardId 데이터 타입 변환 Object boardIdObj = map.get("LOCBRDSEQ"); int boardId = 0; if (boardIdObj instanceof Integer) { boardId = (Integer) boardIdObj; } else if (boardIdObj instanceof String) { boardId = Integer.parseInt((String) boardIdObj); } String rawPassword = map.getString("LOCBRDPWD"); String storedHashedPassword = boardService.selectBoardPassword(boardId); if (storedHashedPassword == null) { throw new NotFoundHandler("해당 게시물이 존재하지 않습니다."); } boolean isMatch = passwordEncoder.matches(rawPassword, storedHashedPassword); if (!isMatch) { throw new InvalidPasswordException("비밀번호가 일치하지 않습니다."); } return ApiResponse.ok(true); } /** * 댓글 비밀번호 확인 (해싱된 비밀번호 비교 적용) * @ReqMap map 수정 데이터 (LOCCMTSEQ, LOCCMTPWD) * @return 비밀번호 확인 결과 */ @Member @ParameterCheck @PostMapping("/comment/{commentId}/password") public ApiResponse checkCommentPassword(@ReqMap MapDto map) { // commentId 데이터 타입 변환 Object commentIdObj = map.get("LOCCMTSEQ"); int commentId = 0; if (commentIdObj instanceof Integer) { commentId = (Integer) commentIdObj; } else if (commentIdObj instanceof String) { commentId = Integer.parseInt((String) commentIdObj); } String rawPassword = map.getString("LOCCMTPWD"); String storedHashedPassword = boardService.selectCommentPassword(commentId); if (storedHashedPassword == null) { throw new NotFoundHandler("해당 댓글이 존재하지 않습니다."); } boolean isMatch = passwordEncoder.matches(rawPassword, storedHashedPassword); if (!isMatch) { throw new InvalidPasswordException("비밀번호가 일치하지 않습니다."); } return ApiResponse.ok(true); } /** * 카테고리 목록 조회 * @return 카테고리 리스트 */ @GetMapping("/categories") public ApiResponse> getCategories() { List categories = commoncodService.selectCategoryList(); return ApiResponse.ok(categories); } }