package io.company.localhost.service; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.UUID; import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import io.company.localhost.common.dto.MapDto; import io.company.localhost.mapper.localbordMapper; import io.company.localhost.utils.AuthUtil; import io.company.localhost.utils.PageUtil; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class localbordService { private final localbordMapper boardMapper; private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB public List selectNotices(MapDto map) { List posts = boardMapper.selectNotices(map); enrichPostsWithAdditionalData(posts); return posts; } public PageInfo selectGeneralPosts(MapDto map) { System.out.println(map); int page = map.getString("page") != null ? Integer.parseInt(map.getString("page")) : 1; int size = map.getString("size") != null ? Integer.parseInt(map.getString("size")) : 10; String orderBy = map.getString("orderBy"); if (orderBy == null || (!orderBy.equals("date") && !orderBy.equals("views"))) { map.put("orderBy", "date"); } PageHelper.startPage(page, size); List result = boardMapper.selectGeneralPosts(map); enrichPostsWithAdditionalData(result); return PageUtil.redefineNavigation(new PageInfo<>(result, size)); } public void updateIncrementViewCount(Long boardId) { boardMapper.updateIncrementViewCount(boardId); } public BigInteger insertBoard(MapDto map) { // 익명게시판이면 회원 정보를 null로 설정 if ("300102".equals(String.valueOf(map.get("LOCBRDTYP")))) { map.put("MEMBERSEQ", null); }else { Long userId = AuthUtil.getUser().getId(); map.put("MEMBERSEQ", userId); } boardMapper.insertBoard(map); return (BigInteger) map.get("LOCBRDSEQ"); } public void insertAttachment(MapDto map) { String boardSeqStr = (String) map.get("CMNBRDSEQ"); Long boardSeq = Long.parseLong(boardSeqStr); map.put("CMNBRDSEQ", boardSeq); String newFilename = UUID.randomUUID().toString(); map.put("CMNFLENAM", newFilename); boardMapper.insertAttachment(map); } public void validateAttachmentsSize(List attachments) { long totalSize = attachments.stream() .mapToLong(attachment -> (Long) attachment.get("size")) .sum(); if (totalSize > MAX_FILE_SIZE) { throw new IllegalArgumentException("첨부파일의 총 용량이 5MB를 초과합니다."); } } public MapDto selectBoardDetail(Long boardId) { updateIncrementViewCount(boardId); MapDto boardDetail = boardMapper.selectBoardDetail(boardId); enrichBoardDetail(boardDetail); return boardDetail; } public List selectAttachments(Long boardId) { return boardMapper.selectAttachments(boardId); } public void deleteBoard(MapDto map) { boardMapper.deleteCommentsByBoardId(map); boardMapper.deleteBoard(map); } public void updateBoard(MapDto map) { boardMapper.updateBoard(map); } public void procReactToBoard(MapDto map) { MapDto existingReaction = boardMapper.selectReaction(map); if (existingReaction != null) { boardMapper.updateReaction(map); } else { boardMapper.insertReaction(map); } } public PageInfo selectComments(MapDto map) { int page = map.getString("page") != null ? Integer.parseInt(map.getString("page")) : 1; int size = map.getString("size") != null ? Integer.parseInt(map.getString("size")) : 10; PageHelper.startPage(page, size); List result = boardMapper.selectComments(map); enrichCommentsWithAdditionalData(result); // 댓글 데이터 보강 return PageUtil.redefineNavigation(new PageInfo<>(result, size)); } public List selectReply(MapDto map) { return boardMapper.selectReply(map); } public void insertCommentOrReply(MapDto map) { if ("300102".equals(String.valueOf(map.get("LOCBRDTYP")))) { map.put("MEMBERSEQ", null); }else { Long userId = AuthUtil.getUser().getId(); map.put("MEMBERSEQ", userId); } boardMapper.insertCommentOrReply(map); } public void updateComment(MapDto map) { boardMapper.updateComment(map); } public void deleteComment(MapDto map) { boolean isReply = boardMapper.selectIsReply(map); if (isReply) { boardMapper.deleteReply(map); } else { boolean hasReplies = boardMapper.selectHasReplies(map); if (hasReplies) { boardMapper.updateSoftDeleteComment(map); } else { boardMapper.deleteComment(map); } } } public String selectCommentPassword(int commentId) { return boardMapper.selectCommentPassword(commentId); } public String selectBoardPassword(int boardId) { return boardMapper.selectBoardPassword(boardId); } public MapDto selectCommentById(int commentId) { return boardMapper.selectCommentById(commentId); } public int selectCountComments(Long boardId) { return boardMapper.selectCountComments(boardId); } public boolean selectIsAttachments(Long boardId) { int count = boardMapper.selectIsAttachments(boardId); return count > 0; } public MapDto selectCountBoardReactions(Long boardId) { return boardMapper.selectCountBoardReactions(boardId); } public MapDto selectCountCommentReactions(Long boardId) { return boardMapper.selectCountCommentReactions(boardId); } private String procBlobToString(Object blob) { try { if (blob instanceof String) { return (String) blob; // 이미 문자열이면 그대로 반환 } else if (blob instanceof java.sql.Blob) { java.sql.Blob sqlBlob = (java.sql.Blob) blob; long blobLength = sqlBlob.length(); byte[] blobBytes = sqlBlob.getBytes(1, (int) blobLength); return new String(blobBytes, StandardCharsets.UTF_8); // SQL BLOB → 바이트 배열 → 문자열 변환 } else { throw new UnsupportedOperationException("Unsupported blob type: " + blob.getClass()); // 지원되지 않는 타입이면 예외 발생 } } catch (Exception e) { throw new RuntimeException("Failed to convert Blob to String: " + e.getMessage(), e); // 변환 실패 시 예외 처리 } } private String procFirstImageUrl(String jsonContent) { try { // JSON 유효성 검사 if (!procIsValidJson(jsonContent)) { throw new IllegalArgumentException("Invalid JSON content: " + jsonContent); } ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(jsonContent); // JSON 배열 순회 for (JsonNode node : rootNode) { JsonNode insertNode = node.get("insert"); if (insertNode != null && insertNode.has("image")) { return insertNode.get("image").asText(); // 첫 번째 이미지 URL 반환 } } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Failed to extract first image URL: " + e.getMessage(), e); } return null; // 이미지가 없는 경우 } private boolean procIsValidJson(String json) { try { final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.readTree(json); // JSON 파싱 시도 return true; // JSON이 유효하면 true 반환 } catch (Exception e) { return false; // 유효하지 않은 경우 false 반환 } } private String procPlainTextFromJson(String jsonContent) { StringBuilder plainTextBuilder = new StringBuilder(); try { ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(jsonContent); // JSON 배열인지 확인 if (!rootNode.isArray()) { System.err.println("JSON content is not an array"); return ""; } // JSON 노드 순회 for (JsonNode node : rootNode) { JsonNode insertNode = node.get("insert"); // insert 노드가 텍스트인지 확인 if (insertNode != null && insertNode.isTextual()) { String text = insertNode.asText(); // '\n' 제거하고 순수 텍스트만 추가 if (!text.trim().isEmpty() && !text.trim().equals("\n")) { plainTextBuilder.append(text.trim()); } } } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Failed to extract plain text: " + e.getMessage(), e); } return plainTextBuilder.toString(); } private void enrichBoardDetail(MapDto boardDetail) { long boardId = ((Number) boardDetail.get("id")).longValue(); boardDetail.put("hasAttachment", selectIsAttachments(boardId)); boardDetail.put("commentCount", selectCountComments(boardId)); MapDto reactions = selectCountBoardReactions(boardId); boardDetail.put("likeCount", reactions.getOrDefault("likeCount", 0)); boardDetail.put("dislikeCount", reactions.getOrDefault("dislikeCount", 0)); // Blob 데이터를 문자열로 변환 Object content = boardDetail.get("content"); if (content != null) { String contentString = procBlobToString(content); // Blob을 문자열로 변환 boardDetail.put("content", contentString); // JSON 변환 가능 } } private void enrichPostsWithAdditionalData(List posts) { for (MapDto post : posts) { Object idObject = post.get("id"); if (idObject instanceof Number) { long postId = ((Number) idObject).longValue(); post.put("commentCount", selectCountComments(postId)); post.put("hasAttachment", selectAttachments(postId)); MapDto reactions = selectCountBoardReactions(postId); post.put("likeCount", reactions.getOrDefault("likeCount", 0)); post.put("dislikeCount", reactions.getOrDefault("dislikeCount", 0)); Object content = post.get("content"); String contentString = procBlobToString(content); post.put("content", contentString); String firstImageUrl = procFirstImageUrl(contentString); post.put("firstImageUrl", firstImageUrl); String plainContent = procPlainTextFromJson(contentString); post.put("plainContent", plainContent); } } } private void enrichCommentsWithAdditionalData(List comments) { for (MapDto comment : comments) { Object idObject = comment.get("LOCCMTSEQ"); if (idObject instanceof Number) { long commentId = ((Number) idObject).longValue(); MapDto reactions = boardMapper.selectCountCommentReactions(commentId); comment.put("likeCount", reactions != null ? reactions.getOrDefault("likeCount", 0) : 0); comment.put("dislikeCount", reactions != null ? reactions.getOrDefault("dislikeCount", 0) : 0); Object content = comment.get("content"); comment.put("content", content); } } } }