Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ dependencies {

//
implementation 'org.springframework.boot:spring-boot-starter-actuator'

//hibernate
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
}
Comment on lines +92 to 95
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🛠️ Refactor suggestion

🧩 Analysis chain

JPA 스타터 중복 및 하이버네이트 개별 의존성 수동 지정은 제거 권장

  • spring-boot-starter-data-jpa 가 이미 Line 28에 선언되어 있습니다. 중복 선언은 버전 충돌/중복 의존성 증가를 유발할 수 있습니다.
  • hibernate-commons-annotations는 Boot BOM이 관리합니다. 특별한 이슈가 없다면 수동 버전 고정은 지양하세요.
-    //hibernate
-    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
-    implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'

중복/충돌 가능성을 빠르게 점검하려면:

추가 정리 예시(참고용): com.mysql:mysql-connector-j 하나만 유지하고, mysql:mysql-connector-java는 제거하는 구성이 최신 권장입니다.

dependencies {
  runtimeOnly 'com.mysql:mysql-connector-j'
  // mysql:mysql-connector-java 는 제거
}

🏁 Script executed:

#!/bin/bash
# JPA 스타터 중복 여부
rg -n "spring-boot-starter-data-jpa" build.gradle -n

# MySQL 커넥터 중복/혼재 여부
rg -n "(com\.mysql:mysql-connector-j|mysql:mysql-connector-java)" build.gradle -n

Length of output: 494


🏁 Script executed:

#!/bin/bash
# 하이버네이트 수동 버전 지정 위치 확인
rg -n "hibernate-commons-annotations" build.gradle -n

Length of output: 137


중복 및 불필요 의존성 제거 필요

아래 사항들을 반영해 빌드 스크립트에서 중복·불필요 의존성을 제거해주세요.

  • spring-boot-starter-data-jpa 중복
    • 28줄에 이미 선언된 JPA 스타터를 93줄(또는 마지막 deps 블록)에서 다시 선언하고 있습니다. 중복 선언은 버전 충돌 및 빌드 속도 저하를 유발하므로 제거가 필요합니다.
  • hibernate-commons-annotations 수동 버전 고정
    • 94줄의 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final 은 Spring Boot BOM이 관리하는 항목입니다. 특별한 이슈가 없다면 수동 버전 고정 대신 BOM에 위임하세요.
  • MySQL 커넥터 중복/혼재
    • 37줄에 com.mysql:mysql-connector-j 를 선언하면서
    • 38, 62, 78줄에 mysql:mysql-connector-java:8.0.33 을 여러 차례 선언하고 있습니다.
    runtimeOnly 'com.mysql:mysql-connector-j' 하나만 남기고 나머지는 모두 제거하세요.
 dependencies {
-   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
-   implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
-   runtimeOnly 'mysql:mysql-connector-java:8.0.33'
-   implementation 'mysql:mysql-connector-java:8.0.33'
-   implementation 'mysql:mysql-connector-java:8.0.33'
+   // spring-boot-starter-data-jpa, hibernate-commons-annotations: BOM 관리
+   // mysql-connector-java: com.mysql:mysql-connector-j 로 통일
    runtimeOnly 'com.mysql:mysql-connector-j'
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//hibernate
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
}
dependencies {
// spring-boot-starter-data-jpa, hibernate-commons-annotations: BOM 관리
// mysql-connector-java: com.mysql:mysql-connector-j 로 통일
runtimeOnly 'com.mysql:mysql-connector-j'
}



Expand Down
28 changes: 24 additions & 4 deletions src/main/java/stackpot/stackpot/event/SseService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
import stackpot.stackpot.chat.service.chatroom.ChatRoomQueryService;
import stackpot.stackpot.chat.service.chatroominfo.ChatRoomInfoQueryService;
import stackpot.stackpot.common.util.AuthService;
import stackpot.stackpot.notification.event.FeedCommentEvent;
import stackpot.stackpot.notification.event.FeedLikeEvent;
import stackpot.stackpot.notification.event.PotApplicationEvent;
import stackpot.stackpot.notification.event.PotCommentEvent;
import stackpot.stackpot.notification.event.*;
import stackpot.stackpot.pot.dto.UserMemberIdDto;
import stackpot.stackpot.pot.service.potMember.PotMemberQueryService;

Expand All @@ -34,6 +31,7 @@
@Slf4j
public class SseService {

// user 마다 SseEmitter 객체 저장
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();

private final ChatRoomQueryService chatRoomQueryService;
Expand Down Expand Up @@ -173,6 +171,28 @@ public void sendFeedLikeNotification(FeedLikeEvent event) {
}
}

/**
* PotEndNotification
* 팟을 끓인 후 팟 멤버들에게 실시간 알림 전송
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendPotEndNotification(PotEndEvent event) {
Long potId = event.getPotId();
List<Long> userIds = potMemberQueryService.selectUserIdsAboutPotMembersByPotId(potId);
for (Long userId : userIds) {
SseEmitter emitter = userId != null ? emitters.get(userId) : null;
if (emitter != null) {
try {
emitter.send(SseEmitter.event()
.name("Notification")
.data(event.getUnReadNotificationDto(), MediaType.APPLICATION_JSON));
} catch (Exception e) {
emitters.remove(userId);
}
}
}
}

private ChatRoomResponseDto.ChatRoomListDto createChatRoomListDto(UserMemberIdDto ids) {
Long potMemberId = ids.getPotMemberId();
Long potId = ids.getPotId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ List<Feed> findFeeds(
// 커서 기반 페이징 조회
List<Feed> findByUserIdAndFeedIdBefore(Long userId, Long cursorFeedId, Pageable pageable);

// 시리즈 필터링 추가된 페이징 조회
List<Feed> findByUser_IdAndSeries_SeriesId(Long userId, Long seriesId, Pageable pageable);
List<Feed> findByUser_IdAndSeries_SeriesIdAndFeedIdBefore(Long userId, Long seriesId, Long cursorFeedId, Pageable pageable);

@Modifying
@Query("DELETE FROM Feed f WHERE f.user.id = :userId")
void deleteByUserId(@Param("userId") Long userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package stackpot.stackpot.feed.service;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
Expand All @@ -13,6 +12,7 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import stackpot.stackpot.apiPayload.code.status.ErrorStatus;
import stackpot.stackpot.apiPayload.exception.handler.FeedHandler;
import stackpot.stackpot.apiPayload.exception.handler.UserHandler;
Expand Down Expand Up @@ -200,7 +200,7 @@ public boolean toggleLike(Long feedId) {
feedLikeRepository.delete(existingLike.get());
feed.setLikeCount(feed.getLikeCount() - 1);
feedRepository.save(feed);

// todo 좋아요 알림 삭제
return false; // 좋아요 취소
} else {
// 좋아요 추가
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
public interface FeedQueryService {
FeedResponseDto.FeedPreviewList getPreViewFeeds(String category, String sort, Long cursor, int limit);
FeedResponseDto.AuthorizedFeedDto getFeed(Long feedId);
UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int pageSize);
UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int pageSize, Long seriesId);
// FeedResponseDto.FeedPreviewList searchByUserIdByKeyword(Long userId, Long nextCursor, int pageSize);
UserMyPageResponseDto getFeeds(Long nextCursor, int pageSize);
UserMyPageResponseDto getFeeds(Long nextCursor, int pageSize, Long seriesId);
Map<Long, String> getMySeries();
Long getLikeCount(Long feedId);
Feed getFeedByFeedId(Long feedId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public FeedResponseDto.AuthorizedFeedDto getFeed(Long feedId) {
}

@Transactional
public UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int pageSize) {
public UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int pageSize, Long seriesId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean isAuthenticated = authentication != null
&& !(authentication instanceof AnonymousAuthenticationToken)
Expand All @@ -202,9 +202,19 @@ public UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int
: List.of();

Pageable pageable = PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "feedId"));
List<Feed> feeds = (nextCursor == null)
? feedRepository.findByUser_Id(userId, pageable)
: feedRepository.findByUserIdAndFeedIdBefore(userId, nextCursor, pageable);
// List<Feed> feeds = (nextCursor == null)
// ? feedRepository.findByUser_Id(userId, pageable)
// : feedRepository.findByUserIdAndFeedIdBefore(userId, nextCursor, pageable);
List<Feed> feeds;
if (seriesId == 0L) {
feeds = (nextCursor == null)
? feedRepository.findByUser_Id(userId, pageable)
: feedRepository.findByUserIdAndFeedIdBefore(userId, nextCursor, pageable);
} else {
feeds = (nextCursor == null)
? feedRepository.findByUser_IdAndSeries_SeriesId(userId, seriesId, pageable)
: feedRepository.findByUser_IdAndSeries_SeriesIdAndFeedIdBefore(userId, seriesId, nextCursor, pageable);
}

List<FeedResponseDto.FeedDto> feedDtos = feeds.stream()
.map(feed -> {
Expand Down Expand Up @@ -233,13 +243,20 @@ public UserMyPageResponseDto getFeedsByUserId(Long userId, Long nextCursor, int
}

@Override
public UserMyPageResponseDto getFeeds(Long nextCursor, int pageSize) {
public UserMyPageResponseDto getFeeds(Long nextCursor, int pageSize, Long seriesId) {
User user = authService.getCurrentUser();

Pageable pageable = PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "feedId"));
List<Feed> feeds = (nextCursor == null)
? feedRepository.findByUser_Id(user.getId(), pageable)
: feedRepository.findByUserIdAndFeedIdBefore(user.getId(), nextCursor, pageable);
List<Feed> feeds;
if (seriesId == 0L) {
feeds = (nextCursor == null)
? feedRepository.findByUser_Id(user.getId(), pageable)
: feedRepository.findByUserIdAndFeedIdBefore(user.getId(), nextCursor, pageable);
} else {
feeds = (nextCursor == null)
? feedRepository.findByUser_IdAndSeries_SeriesId(user.getId(), seriesId, pageable)
: feedRepository.findByUser_IdAndSeries_SeriesIdAndFeedIdBefore(user.getId(), seriesId, nextCursor, pageable);
}

List<Long> likedFeedIds = feedLikeRepository.findFeedIdsByUserId(user.getId());
List<Long> savedFeedIds = feedSaveRepository.findFeedIdsByUserId(user.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.List;

@RestController
@RequestMapping("/api/notifications")
@RequestMapping("/notifications")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

베이스 경로 변경은 외부 호환성 파괴 — 기존 경로 병행 제공 권장

/api/notifications → /notifications 변경은 클라이언트/게이트웨이/문서 전반에 영향이 큽니다. 최소 한 리리스 동안 구 경로를 병행 제공해 다운타임/배포 순서 문제를 방지하세요.

안전한 이행을 위한 최소 수정:

-@RequestMapping("/notifications")
+@RequestMapping({"/notifications", "/api/notifications"})

체크리스트:

  • API 게이트웨이/리버스 프록시 라우팅 업데이트
  • Swagger/OpenAPI 서버 URL/예제 경로 수정
  • 클라이언트 앱/SDK 호출 경로 동시 지원 기간 공지
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@RequestMapping("/notifications")
@RequestMapping({"/notifications", "/api/notifications"})

@RequiredArgsConstructor
public class NotificationController {

Expand All @@ -31,7 +31,10 @@ public ResponseEntity<ApiResponse<List<NotificationResponseDto.UnReadNotificatio
return ResponseEntity.ok(ApiResponse.onSuccess(notificationQueryService.getAllUnReadNotifications()));
}

@Operation(summary = "알림 읽음 처리 API")
@Operation(
summary = "알림 읽음 처리 API",
description = "NotificationType은 알림 조회 응답의 type 필드를 그대로 보내면 됩니다"
)
@ApiErrorCodeExamples({
ErrorStatus.NOTIFICATION_NOT_FOUND,
ErrorStatus.INVALID_NOTIFICATION_TYPE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import org.springframework.stereotype.Component;
import stackpot.stackpot.notification.dto.NotificationDto;
import stackpot.stackpot.notification.dto.NotificationResponseDto;
import stackpot.stackpot.user.entity.enums.Role;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

Expand All @@ -17,19 +15,21 @@ public class NotificationConverter {
public NotificationResponseDto.UnReadNotificationDto toUnReadNotificationDto(NotificationDto.UnReadNotificationDto unReadNotificationDto) {
return NotificationResponseDto.UnReadNotificationDto.builder()
.notificationId(unReadNotificationDto.getNotificationId())
.potOrFeedId(unReadNotificationDto.getPotOrFeedId())
.userName(unReadNotificationDto.getUserName() + " 새싹")
.potId(unReadNotificationDto.getPotId())
.feedId(unReadNotificationDto.getFeedId())
.userName(unReadNotificationDto.getUserName() == null ? " " : unReadNotificationDto.getUserName() + " 새싹")
.type(unReadNotificationDto.getType())
.content(unReadNotificationDto.getContent())
.createdAt(unReadNotificationDto.getCreatedAt().format(DATE_FORMATTER))
.build();
}

public NotificationResponseDto.UnReadNotificationDto toUnReadNotificationDto(
Long notificationId, Long potOrFeedId, String userName, String type, String content, LocalDateTime createdAt) {
Long notificationId, Long potId, Long feedId, String userName, String type, String content, LocalDateTime createdAt) {
return NotificationResponseDto.UnReadNotificationDto.builder()
.notificationId(notificationId)
.potOrFeedId(potOrFeedId)
.potId(potId)
.feedId(feedId)
.userName(userName + " 새싹")
.type(type)
.content(content)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ public class NotificationDto {
@AllArgsConstructor
public static class UnReadNotificationDto {
private Long notificationId;
private Long potOrFeedId;
private Long potId;
private Long feedId;
private String userName;
private String type;
private String content;
private String type; // 알림 종류
private String content; // 알림 내용
private LocalDateTime createdAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public class NotificationResponseDto {
@AllArgsConstructor
public static class UnReadNotificationDto {
private Long notificationId;
private Long potOrFeedId;
private Long potId;
private Long feedId;
private String userName;
private String type;
private String content;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package stackpot.stackpot.notification.entity;

import jakarta.persistence.*;
import lombok.*;
import stackpot.stackpot.common.BaseEntity;
import stackpot.stackpot.pot.entity.Pot;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class PotEndNotification extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "pot_end_notification_id")
private Long id;

private Boolean isRead;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pot_id")
private Pot pot;

public void updateIsRead(Boolean isRead) {
this.isRead = isRead;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,25 @@ public void read(Long id, NotificationCommandService service) {
.orElseThrow(() -> new NotificationHandler(ErrorStatus.NOTIFICATION_NOT_FOUND))
.updateIsRead(true);
}
},
POT_END {
@Override
public void read(Long id, NotificationCommandService service){
service.getPotEndNotificationRepository().findById(id)
.orElseThrow(() -> new NotificationHandler(ErrorStatus.NOTIFICATION_NOT_FOUND))
.updateIsRead(true);
}
};

public abstract void read(Long id, NotificationCommandService service);

public static NotificationType from(String type) {
return switch (type) {
case "PotApplication" -> POT_APPLICATION;
case "PotComment" -> POT_COMMENT;
case "FeedLike" -> FEED_LIKE;
case "FeedComment" -> FEED_COMMENT;
case "팟 지원 알림" -> POT_APPLICATION;
case "팟 댓글 알림" -> POT_COMMENT;
case "피드 좋아요 알림" -> FEED_LIKE;
case "피드 댓글 알림" -> FEED_COMMENT;
case "팟 종료 알림" -> POT_END;
default -> throw new NotificationHandler(ErrorStatus.INVALID_NOTIFICATION_TYPE);
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package stackpot.stackpot.notification.event;

import lombok.AllArgsConstructor;
import lombok.Getter;
import stackpot.stackpot.notification.dto.NotificationResponseDto;

@Getter
@AllArgsConstructor
public class PotEndEvent {
private Long potId;
private NotificationResponseDto.UnReadNotificationDto unReadNotificationDto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,26 @@
import stackpot.stackpot.notification.entity.FeedCommentNotification;

import java.util.List;
import java.util.Optional;

public interface FeedCommentNotificationRepository extends JpaRepository<FeedCommentNotification, Long> {

@Query("SELECT new stackpot.stackpot.notification.dto.NotificationDto$UnReadNotificationDto(" +
"fcn.id, fcn.feedComment.feed.feedId, " +
"fcn.feedComment.user.nickname, 'FeedComment', fcn.feedComment.comment, fcn.createdAt) " +
"fcn.id, " +
"null, " +
"f.feedId, " +
"u.nickname, " +
"'피드 댓글 알림', " +
"CONCAT(u.nickname, ' 새싹님의 댓글이 달렸어요. ', fc.comment), " +
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JPQL CONCAT 3개 인자 사용은 JPA 표준 위반 — 런타임 에러 가능성이 큽니다

JPQL 표준 CONCAT은 2개 인자만 허용합니다. 현재 3개 인자를 사용하고 있어 데이터베이스/방언에 따라 즉시 실패할 수 있습니다. 중첩 CONCAT으로 치환해 주세요.

다음 패치를 적용하세요:

-            "CONCAT(u.nickname, ' 새싹님의 댓글이 달렸어요. ', fc.comment), " +
+            "CONCAT(CONCAT(u.nickname, ' 새싹님의 댓글이 달렸어요. '), fc.comment), " +
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"CONCAT(u.nickname, ' 새싹님의 댓글이 달렸어요. ', fc.comment), " +
"CONCAT(CONCAT(u.nickname, ' 새싹님의 댓글이 달렸어요. '), fc.comment), " +
🤖 Prompt for AI Agents
In
src/main/java/stackpot/stackpot/notification/repository/FeedCommentNotificationRepository.java
around line 20, the JPQL uses CONCAT with three arguments which violates the JPA
spec and can cause runtime errors; replace the three-argument call with nested
CONCAT calls (e.g., CONCAT(CONCAT(part1, part2), part3)) so the nickname,
literal text, and comment are concatenated via two-argument CONCAT nesting.

"fcn.createdAt) " +
"FROM FeedCommentNotification fcn " +
"WHERE fcn.isRead = false AND (" +
" (fcn.feedComment.parent is null AND fcn.feedComment.feed.user.id = :userId) OR " +
" (fcn.feedComment.parent is not null AND " +
" (fcn.feedComment.parent.user.id = :userId OR fcn.feedComment.feed.user.id = :userId)" +
" ))")
List<NotificationDto.UnReadNotificationDto> findAllUnReadNotificationsByUserId(Long userId);
"JOIN fcn.feedComment fc " +
"JOIN fc.user u " +
"JOIN fc.feed f " +
"LEFT JOIN fc.parent fcp " +
"WHERE fcn.isRead = false AND " +
"((fc.parent is null AND f.user.id = :userId) OR " +
" (fc.parent is not null AND (fcp.user.id = :userId OR f.user.id = :userId)))")
Comment on lines +27 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

자기 자신의 활동에 대한 알림 제외 조건 누락

댓글 작성자(u)가 수신자(:userId)와 동일한 경우까지 매칭되어 자기 자신의 행동에 대한 알림이 노출될 수 있습니다. 일반적으로는 제외하는 것이 기대 동작입니다. 확인 후 제외 조건을 추가해 주세요.

제안 패치:

-            "WHERE fcn.isRead = false AND " +
+            "WHERE fcn.isRead = false AND u.id <> :userId AND " +
             "((fc.parent is null AND f.user.id = :userId) OR " +
             " (fc.parent is not null AND (fcp.user.id = :userId OR f.user.id = :userId)))")

의도적으로 자기 알림을 허용하는 요구사항이라면 본 제안은 무시하셔도 됩니다. 요구사항만 확인 부탁드립니다.


🏁 Script executed:

#!/usr/bin/env bash
set -e
# Show 15 lines of context around the HQL query to verify alias definitions
rg -n '"WHERE fcn.isRead' -C15 src/main/java/stackpot/stackpot/notification/repository/FeedCommentNotificationRepository.java || true

Length of output: 1352


자기 자신의 활동에 대한 알림 제외 조건 추가 필요

아래 쿼리에서 댓글 작성자(u)가 수신자(:userId)와 동일한 경우에도 알림이 조회되고 있으므로, u.id <> :userId 조건을 추가해야 합니다.

문제가 되는 위치:

  • 파일: src/main/java/stackpot/stackpot/notification/repository/FeedCommentNotificationRepository.java
  • 메서드: findAllUnReadNotificationsByUserId
  • 쿼리 WHERE 절 시작 지점 (27~29행)

제안 수정(diff):

-            "WHERE fcn.isRead = false AND " +
+            "WHERE fcn.isRead = false AND u.id <> :userId AND " +
             "((fc.parent is null AND f.user.id = :userId) OR " +
             " (fc.parent is not null AND (fcp.user.id = :userId OR f.user.id = :userId)))")

요구사항상 본인이 발생시킨 댓글 알림까지 허용해야 하는 특별한 이유가 없다면, 위 패치를 적용해 주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"WHERE fcn.isRead = false AND " +
"((fc.parent is null AND f.user.id = :userId) OR " +
" (fc.parent is not null AND (fcp.user.id = :userId OR f.user.id = :userId)))")
"WHERE fcn.isRead = false AND u.id <> :userId AND " +
"((fc.parent is null AND f.user.id = :userId) OR " +
" (fc.parent is not null AND (fcp.user.id = :userId OR f.user.id = :userId)))")
🤖 Prompt for AI Agents
In
src/main/java/stackpot/stackpot/notification/repository/FeedCommentNotificationRepository.java
around lines 27-29, the WHERE clause currently returns notifications even when
the comment author (alias u) is the same as the recipient (:userId); update the
WHERE clause to exclude self-generated notifications by adding the condition
u.id <> :userId so that only comments authored by others are returned (ensure
the condition is applied to both parent-null and parent-not-null branches and
that the :userId parameter is already available in the query).

List<NotificationDto.UnReadNotificationDto> findAllUnReadNotificationsByUserId(@Param("userId") Long userId);

@Modifying
@Query("DELETE FROM FeedCommentNotification fcn WHERE fcn.feedComment.id = :feedCommentId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.security.core.parameters.P;
import stackpot.stackpot.notification.dto.NotificationDto;
import stackpot.stackpot.notification.entity.FeedLikeNotification;

import java.util.List;
import java.util.Optional;

public interface FeedLikeNotificationRepository extends JpaRepository<FeedLikeNotification, Long> {

@Query("SELECT new stackpot.stackpot.notification.dto.NotificationDto$UnReadNotificationDto(" +
"fln.id, fln.feedLike.feed.feedId, " +
"fln.feedLike.user.nickname, 'FeedLike', null, fln.createdAt) " +
"fln.id, " +
"null, " +
"fln.feedLike.feed.feedId, " +
"fln.feedLike.user.nickname, " +
"'피드 좋아요 알림', " +
"CONCAT(fln.feedLike.user.nickname, ' 새싹님이 내 피드에 좋아요를 눌렀어요. 확인해 보세요!'), " +
"fln.createdAt) " +
"FROM FeedLikeNotification fln " +
"WHERE fln.isRead = false and fln.feedLike.feed.user.id = :userId ")
List<NotificationDto.UnReadNotificationDto> findAllUnReadNotificationsByUserId(@Param("userId") Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
public interface PotApplicationNotificationRepository extends JpaRepository<PotApplicationNotification, Long> {

@Query("SELECT new stackpot.stackpot.notification.dto.NotificationDto$UnReadNotificationDto(" +
"pan.id, pan.potApplication.pot.potId, pan.potApplication.user.nickname, " +
"'PotApplication', null, pan.createdAt) " +
"pan.id, " +
"pan.potApplication.pot.potId, " +
"null, " +
"pan.potApplication.user.nickname, " +
"'팟 지원 알림', " +
"CONCAT(pan.potApplication.user.nickname, ' 새싹님이 내 팟에 지원했어요. 확인해 보세요!'), " +
"pan.createdAt) " +
"FROM PotApplicationNotification pan " +
"WHERE pan.isRead = false AND pan.potApplication.pot.user.id = :userId")
List<NotificationDto.UnReadNotificationDto> findAllUnReadNotificationsByUserId(@Param("userId") Long userId);
Expand Down
Loading