diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/entity/Answer.java b/src/main/java/com/hyetaekon/hyetaekon/answer/entity/Answer.java index a642751..6708558 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/entity/Answer.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/entity/Answer.java @@ -24,11 +24,11 @@ public class Answer { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 답변 ID - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; diff --git a/src/main/java/com/hyetaekon/hyetaekon/bookmark/entity/Bookmark.java b/src/main/java/com/hyetaekon/hyetaekon/bookmark/entity/Bookmark.java index 1721ad2..d0ccaeb 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/bookmark/entity/Bookmark.java +++ b/src/main/java/com/hyetaekon/hyetaekon/bookmark/entity/Bookmark.java @@ -21,12 +21,12 @@ public class Bookmark extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JsonIgnore @JoinColumn(name = "public_service_id", nullable = false) private PublicService publicService; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; diff --git a/src/main/java/com/hyetaekon/hyetaekon/comment/entity/Comment.java b/src/main/java/com/hyetaekon/hyetaekon/comment/entity/Comment.java index 2a40fe2..4f34fd4 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/comment/entity/Comment.java +++ b/src/main/java/com/hyetaekon/hyetaekon/comment/entity/Comment.java @@ -23,11 +23,11 @@ public class Comment { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/entity/Post.java b/src/main/java/com/hyetaekon/hyetaekon/post/entity/Post.java index 4a6de0d..26d1f78 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/entity/Post.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/entity/Post.java @@ -29,11 +29,11 @@ public class Post { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 게시글 ID - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "public_service_id") private PublicService publicService; @@ -83,13 +83,12 @@ public class Post { @Column(name = "suspend_at") private LocalDateTime suspendAt; - @Builder.Default @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private List postImages = new ArrayList<>(); // ✅ 게시글 이미지와 연결 - @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) @Builder.Default + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private List recommends = new ArrayList<>(); // 조회수 증가 diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostImage.java b/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostImage.java index ffc5702..b8ae7d1 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostImage.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostImage.java @@ -17,7 +17,7 @@ public class PostImage { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/repository/PostRepository.java b/src/main/java/com/hyetaekon/hyetaekon/post/repository/PostRepository.java index 589177b..4f1fcf8 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/repository/PostRepository.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/repository/PostRepository.java @@ -10,7 +10,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; import java.util.Optional; @Repository @@ -24,7 +23,8 @@ public interface PostRepository extends JpaRepository { // ID로 삭제되지 않은 게시글 조회 Optional findByIdAndDeletedAtIsNull(Long id); - boolean existsByUser_IdAndDeletedAtIsNull(Long userId); + // 특정 사용자가 특정 타입의 게시글을 작성한 적이 있는지 확인 + boolean existsByUser_IdAndPostTypeAndDeletedAtIsNull(Long userId, PostType postType); // 특정 사용자가 작성한 게시글 조회 @EntityGraph(attributePaths = {"user"}) @@ -35,7 +35,6 @@ public interface PostRepository extends JpaRepository { Page findMyPostsOptimized(@Param("userId") Long userId, Pageable pageable); // 특정 사용자가 추천한 게시글 조회 - @EntityGraph(attributePaths = {"user", "recommends"}) @Query("SELECT DISTINCT p FROM Post p " + "JOIN p.recommends r " + "WHERE r.user.id = :userId " + diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java b/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java index 1f5d829..8ae0181 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java @@ -130,12 +130,26 @@ public PostDetailResponseDto createPost(PostCreateRequestDto requestDto, Long us PostType postType = PostType.fromString(requestDto.getPostType()); log.info("Received postType: '{}'", requestDto.getPostType()); + // 포인트 부여 로직을 게시글 저장 전에 수행 + boolean isFirstGreetingPost = false; + try { + if (postType == PostType.GREETING) { + // 첫 인사 게시글 여부 확인 (게시글 저장 전에 체크) + isFirstGreetingPost = !postRepository.existsByUser_IdAndPostTypeAndDeletedAtIsNull(userId, PostType.GREETING); + log.info("인사 게시글 작성 체크 - 사용자 ID: {}, 첫 인사 게시글 여부: {}", userId, isFirstGreetingPost); + } + } catch (Exception e) { + log.error("포인트 부여 체크 중 오류 발생 - 사용자 ID: {}", userId, e); + } + + // 게시글 저장 Post post = postMapper.toEntity(requestDto); post.setUser(user); post.setPostType(postType); Post savedPost = postRepository.save(post); + // 이미지 처리 if (requestDto.getImages() != null && !requestDto.getImages().isEmpty()) { List postImages = processPostImages(requestDto.getImages(), savedPost); if (!postImages.isEmpty()) { @@ -144,9 +158,27 @@ public PostDetailResponseDto createPost(PostCreateRequestDto requestDto, Long us } } - // 게시글 작성 포인트 부여 - userPointService.addPointForAction(userId, PointActionType.POST_CREATION); - log.info("사용자 {}에게 게시글 작성 포인트가 부여되었습니다.", userId); + // 게시글 저장 후 포인트 부여 + try { + if (postType == PostType.GREETING) { + if (isFirstGreetingPost) { + // 첫 인사 게시글인 경우 100점 + userPointService.addPointForAction(userId, PointActionType.FIRST_GREETING_POST_CREATION); + log.info("첫 인사 게시글 작성 완료 - 사용자 ID: {}, 부여 포인트: 100점", userId); + } else { + // 추가 인사 게시글인 경우 20점 + userPointService.addPointForAction(userId, PointActionType.POST_CREATION); + log.info("추가 인사 게시글 작성 완료 - 사용자 ID: {}, 부여 포인트: 20점", userId); + } + } else { + // 일반 게시글인 경우 20점 + userPointService.addPointForAction(userId, PointActionType.POST_CREATION); + log.info("일반 게시글 작성 완료 - 사용자 ID: {}, 부여 포인트: 20점", userId); + } + } catch (Exception e) { + log.error("포인트 부여 중 오류 발생 - 사용자 ID: {}, 게시글 ID: {}", userId, savedPost.getId(), e); + // 포인트 부여 실패해도 게시글 생성은 성공으로 처리 + } return postMapper.toPostDetailDto(savedPost); } @@ -306,8 +338,6 @@ public Page getRecommendedPostsByUserId(Long userId, Page return postRepository.findRecommendedPostsOptimized(userId, pageable) .map(post -> { MyPostListResponseDto dto = postMapper.toMyPostListDto(post); - // 추천 개수는 이미 로드된 데이터에서 가져옴 (추가 쿼리 없음) - // post.getRecommends().size()는 이미 메모리에 있음 return dto; }); } diff --git a/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java b/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java index d6bc086..8fc9ef8 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java +++ b/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java @@ -21,12 +21,12 @@ public class Recommend extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne @JsonIgnore + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id", nullable = false) private Post post; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; } \ No newline at end of file diff --git a/src/main/java/com/hyetaekon/hyetaekon/recommend/repository/RecommendRepository.java b/src/main/java/com/hyetaekon/hyetaekon/recommend/repository/RecommendRepository.java index fe5dd34..ca5baa8 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/recommend/repository/RecommendRepository.java +++ b/src/main/java/com/hyetaekon/hyetaekon/recommend/repository/RecommendRepository.java @@ -12,5 +12,4 @@ public interface RecommendRepository extends JpaRepository { Optional findByUserIdAndPostId(Long userId, Long postId); - int countByPostId(Long postId); } diff --git a/src/main/java/com/hyetaekon/hyetaekon/user/entity/PointActionType.java b/src/main/java/com/hyetaekon/hyetaekon/user/entity/PointActionType.java index 2a07a24..9c02ed9 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/user/entity/PointActionType.java +++ b/src/main/java/com/hyetaekon/hyetaekon/user/entity/PointActionType.java @@ -5,7 +5,7 @@ @Getter public enum PointActionType { POST_CREATION(20), // 게시글 작성 (20점) - FIRST_POST_CREATION(100), // 첫 게시글 작성 (100점) + FIRST_GREETING_POST_CREATION(100), // 인사 게시글 작성 (100점) ANSWER_CREATION(10), // 답변 작성 (10점) ANSWER_ACCEPTED(50); // 답변이 채택됨 (50점) diff --git a/src/main/java/com/hyetaekon/hyetaekon/user/entity/User.java b/src/main/java/com/hyetaekon/hyetaekon/user/entity/User.java index 17f79d4..390c657 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/user/entity/User.java +++ b/src/main/java/com/hyetaekon/hyetaekon/user/entity/User.java @@ -18,6 +18,7 @@ @NoArgsConstructor @AllArgsConstructor public class User { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/com/hyetaekon/hyetaekon/user/repository/UserRepository.java b/src/main/java/com/hyetaekon/hyetaekon/user/repository/UserRepository.java index 2c7c20d..63a7de3 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/user/repository/UserRepository.java +++ b/src/main/java/com/hyetaekon/hyetaekon/user/repository/UserRepository.java @@ -3,6 +3,7 @@ import com.hyetaekon.hyetaekon.user.entity.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -16,6 +17,10 @@ public interface UserRepository extends JpaRepository { // user id로 검색 Optional findByIdAndDeletedAtIsNull(Long id); + @EntityGraph(attributePaths = {"interests"}) + @Query("SELECT u FROM User u WHERE u.id = :id AND u.deletedAt IS NULL") + Optional findByIdAndDeletedAtIsNullWithInterests(@Param("id") Long id); + // user realId로 검색 Optional findByRealIdAndDeletedAtIsNull(String realId); diff --git a/src/main/java/com/hyetaekon/hyetaekon/user/service/UserPointService.java b/src/main/java/com/hyetaekon/hyetaekon/user/service/UserPointService.java index 2dd70db..f55529d 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/user/service/UserPointService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/user/service/UserPointService.java @@ -2,6 +2,7 @@ import com.hyetaekon.hyetaekon.common.exception.ErrorCode; import com.hyetaekon.hyetaekon.common.exception.GlobalException; +import com.hyetaekon.hyetaekon.post.entity.PostType; import com.hyetaekon.hyetaekon.post.repository.PostRepository; import com.hyetaekon.hyetaekon.user.entity.PointActionType; import com.hyetaekon.hyetaekon.user.entity.User; @@ -17,7 +18,7 @@ public class UserPointService { private final UserRepository userRepository; private final UserLevelService userLevelService; - private final PostRepository postRepository; // 게시글 저장소 추가 필요 + private final PostRepository postRepository; @Transactional public void addPointForAction(Long userId, PointActionType actionType) { @@ -26,21 +27,20 @@ public void addPointForAction(Long userId, PointActionType actionType) { int pointToAdd = actionType.getPoints(); - // 게시글 작성인 경우 첫 게시글 여부 확인 - if (actionType == PointActionType.POST_CREATION) { - boolean isFirstPost = !postRepository.existsByUser_IdAndDeletedAtIsNull(userId); - if (isFirstPost) { - // 첫 게시글인 경우 FIRST_POST_CREATION 포인트 적용 - pointToAdd = PointActionType.FIRST_POST_CREATION.getPoints(); - log.info("사용자 {}의 첫 게시글 작성으로 {}점 획득", userId, pointToAdd); - } - } + // 현재 포인트 로깅 + log.info("포인트 부여 전 - 사용자 ID: {}, 현재 포인트: {}, 부여할 포인트: {}", + userId, user.getPoint(), pointToAdd); user.addPoint(pointToAdd); // 레벨 체크 및 업데이트 userLevelService.checkAndUpdateLevel(user); - userRepository.save(user); + User savedUser = userRepository.save(user); + + // 포인트 부여 후 로깅 + log.info("포인트 부여 후 - 사용자 ID: {}, 최종 포인트: {}, 레벨: {}", + userId, savedUser.getPoint(), savedUser.getLevel().getName()); } + } diff --git a/src/main/java/com/hyetaekon/hyetaekon/userInterest/entity/UserInterest.java b/src/main/java/com/hyetaekon/hyetaekon/userInterest/entity/UserInterest.java index b7ace16..fb696c2 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/userInterest/entity/UserInterest.java +++ b/src/main/java/com/hyetaekon/hyetaekon/userInterest/entity/UserInterest.java @@ -17,7 +17,7 @@ public class UserInterest { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; diff --git a/src/main/java/com/hyetaekon/hyetaekon/userInterest/repository/UserInterestRepository.java b/src/main/java/com/hyetaekon/hyetaekon/userInterest/repository/UserInterestRepository.java index adca3f3..7248ddc 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/userInterest/repository/UserInterestRepository.java +++ b/src/main/java/com/hyetaekon/hyetaekon/userInterest/repository/UserInterestRepository.java @@ -13,9 +13,5 @@ public interface UserInterestRepository extends JpaRepository findByUserId(Long userId); - /** - * 사용자 ID로 관심사 존재 여부 확인 - */ - boolean existsByUserId(Long userId); } diff --git a/src/main/java/com/hyetaekon/hyetaekon/userInterest/service/UserInterestService.java b/src/main/java/com/hyetaekon/hyetaekon/userInterest/service/UserInterestService.java index f22ab74..ff459c2 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/userInterest/service/UserInterestService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/userInterest/service/UserInterestService.java @@ -27,7 +27,7 @@ public class UserInterestService { // 모든 관심사 목록과 사용자 선택 여부 함께 조회 @Transactional(readOnly = true) public CategorizedInterestsWithSelectionDto getUserInterestsWithSelection(Long userId) { - User user = userRepository.findByIdAndDeletedAtIsNull(userId) + User user = userRepository.findByIdAndDeletedAtIsNullWithInterests(userId) .orElseThrow(() -> new GlobalException(ErrorCode.USER_NOT_FOUND_BY_ID)); // 사용자가 선택한 관심사 목록