package sgis.board.controller; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.time.OffsetDateTime; // OffsetDateTime import 추가 import java.time.format.DateTimeFormatter; import java.util.Map; import java.util.stream.Collectors; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; // ModelAttribute import는 다른 메서드를 위해 유지 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.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import egovframework.com.cmm.util.EgovUserDetailsHelper; import sgis.board.entity.Board; import sgis.board.entity.Member; import sgis.board.entity.Post; // Post entity import import sgis.board.mapper.BoardMapper; import sgis.board.mapper.MemberMapper; import sgis.board.mapper.PostMapper; import sgis.board.service.PostService; import sgis.com.vo.SessionVO; import sgis.com.web.BaseController; @RestController // @ResponseBody(JSON)응답 public class BoardRestController extends BaseController { @Autowired BoardMapper boardMapper; // 기존 Board 관련 로직을 사용하지 않는다면 제거 고려 @Autowired PostService postService; @Autowired MemberMapper memberMapper; // ObjectMapper 인스턴스 생성 private final ObjectMapper objectMapper = new ObjectMapper(); public BoardRestController() { // OffsetDateTime을 ISO 8601 형식으로 직렬화하기 위한 설정 objectMapper.findAndRegisterModules(); // Java 8 date/time modules 등록 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } /** * @Method_Name : getSessionInfo * @Date : 2022. 5. 10. * @Creator : ICTWAY KIM Yoon-Su * @Method_Discription : 세션정보 가져오기 * @return * @throws Exception */ public SessionVO getSessionInfo() throws Exception { return (SessionVO) EgovUserDetailsHelper.getAuthenticatedUser(); } // Post 객체를 Board 객체로 변환하는 헬퍼 메서드 private Board postToBoard(Post post) { Board board = new Board(); if( post == null ) { return board; } board.setIdx(post.getPostId() != null ? post.getPostId().intValue() : 0); board.setUserId(post.getUserNo() != null ? String.valueOf(post.getUserNo()) : null); // 답변글 제목 형식 변경 로직 if (post.getParentPostId() != null && post.getParentPostId() != 0) { // 부모 게시글 정보 조회 (부모 게시글의 제목을 가져오기 위함) Post parentPost = postService.getPostById(post.getParentPostId()); if (parentPost != null) { board.setTitle("" + parentPost.getTitle()); // "<상위글 제목>" 형식으로 설정 } else { board.setTitle(post.getTitle()); // 부모 게시글을 찾을 수 없으면 원본 제목 사용 } } else { board.setTitle(post.getTitle()); // 답변글이 아니면 원본 제목 사용 } board.setBoardContent(post.getContent()); board.setWriter(post.getUserNo() != null ? "User " + post.getUserNo() : "Unknown"); // 실제 사용자 이름을 가져오는 로직 필요 // Handle dates: OffsetDateTime to String (assuming 'yyyy-MM-dd HH:mm:ss' format for indate) if (post.getCreatedAt() != null) { // Checks if createdAt is not null board.setIndate(post.getCreatedAt().toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); // Formats createdAt to String } else { board.setIndate(null); // Sets indate to null if createdAt is null } board.setCount(post.getViewCount() != null ? post.getViewCount() : 0); // Maps viewCount to count, handling potential null // JSP의 Kendo Grid가 기대하는 project 관련 필드들 (Post 데이터에서 매핑 필요) board.setProjectName(post.getTitle()); // 예시: Post의 title을 projectName으로 매핑 board.setProjectHarborName(""); // Post에 직접적인 매핑이 없다면 빈 문자열 또는 다른 로직으로 채움 board.setProjectOrderingName(""); board.setProjectAffiliatedName(""); board.setProjectInputId(post.getUserNo() != null ? String.valueOf(post.getUserNo()) : null); // 예시: Post의 userId를 projectInputId로 매핑 // projectStartDate, projectEndDate도 Post의 createdAt, updatedAt에서 매핑 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); if (post.getCreatedAt() != null) { board.setProjectStartDate(post.getCreatedAt().format(formatter)); } else { board.setProjectStartDate(null); } if (post.getUpdatedAt() != null) { board.setProjectEndDate(post.getUpdatedAt().format(formatter)); } else { board.setProjectEndDate(null); } board.setUserNm(post.getUserNo() != null ? "User" + post.getUserNo() : "Unknown"); // userNm 매핑 (예시) board.setParentPostId(post.getParentPostId()); // parentPostId 매핑 추가 return board; } // @ResponseBody->jackson-databind(객체를->JSON 데이터포멧으로 변환) @RequestMapping(value = "/sgis/portal/board/all.do", produces = "application/json;charset=UTF-8") public String boardList(@RequestParam HashMap params, ModelMap model, Member m, HttpSession session) throws JsonProcessingException { String boardCategoryIdParam = (String) params.get("boardCategoryId"); // Get the parameter as String List posts; // 게시글 조회 if ("all".equalsIgnoreCase(boardCategoryIdParam) || boardCategoryIdParam == null || boardCategoryIdParam.isEmpty()) { posts = postService.getAllPosts(); // If "all" or null/empty, get all posts } else { try { Integer boardCategoryId = Integer.valueOf(boardCategoryIdParam); // Try to convert to Integer posts = postService.getPostsByBoardCategoryId(boardCategoryId); // Get posts by specific category } catch (NumberFormatException e) { System.err.println("Invalid boardCategoryId format: " + boardCategoryIdParam + ". Fetching all posts instead."); posts = postService.getAllPosts(); // Fallback to all posts if conversion fails } } // 게시글 정렬 로직 (부모-자식 관계에 따라 정렬) List boardListForJsp = new ArrayList<>(); // 1. 부모 게시글과 답변 게시글을 분리 Map> repliesMap = posts.stream() .filter(post -> post.getParentPostId() != null) .collect(Collectors.groupingBy(Post::getParentPostId, Collectors.toCollection(ArrayList::new))); List topLevelPosts = posts.stream() .filter(post -> post.getParentPostId() == null) .sorted(Comparator.comparing(Post::getCreatedAt).reversed()) // 최신글이 위로 오도록 정렬 .collect(Collectors.toList()); // 2. 부모 게시글 아래에 답변 게시글을 삽입하여 최종 정렬된 리스트 생성 for (Post topPost : topLevelPosts) { boardListForJsp.add(postToBoard(topPost)); // 부모 게시글 추가 // 해당 부모 게시글의 답변들을 가져와서 정렬 (예: 오래된 답변이 먼저 오도록) List currentReplies = repliesMap.get(topPost.getPostId()); if (currentReplies != null && !currentReplies.isEmpty()) { currentReplies.sort(Comparator.comparing(Post::getCreatedAt)); // 답변은 오래된 순서로 for (Post reply : currentReplies) { boardListForJsp.add(postToBoard(reply)); // 답변 게시글 추가 } } } HashMap responseMap = new HashMap<>(); responseMap.put("dataList1", boardListForJsp); return objectMapper.writeValueAsString(responseMap); // Returns the list of Board objects as JSON data } @PostMapping("/sgis/portal/board/new.do") @ResponseBody public Post createNewPost( @RequestParam("boardCategoryId") Integer boardCategoryId, @RequestParam("title") String title, @RequestParam("boardContent") String content, // Using @RequestParam(value = "parentPostId", required = false) for optionality. // If it comes as an empty string, it will be null. @RequestParam(value = "parentPostId", required = false) Long parentPostId // This is good ) { Post newPost = new Post(); newPost.setBoardCategoryId(boardCategoryId); newPost.setTitle(title); newPost.setContent(content); // parentPostId를 Post 객체에 설정합니다. (null일 수도 있음) newPost.setParentPostId(parentPostId); // userId를 세션에서 직접 가져와 Post 객체에 설정합니다. try { SessionVO sessionVO = getSessionInfo(); String userId = sessionVO.getsUserId(); if (sessionVO != null && sessionVO.getsUserId() != null) { Member m = memberMapper.registerCheck(userId); if( m!=null ) { newPost.setUserNo(m.getUserNo()); // userId는 String 타입으로 직접 설정 } } else { System.err.println("User ID not found in session for Post creation or session is null."); throw new IllegalStateException("User ID is required for post creation."); } } catch (Exception e) { System.err.println("Error getting session info for Post creation: " + e.getMessage()); throw new RuntimeException("Failed to get session user info.", e); } // 그 외 필드 (기본값 설정) newPost.setViewCount(0); // 새 글이므로 0 newPost.setStatus("active"); // 기본 상태 newPost.setCreatedAt(OffsetDateTime.now()); // 현재 시간으로 설정 newPost.setUpdatedAt(OffsetDateTime.now()); // 현재 시간으로 설정 // PostService를 통해 게시글 생성 Post createdPost = postService.createPost(newPost); // 생성된 Post 객체 반환 (클라이언트에게 성공 여부 및 생성된 게시글 정보 전달) return createdPost; } @DeleteMapping("/sgis/portal/board/delete{idx}.do") public void boardDelete(@PathVariable("idx") int idx) { postService.deletePost(Long.valueOf(idx)); // PostService의 deletePost 호출 } @PutMapping("/sgis/portal/board/update.do") public void boardUpdate(@RequestBody Board vo) { // 1. Get the postId from the incoming Board object Long postId = Long.valueOf(vo.getIdx()); // 2. Fetch the existing Post from the database Post existingPost = postService.getPostById(postId); if (existingPost == null) { // Handle case where the post to update is not found. // You might want to throw a specific exception or return an error response. System.err.println("Post with ID " + postId + " not found for update."); // Example: throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Post not found"); return; // Or return an appropriate ResponseEntity } // 3. Update only the fields that are provided in the request body // The JSP sends title and boardContent, which map to title and content in Post. existingPost.setTitle(vo.getTitle()); existingPost.setContent(vo.getBoardContent()); existingPost.setUpdatedAt(OffsetDateTime.now()); // Update the modification timestamp // Other fields like boardCategoryId, parentPostId, userNo, viewCount, status // will retain their original values from 'existingPost' as they are not // explicitly updated from the 'Board vo' in this method. // 4. Call PostService to update the post with the modified existingPost object postService.updatePost(existingPost); } @GetMapping("/sgis/portal/board/{idx}.do") public Board boardContent(@RequestParam HashMap params, @PathVariable("idx") int idx) { Post post = postService.getPostById(Long.valueOf(idx) ); // PostService를 통해 Post 조회 Board vo = postToBoard(post); // Post 객체를 Board 객체로 변환 return vo; } @PutMapping("/sgis/portal/board/count/{idx}.do") public Board boardCount(@PathVariable("idx") int idx) { Post post = postService.getPostById(Long.valueOf(idx)); // getPostById 호출 시 조회수 자동 증가 Board vo = postToBoard(post); return vo; } }