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 @@ -10,16 +10,14 @@
import hanium.modic.backend.common.error.exception.LockException;
import hanium.modic.backend.domain.ai.aiChat.entity.AiChatRoomEntity;
import hanium.modic.backend.domain.ai.aiChat.repository.AiChatRoomRepository;
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.notification.factory.NotificationFactory;
import hanium.modic.backend.domain.post.entity.PostEntity;
import hanium.modic.backend.domain.post.repository.PostEntityRepository;
import hanium.modic.backend.domain.ticket.service.TicketService;
import hanium.modic.backend.domain.transaction.service.AccountService;
import hanium.modic.backend.domain.transaction.service.HistoryService;
import hanium.modic.backend.domain.user.entity.UserEntity;
import hanium.modic.backend.domain.user.repository.UserEntityRepository;
import hanium.modic.backend.domain.user.service.UserImageService;
import hanium.modic.backend.infra.redis.distributedLock.LockManager;
import hanium.modic.backend.web.ai.aiChat.dto.response.GetRemainingGenerationsResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -28,14 +26,26 @@
@RequiredArgsConstructor
public class AiImagePermissionService {

// 거래 관련
private final AccountService accountService;
private final HistoryService historyService;
private final TicketService ticketService;

// 포스트 관련
private final PostEntityRepository postRepository;

// 채팅 관련
private final AiChatRoomRepository aiChatRoomRepository;
private final NotificationService notificationService;
private final LockManager lockManager;

// 알림 관련
private final NotificationFactory notificationFactory;

// 유저 관련
private final UserEntityRepository userEntityRepository;
private final UserImageService userImageService;

// 기타
private final LockManager lockManager;

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

Expand All @@ -58,17 +68,7 @@ public void buyAiImagePermissionByCoin(Long userId, Long postId) {
post.getTitle());

// 5) 알림
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()
);
notificationFactory.postPurchasedByCoin(userId, postId);
}

// 티켓으로 AI 이미지 생성권 구매
Expand All @@ -85,17 +85,7 @@ public void buyAiImagePermissionByTicket(final Long userId, final Long postId) {
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()
);
notificationFactory.postPurchasedByTicket(userId, postId);
}

// 이미지 생성권 소모
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
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.notification.factory.NotificationFactory;
import hanium.modic.backend.domain.user.entity.UserEntity;
import hanium.modic.backend.domain.user.repository.UserEntityRepository;
import hanium.modic.backend.domain.user.service.UserImageService;
Expand All @@ -38,7 +36,7 @@ public class FollowService {
private final UserImageService userImageService;

// 알림 관련
private final NotificationService notificationService;
private final NotificationFactory notificationFactory;

// 팔로우 또는 언팔로우 처리
@Transactional
Expand All @@ -60,12 +58,7 @@ public void followOrUnfollow(
followRepository.insertFollowIfExist(me.getId(), target.getId());

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

import java.time.LocalDateTime;
import java.util.Optional;

import hanium.modic.backend.domain.notification.entity.NotificationEntity;
import hanium.modic.backend.domain.notification.enums.NotificationStatus;
Expand All @@ -14,10 +15,17 @@ public record GetNotificationsResponse(
String body,
Long postId,
Long senderId,
boolean hasSenderImage,
String senderImageUrl,
LocalDateTime createdAt
) {

public static GetNotificationsResponse of(NotificationEntity entity, NotificationPayload payload) {
public static GetNotificationsResponse of(
NotificationEntity entity,
NotificationPayload payload,
boolean hasSenderImage,
String senderImageUrl
) {
return new GetNotificationsResponse(
entity.getId(),
entity.getType(),
Expand All @@ -26,6 +34,8 @@ public static GetNotificationsResponse of(NotificationEntity entity, Notificatio
entity.getBody(),
payload.postId(),
payload.senderId(),
hasSenderImage,
senderImageUrl,
entity.getCreateAt()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public record NotificationPayload(
Long senderId,
String senderNickname,
String senderEmail, // 추가됨
String senderEmail,
Long postId,
String postTitle,
Long amount,
Expand All @@ -12,7 +12,13 @@ public record NotificationPayload(

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

Expand All @@ -30,7 +36,11 @@ public static class Builder {
private Long amount;
private String reviewContent;

public Builder(Long senderId, String senderNickname, String senderEmail) {
public Builder(
Long senderId,
String senderNickname,
String senderEmail
) {
this.senderId = senderId;
this.senderNickname = senderNickname;
this.senderEmail = senderEmail;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package hanium.modic.backend.domain.notification.factory;

import java.util.Optional;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import hanium.modic.backend.common.error.ErrorCode;
import hanium.modic.backend.common.error.exception.AppException;
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.post.entity.PostEntity;
import hanium.modic.backend.domain.post.repository.PostEntityRepository;
import hanium.modic.backend.domain.postReview.entity.PostReviewEntity;
import hanium.modic.backend.domain.postReview.repository.PostReviewRepository;
import hanium.modic.backend.domain.user.entity.UserEntity;
import hanium.modic.backend.domain.user.repository.UserEntityRepository;
import hanium.modic.backend.domain.user.service.UserImageService;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
@Transactional
public class NotificationFactory {

private final NotificationService notificationService;

private final UserEntityRepository userRepository;
private final UserImageService userImageService;

private final PostEntityRepository postRepository;
private final PostReviewRepository postReviewRepository;

// 유저 조회
private UserEntity getUser(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new AppException(ErrorCode.USER_NOT_FOUND_EXCEPTION));
}

// 유저 이미지 URL 조회
private Optional<String> getUserImageUrl(Long userId) {
return userImageService.createImageGetUrlOptional(userId);
}

// 알림 전송 공통 메서드
private void send(Long toUserId, NotificationType type, NotificationPayload payload) {
notificationService.createNotification(toUserId, type, payload);
}

// 알림 페이로드 기본 빌더(보내는 이 정보와 보내는 이의 이미지)
private NotificationPayload.Builder basePayload(Long senderId) {
UserEntity sender = getUser(senderId);

return NotificationPayload.builder(
senderId,
sender.getName(),
sender.getEmail()
);
}

// 코인 수신 알림 (COIN_RECEIVED)
public void coinReceived(Long fromUserId, Long toUserId, Long coinAmount) {
NotificationPayload payload = basePayload(fromUserId)
.amount(coinAmount)
.build();

send(toUserId, NotificationType.COIN_RECEIVED, payload);
}

// 티켓으로 게시글 구매 알림 (POST_PURCHASED_BY_COIN)
public void postPurchasedByCoin(Long buyerId, Long postId) {
PostEntity post = postRepository.findById(postId)
.orElseThrow();

NotificationPayload payload = basePayload(buyerId)
.postId(post.getId())
.postTitle(post.getTitle())
.amount(post.getNonCommercialPrice())
.build();

send(post.getUserId(), NotificationType.POST_PURCHASED_BY_COIN, payload);
}

// 티켓으로 게시글 구매 알림 (POST_PURCHASED_BY_TICKET)
public void postPurchasedByTicket(Long buyerId, Long postId) {
PostEntity post = postRepository.findById(postId)
.orElseThrow();

NotificationPayload payload = basePayload(buyerId)
.postId(post.getId())
.postTitle(post.getTitle())
.amount(post.getTicketPrice())
.build();

send(post.getUserId(), NotificationType.POST_PURCHASED_BY_TICKET, payload);
}

// 게시글 후기 작성 알림 (POST_REVIEWED)
public void postReviewed(Long reviewerId, Long postId, Long reviewId) {
PostEntity post = postRepository.findById(postId).orElseThrow();
PostReviewEntity review = postReviewRepository.findById(reviewId).orElseThrow();

NotificationPayload payload = basePayload(reviewerId)
.postId(post.getId())
.postTitle(post.getTitle())
.reviewContent(review.getDescription())
.build();

send(post.getUserId(), NotificationType.POST_REVIEWED, payload);
}

// 팔로우 (FOLLOWED)
public void followed(Long followerId, Long targetUserId) {
NotificationPayload payload = basePayload(followerId)
.build();

send(targetUserId, NotificationType.FOLLOWED, payload);
}

// 2차 창작물 생성 알림(DERIVED_POST_CREATED)
public void derivedPostCreated(Long userId, Long derivedPostId) {
PostEntity derivedPost = postRepository.findById(derivedPostId)
.orElseThrow();

NotificationPayload payload = basePayload(userId)
.postId(derivedPost.getId())
.postTitle(derivedPost.getTitle())
.build();

send(derivedPost.getUserId(), NotificationType.DERIVED_POST_CREATED, payload);
}

// 좋아요 알림 생성(LIKED)
public void liked(Long userId, Long postId) {
PostEntity post = postRepository.findById(postId).orElseThrow();

NotificationPayload payload = basePayload(userId)
.postId(post.getId())
.postTitle(post.getTitle())
.build();

send(post.getUserId(), NotificationType.LIKED, payload);
}
}
Loading