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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import hanium.modic.backend.common.property.property.CloudFrontProperties;
import hanium.modic.backend.common.property.property.CorsProperties;
import hanium.modic.backend.common.property.property.EmailProperty;
import hanium.modic.backend.common.property.property.NotificationProperties;
import hanium.modic.backend.common.property.property.RabbitMqProperties;
import hanium.modic.backend.common.property.property.RedisProperty;
import hanium.modic.backend.common.property.property.S3Properties;
Expand All @@ -28,7 +29,8 @@
RabbitMqProperties.class,
CloudFrontProperties.class,
AiProperties.class,
VoteProperties.class
VoteProperties.class,
NotificationProperties.class
})
public class PropertyConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package hanium.modic.backend.common.property.property;

import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

중복된 getter/setter를 제거하세요.

Lombok의 @Getter@Setter 어노테이션을 사용하면서 Line 20-26에서 명시적으로 getter/setter를 다시 정의하고 있어 중복입니다.

다음 중 하나를 선택하세요:

  1. Lombok 어노테이션을 유지하고 Line 20-26의 명시적 메서드를 제거
  2. Lombok 어노테이션을 제거하고 명시적 메서드만 유지

권장 방안: Lombok 어노테이션 유지

 @Getter
 @Setter
 @ConfigurationProperties(prefix = "notification")
 public class NotificationProperties {
 
 	/**
 	 * 읽은 알림을 얼마 동안 유지할지(분 단위)
 	 */
 	private long readRetentionMinutes = Duration.ofDays(30).toMinutes(); // 기본값: 30일
 
-	public long getReadRetentionMinutes() {
-		return readRetentionMinutes;
-	}
-
-	public void setReadRetentionMinutes(long readRetentionMinutes) {
-		this.readRetentionMinutes = readRetentionMinutes;
-	}
-
 	public Duration getReadRetentionDuration() {
 		return Duration.ofMinutes(readRetentionMinutes);
 	}
 }
📝 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
@Getter
@Setter
@Getter
@Setter
@ConfigurationProperties(prefix = "notification")
public class NotificationProperties {
/**
* 읽은 알림을 얼마 동안 유지할지( 단위)
*/
private long readRetentionMinutes = Duration.ofDays(30).toMinutes(); // 기본값: 30일
public Duration getReadRetentionDuration() {
return Duration.ofMinutes(readRetentionMinutes);
}
}
🤖 Prompt for AI Agents
In
src/main/java/hanium/modic/backend/common/property/property/NotificationProperties.java
around lines 10-11 and 20-26, you have duplicated getters/setters: Lombok
@Getter/@Setter are applied at the class level while explicit getter and setter
methods are defined on lines 20-26; remove the explicit methods (lines 20-26)
and keep the Lombok annotations so the compiler-generated accessors are used,
ensuring imports for Lombok remain and no other references rely on the removed
method bodies.

@ConfigurationProperties(prefix = "notification")
public class NotificationProperties {

/**
* 읽은 알림을 얼마 동안 유지할지(분 단위)
*/
private long readRetentionMinutes = Duration.ofDays(30).toMinutes(); // 기본값: 30일

public long getReadRetentionMinutes() {
return readRetentionMinutes;
}

public void setReadRetentionMinutes(long readRetentionMinutes) {
this.readRetentionMinutes = readRetentionMinutes;
}

public Duration getReadRetentionDuration() {
return Duration.ofMinutes(readRetentionMinutes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package hanium.modic.backend.common.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import hanium.modic.backend.domain.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class DailyScheduler {

private final NotificationService notificationService;

// 매일 자정(00:00:00)에 실행
@Scheduled(cron = "0 0 0 * * *")
public void runEveryMidnight() {
notificationService.cleanupExpiredNotifications();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hanium.modic.backend.common.scheduler.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class SchedulingConfig {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

import hanium.modic.backend.common.error.exception.AppException;
import hanium.modic.backend.common.error.exception.LockException;
import hanium.modic.backend.common.redis.distributedLock.LockManager;
import hanium.modic.backend.domain.notification.dto.NotificationPayload;
import hanium.modic.backend.domain.notification.enums.NotificationType;
import hanium.modic.backend.domain.notification.service.NotificationService;
import hanium.modic.backend.domain.user.entity.UserEntity;
import hanium.modic.backend.domain.user.repository.UserEntityRepository;
import hanium.modic.backend.infra.redis.distributedLock.LockManager;
import hanium.modic.backend.domain.ai.aiChat.entity.AiChatRoomEntity;
import hanium.modic.backend.domain.ai.aiChat.repository.AiChatRoomRepository;
import hanium.modic.backend.domain.post.entity.PostEntity;
Expand All @@ -25,7 +30,9 @@ public class AiImagePermissionService {
private final TicketService ticketService;
private final PostEntityRepository postRepository;
private final AiChatRoomRepository aiChatRoomRepository;
private final NotificationService notificationService;
private final LockManager lockManager;
private final UserEntityRepository userEntityRepository;

private final int AI_IMAGE_PERMISSION_COUNT = 20; // 구매 시 제공되는 이미지 생성 횟수

Expand All @@ -42,6 +49,19 @@ public void buyAiImagePermissionByCoin(Long userId, Long postId) {

// 3) 코인 후차감, 코인 거래는 별도의 트랜잭션으로 동작하여 후처리, 예외는 전파
userCoinService.consumeCoin(userId, post.getNonCommercialPrice());

// 4) 알림
UserEntity user = userEntityRepository.findById(userId)
.orElseThrow(() -> new AppException(USER_NOT_FOUND_EXCEPTION));
notificationService.createNotification(
post.getUserId(),
NotificationType.POST_PURCHASED_BY_COIN,
NotificationPayload.builder(userId, user.getName(), user.getEmail())
.postId(post.getId())
.postTitle(post.getTitle())
.amount(post.getNonCommercialPrice())
.build()
);
}

// 티켓으로 AI 이미지 생성권 구매
Expand All @@ -56,6 +76,19 @@ public void buyAiImagePermissionByTicket(final Long userId, final Long postId) {

// 3) 티켓 후차감, 코인 거래는 별도의 트랜잭션으로 동작하여 후처리, 예외는 전파
ticketService.useTicket(userId, post.getTicketPrice());

// 4) 알림
UserEntity user = userEntityRepository.findById(userId)
.orElseThrow(() -> new AppException(USER_NOT_FOUND_EXCEPTION));
notificationService.createNotification(
post.getUserId(),
NotificationType.POST_PURCHASED_BY_TICKET,
NotificationPayload.builder(userId, user.getName(), user.getEmail())
.postId(post.getId())
.postTitle(post.getTitle())
.amount(post.getTicketPrice())
.build()
);
}

// 이미지 생성권 소모
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hanium.modic.backend.domain.ai.aiServer.listener;

import static hanium.modic.backend.common.amqp.config.RabbitMqConfig.*;
import static hanium.modic.backend.infra.amqp.config.RabbitMqConfig.*;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hanium.modic.backend.domain.ai.aiServer.listener;

import static hanium.modic.backend.common.amqp.config.RabbitMqConfig.*;
import static hanium.modic.backend.infra.amqp.config.RabbitMqConfig.*;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hanium.modic.backend.domain.ai.aiServer.listener;

import static hanium.modic.backend.common.amqp.config.RabbitMqConfig.*;
import static hanium.modic.backend.infra.amqp.config.RabbitMqConfig.*;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import hanium.modic.backend.common.error.exception.AppException;
import hanium.modic.backend.common.sse.service.EmitterService;
import hanium.modic.backend.infra.sse.service.EmitterService;
import hanium.modic.backend.domain.ai.aiChat.entity.AiChatMessageEntity;
import hanium.modic.backend.domain.ai.aiChat.repository.AiChatMessageRepository;
import hanium.modic.backend.domain.ai.aiServer.enums.SenderType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;

import hanium.modic.backend.common.amqp.service.MessageQueueService;
import hanium.modic.backend.infra.amqp.service.MessageQueueService;
import hanium.modic.backend.common.error.ErrorCode;
import hanium.modic.backend.common.error.exception.AppException;
import hanium.modic.backend.domain.ai.aiChat.entity.AiChatMessageEntity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
import hanium.modic.backend.domain.follow.dto.FollowerWithStatus;
import hanium.modic.backend.domain.follow.dto.FollowingWithStatus;
import hanium.modic.backend.domain.follow.repository.FollowEntityRepository;
import hanium.modic.backend.domain.notification.dto.NotificationPayload;
import hanium.modic.backend.domain.notification.enums.NotificationType;
import hanium.modic.backend.domain.notification.service.NotificationService;
import hanium.modic.backend.domain.user.entity.UserEntity;
import hanium.modic.backend.domain.user.repository.UserEntityRepository;
import hanium.modic.backend.domain.user.repository.UserImageEntityRepository;
import hanium.modic.backend.domain.user.service.UserImageService;
import hanium.modic.backend.web.follow.dto.response.GetFollowersResponse;
import hanium.modic.backend.web.follow.dto.response.GetFollowersWithStatusResponse;
Expand All @@ -28,10 +30,15 @@
@Service
@RequiredArgsConstructor
public class FollowService {
// 팔로우 관련
private final FollowEntityRepository followRepository;

// 유저 관련
private final UserEntityRepository userRepository;
private final UserImageService userImageService;
private final UserImageEntityRepository userImageRepository;

// 알림 관련
private final NotificationService notificationService;

// 팔로우 또는 언팔로우 처리
@Transactional
Expand All @@ -51,6 +58,14 @@ public void followOrUnfollow(
// 팔로우 요청 및 기존 팔로우 존재 여부에 따라 팔로우, 언팔로우 처리
if (type == FOLLOW) {
followRepository.insertFollowIfExist(me.getId(), target.getId());

// 알림 생성
notificationService.createNotification(
targetId,
NotificationType.FOLLOWED,
NotificationPayload.builder(me.getId(), me.getName(), me.getEmail())
.build()
);
} else if (type == UNFOLLOW) {
followRepository.deleteByMyIdAndFollowingId(me.getId(), targetId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package hanium.modic.backend.domain.notification.dto;

import java.time.LocalDateTime;

import hanium.modic.backend.domain.notification.entity.NotificationEntity;
import hanium.modic.backend.domain.notification.enums.NotificationStatus;
import hanium.modic.backend.domain.notification.enums.NotificationType;

public record GetNotificationsResponse(
Long notificationId,
NotificationType type,
NotificationStatus status,
String title,
String body,
Long postId,
LocalDateTime createdAt
) {

public static GetNotificationsResponse of(NotificationEntity entity, NotificationPayload payload) {
return new GetNotificationsResponse(
entity.getId(),
entity.getType(),
entity.getStatus(),
entity.getTitle(),
entity.getBody(),
payload.postId(),
entity.getCreateAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package hanium.modic.backend.domain.notification.dto;

public record GetUnreadCountResponse(
long unreadCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package hanium.modic.backend.domain.notification.dto;

public record NotificationPayload(
Long senderId,
String senderNickname,
String senderEmail, // 추가됨
Long postId,
String postTitle,
Long amount,
String reviewContent
) {

public static NotificationPayload empty() {
return new NotificationPayload(
null, null, null, null, null, null, null
);
}

public static Builder builder(Long senderId, String senderNickname, String senderEmail) {
return new Builder(senderId, senderNickname, senderEmail);
}

public static class Builder {
private final Long senderId;
private final String senderNickname;
private final String senderEmail;

private Long postId;
private String postTitle;
private Long amount;
private String reviewContent;

public Builder(Long senderId, String senderNickname, String senderEmail) {
this.senderId = senderId;
this.senderNickname = senderNickname;
this.senderEmail = senderEmail;
}

public Builder postId(Long postId) {
this.postId = postId;
return this;
}

public Builder postTitle(String postTitle) {
this.postTitle = postTitle;
return this;
}

public Builder amount(Long amount) {
this.amount = amount;
return this;
}

public Builder reviewContent(String reviewContent) {
this.reviewContent = reviewContent;
return this;
}

public NotificationPayload build() {
return new NotificationPayload(
senderId,
senderNickname,
senderEmail,
postId,
postTitle,
amount,
reviewContent
);
}
}
}
Loading