Skip to content

Commit

Permalink
Merge pull request #28 from it-s-time-goat/dev/encJ/pushAlarm
Browse files Browse the repository at this point in the history
[FEAT] 알림 목록 출력 기능 추가
  • Loading branch information
encoreJeong authored Jul 11, 2024
2 parents 5280d9e + a649f5c commit 7eadb08
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public void saveOnBoardingInfo(Long userId, OnBoardingRequest onBoardingRequest)

user.updateOnBoardingInfo(onBoardingRequest.nickname(), onBoardingRequest.goal());

if(!onBoardingRequest.fcmToken().isBlank()) {
user.updateFcmToken(onBoardingRequest.fcmToken());
}

userRepository.save(user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public record OnBoardingRequest(
String nickname,
String goal
String goal,
String fcmToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.goat.server.global.exception;

import com.goat.server.global.exception.errorcode.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class AccessDeniedException extends RuntimeException{
private final ErrorCode errorCode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum GlobalErrorCode implements ErrorCode {
INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "Invalid parameter included"),
RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "Resource not exists"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error"),
ACCESS_DENIED(HttpStatus.FORBIDDEN, "You don't have permission to access this resource"),
INVALID_METHOD(HttpStatus.METHOD_NOT_ALLOWED, "Invalid Method"),
;

Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/goat/server/mypage/application/UserService.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.goat.server.mypage.application;

import com.goat.server.auth.dto.response.KakaoUserResponse;
import com.goat.server.directory.domain.Directory;
import com.goat.server.directory.repository.DirectoryRepository;
import com.goat.server.global.application.S3Uploader;
import com.goat.server.global.domain.ImageInfo;
import com.goat.server.mypage.domain.User;
import com.goat.server.mypage.domain.type.Role;
import com.goat.server.mypage.exception.UserNotFoundException;
import com.goat.server.mypage.exception.errorcode.MypageErrorCode;
import com.goat.server.mypage.repository.UserRepository;
import com.goat.server.notification.domain.NotificationSetting;
import com.goat.server.notification.repository.NotificationSettingRepository;
import com.goat.server.review.domain.Review;
import com.goat.server.review.dto.request.ReviewUpdateRequest;
import com.goat.server.review.exception.ReviewNotFoundException;
Expand All @@ -24,19 +28,46 @@
@Service
public class UserService {

private final DirectoryRepository directoryRepository;
private final UserRepository userRepository;
private final S3Uploader s3Uploader;
private final NotificationSettingRepository notificationSettingRepository;

/**
* 유저 회원가입
*/
@Transactional
public User createUser(final KakaoUserResponse userResponse) {
User user = User.builder()
.socialId(userResponse.id().toString())
.nickname(userResponse.getNickname())
.role(Role.GUEST)
.build();

Directory trashDirectory = Directory.builder()
.user(user)
.title("trash_directory")
.depth(1L)
.build();

Directory storageDirectory = Directory.builder()
.user(user)
.title("storage_directory")
.depth(1L)
.build();

NotificationSetting notificationSetting = NotificationSetting.builder()
.user(user)
.isCommentNoti(false)
.isPostNoti(false)
.isReviewNoti(false)
.build();


notificationSettingRepository.save(notificationSetting);
directoryRepository.save(trashDirectory);
directoryRepository.save(storageDirectory);

return userRepository.save(user);
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/goat/server/mypage/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,8 @@ public void updateOnBoardingInfo(String nickname, String goal) {
this.goal = goal;
this.role = Role.USER;
}

public void updateFcmToken(String fcmToken) {
this.fcmToken = fcmToken;
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package com.goat.server.notification.application;

import com.goat.server.global.exception.AccessDeniedException;
import com.goat.server.mypage.domain.User;
import com.goat.server.mypage.exception.UserNotFoundException;
import com.goat.server.mypage.repository.UserRepository;
import com.goat.server.notification.domain.Notification;
import com.goat.server.notification.dto.response.NotificationResponse;
import com.goat.server.notification.repository.NotificationRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static com.goat.server.global.exception.errorcode.GlobalErrorCode.ACCESS_DENIED;
import static com.goat.server.mypage.exception.errorcode.MypageErrorCode.USER_NOT_FOUND;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class NotificationService {

private final NotificationRepository notificationRepository;
private final UserRepository userRepository;

@Transactional
public void saveNotification(Notification notification) {
Expand All @@ -21,4 +33,37 @@ public void saveNotification(Notification notification) {

notificationRepository.save(notification);
}

public NotificationResponse getNotifications(Long userId) {

log.info("[NotificationService.getNotifications]");

User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(USER_NOT_FOUND));

List<Notification> notifications = notificationRepository.findAllByUser(user);

List<NotificationResponse.NotificationComponentResponse> notificationComponents = notifications.stream()
.map(NotificationResponse.NotificationComponentResponse::from)
.toList();

return NotificationResponse.from(notificationComponents);
}

@Transactional
public void readNotification(Long userId, Long notificationId) {

log.info("[NotificationService.readNotification]");

Notification notification = notificationRepository.findById(notificationId)
.orElseThrow(() -> new IllegalArgumentException("Notification not found"));

if (!notification.getUser().getUserId().equals(userId)) {
throw new AccessDeniedException(ACCESS_DENIED);
}

notification.read();

notificationRepository.save(notification);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,18 @@ public class Notification extends BaseTimeEntity {
@ManyToOne(fetch = FetchType.LAZY)
private Review review;

@Column(name = "is_read")
private Boolean isRead;

@Builder
public Notification(String content, User user, Review review) {
this.content = content;
this.user = user;
this.review = review;
this.isRead = false;
}

public void read() {
this.isRead = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.goat.server.notification.dto.response;

import com.goat.server.notification.domain.Notification;
import lombok.Builder;

import java.util.List;

@Builder
public record NotificationResponse(
List<NotificationComponentResponse> notifications
) {
@Builder
public record NotificationComponentResponse(
Long notificationId,
Long reviewId,
String reviewBody,
Boolean isRead
){
public static NotificationComponentResponse from(Notification notification) {
return NotificationComponentResponse.builder()
.notificationId(notification.getNoti_id())
.reviewId(notification.getReview().getId())
.reviewBody(notification.getContent())
.isRead(notification.getIsRead())
.build();
}
}

public static NotificationResponse from(List<NotificationComponentResponse> notifications) {
return NotificationResponse.builder()
.notifications(notifications)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.goat.server.notification.presentation;

import com.goat.server.global.dto.ResponseTemplate;
import com.goat.server.notification.application.NotificationService;
import com.goat.server.notification.dto.response.NotificationResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequiredArgsConstructor
@RestController
@Tag(name = "NotificationController", description = "NotificationController 관련 API")
@RequestMapping("/goat/notification")
public class NotificationController {

private final NotificationService notificationService;

@Operation(summary = "알림 목록 출력", description = "알림 목록 보기")
@GetMapping
public ResponseEntity<ResponseTemplate<Object>> getNotifications(@AuthenticationPrincipal Long userId) {

log.info("[NotificationController.getNotifications]");

NotificationResponse notification = notificationService.getNotifications(userId);

return ResponseEntity
.status(HttpStatus.OK)
.body(ResponseTemplate.from(notification));
}

@Operation(summary = "알림 읽음 표시", description = "알림 읽음 표시")
@GetMapping("/{notificationId}")
public ResponseEntity<ResponseTemplate<Object>> readNotification(@AuthenticationPrincipal Long userId, @PathVariable Long notificationId) {

log.info("[NotificationController.readNotification]");

notificationService.readNotification(userId, notificationId);

return ResponseEntity
.status(HttpStatus.OK)
.body(ResponseTemplate.EMPTY_RESPONSE);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.goat.server.notification.repository;

import com.goat.server.mypage.domain.User;
import com.goat.server.notification.domain.Notification;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface NotificationRepository extends JpaRepository<Notification, Long> {

List<Notification> findAllByUser(User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ void saveOnBoardingInfo() {
given(userRepository.findById(1L)).willReturn(Optional.of(USER_GUEST));

// when
authService.saveOnBoardingInfo(1L, new OnBoardingRequest("modifiedNickname", "modifiedGoal"));
authService.saveOnBoardingInfo(1L, new OnBoardingRequest("modifiedNickname", "modifiedGoal", "fcmToken"));

// then
assertEquals("modifiedNickname", USER_GUEST.getNickname());
assertEquals("modifiedGoal", USER_GUEST.getGoal());
assertEquals("fcmToken", USER_GUEST.getFcmToken());
assertEquals(Role.USER, USER_GUEST.getRole());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.goat.server.directory.repository.DirectoryRepository;
import com.goat.server.mypage.repository.UserRepository;
import com.goat.server.notification.application.FcmServiceImpl;
import org.junit.jupiter.api.AfterEach;
Expand All @@ -16,6 +17,8 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat;


import java.util.Map;

Expand Down Expand Up @@ -44,6 +47,9 @@ class KakaoSocialServiceTest {

private ObjectMapper objectMapper = new ObjectMapper();

@Autowired
private DirectoryRepository directoryRepository;

@BeforeEach
void setUp() {
wireMockServer.stop();
Expand Down Expand Up @@ -84,6 +90,8 @@ void socialLoginTest() throws JsonProcessingException {

//then
assertNotNull(userRepository.findBySocialId(String.valueOf(1231241)));
assertThat(directoryRepository.findTrashDirectoryByUser(1L).get().getTitle()).isEqualTo("trash_directory");
assertThat(directoryRepository.findStorageDirectoryByUser(1L).get().getTitle()).isEqualTo("storage_directory");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ void saveOnBoardingInfo() throws Exception {

//when
ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.patch("/goat/auth/info")
.content(objectMapper.writeValueAsString(new OnBoardingRequest("nickname", "안녕하세요!")))
.content(objectMapper.writeValueAsString(new OnBoardingRequest("nickname", "안녕하세요!", "fcmToken")))
.contentType("application/json"));

//then
Expand Down

0 comments on commit 7eadb08

Please sign in to comment.