Skip to content

Commit

Permalink
feat: 알림 조회 API 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
big-cir committed Dec 30, 2023
1 parent c681d22 commit e28460c
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
package io.perfume.api.common.config;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
public class RedisConfiguration {

@Value("${spring.redis.host}")
private String host;

@Value("${spring.redis.port}")
private int port;

@Value("${spring.redis.password}")
private String password;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
}
//package io.perfume.api.common.config;
//
//import lombok.RequiredArgsConstructor;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.data.redis.connection.RedisConnectionFactory;
//import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
//import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
//import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
//
//@Configuration
//@EnableRedisRepositories
//@RequiredArgsConstructor
//public class RedisConfiguration {
//
// @Value("${spring.redis.host}")
// private String host;
//
// @Value("${spring.redis.port}")
// private int port;
//
// @Value("${spring.redis.password}")
// private String password;
//
// @Bean
// public RedisConnectionFactory redisConnectionFactory() {
// RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
// redisStandaloneConfiguration.setHostName(host);
// redisStandaloneConfiguration.setPort(port);
// redisStandaloneConfiguration.setPassword(password);
// return new LettuceConnectionFactory(redisStandaloneConfiguration);
// }
//}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package io.perfume.api.notification.adapter.port.in;

import dto.ui.CursorResponse;
import io.perfume.api.notification.adapter.port.in.dto.DeleteNotificationResponseDto;
import io.perfume.api.notification.adapter.port.in.dto.GetNotificationRequestDto;
import io.perfume.api.notification.adapter.port.in.dto.GetNotificationResponseDto;
import io.perfume.api.notification.application.facade.NotificationFacadeService;
import io.perfume.api.notification.application.port.in.DeleteNotificationUseCase;
import io.perfume.api.notification.application.port.in.GetNotificationUseCase;
import io.perfume.api.notification.application.port.in.ReadNotificationUseCase;
import java.time.LocalDateTime;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,6 +17,8 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.time.LocalDateTime;

@RestController
@RequestMapping("/v1/notification")
public class NotificationController {
Expand All @@ -24,16 +29,35 @@ public class NotificationController {

private final DeleteNotificationUseCase deleteNotificationUseCase;

private final GetNotificationUseCase getNotificationUseCase;

public NotificationController(
NotificationFacadeService notificationService,
ReadNotificationUseCase readNotificationUseCase,
DeleteNotificationUseCase deleteNotificationUseCase) {
NotificationFacadeService notificationService,
ReadNotificationUseCase readNotificationUseCase,
DeleteNotificationUseCase deleteNotificationUseCase, GetNotificationUseCase getNotificationUseCase) {
this.notificationService = notificationService;
this.readNotificationUseCase = readNotificationUseCase;
this.deleteNotificationUseCase = deleteNotificationUseCase;
this.getNotificationUseCase = getNotificationUseCase;
}

// TODO : 알림 조회 pagination

@PreAuthorize("isAuthenticated()")
@GetMapping()
public ResponseEntity<CursorResponse<GetNotificationResponseDto>> getNotifications(
@AuthenticationPrincipal User user, GetNotificationRequestDto request) {
var userId = Long.parseLong(user.getUsername());
var notifications = getNotificationUseCase.getNotifications(userId, request.toCommand());
final var responseItems =
notifications.getItems().stream().map(GetNotificationResponseDto::from).toList();
return ResponseEntity.ok(
CursorResponse.of(
responseItems,
notifications.hasNext(),
notifications.hasPrevious(),
notifications.getNextCursor(),
notifications.getPreviousCursor()));
}

@PreAuthorize("isAuthenticated()")
@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.perfume.api.notification.adapter.port.in.dto;

import io.perfume.api.notification.application.port.in.dto.GetNotificationCommand;

public record GetNotificationRequestDto(Integer pageSize, String before, String after) {
public GetNotificationCommand toCommand() {
return new GetNotificationCommand(getSizeOrDefault(), before, after);
}

private int getSizeOrDefault() {
return pageSize == null ? 10 : pageSize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.perfume.api.notification.adapter.port.in.dto;

import io.perfume.api.notification.application.port.in.dto.GetNotificationResult;

public record GetNotificationResponseDto(Long id, String content, String notificationType) {

public static GetNotificationResponseDto from(GetNotificationResult getNotificationResult) {
return new GetNotificationResponseDto(
getNotificationResult.id(),
getNotificationResult.content(),
getNotificationResult.notificationType()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package io.perfume.api.notification.adapter.port.out;

import static io.perfume.api.notification.adapter.port.out.QNotificationEntity.notificationEntity;

import com.querydsl.jpa.impl.JPAQueryFactory;
import dto.repository.CursorPageable;
import dto.repository.CursorPagination;
import io.perfume.api.base.PersistenceAdapter;
import io.perfume.api.notification.application.port.out.notification.NotificationQueryRepository;
import io.perfume.api.notification.domain.Notification;

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

import static io.perfume.api.notification.adapter.port.out.QNotificationEntity.notificationEntity;

@PersistenceAdapter
public class NotificationQueryPersistenceAdapter implements NotificationQueryRepository {

Expand All @@ -34,4 +38,30 @@ public Optional<Notification> findById(Long id) {
}
return Optional.of(notificationMapper.toDomain(entity));
}

@Override
public CursorPagination<Notification> findByreceiveUserId(CursorPageable pageable, long userId) {
var qb = jpaQueryFactory
.selectFrom(notificationEntity)
.where(notificationEntity.receiveUserId.eq(userId)
.and(notificationEntity.deletedAt.isNull()))
.orderBy(notificationEntity.id.desc());

final Optional<Long> cursor = pageable.getCursor(Long::parseLong);
if (cursor.isPresent()) {
final long id = cursor.get();
if (pageable.isNext()) {
qb.where(notificationEntity.receiveUserId.gt(id));
} else {
qb.where(notificationEntity.receiveUserId.lt(id));
}
}

final List<Notification> notifications =
qb.limit(pageable.getFetchSize()).fetch().stream()
.map(notificationMapper::toDomain)
.toList();

return CursorPagination.of(notifications, pageable, notification -> notification.getReceiveUserId().toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.perfume.api.notification.application.port.in;

import dto.repository.CursorPagination;
import io.perfume.api.notification.application.port.in.dto.GetNotificationCommand;
import io.perfume.api.notification.application.port.in.dto.GetNotificationResult;

public interface GetNotificationUseCase {

CursorPagination<GetNotificationResult> getNotifications(long userId, GetNotificationCommand command);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.perfume.api.notification.application.port.in.dto;

import dto.repository.CursorDirection;

public record GetNotificationCommand(int pageSize, String before, String after) {
public String getCursor() {
if (before != null) return before;
return after;
}

public boolean hasNext() {
return after != null;
}

public boolean hasPrevious() {
return before != null;
}

public CursorDirection getDirection() {
if (before != null) return CursorDirection.PREVIOUS;
return CursorDirection.NEXT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.perfume.api.notification.application.port.in.dto;

public record GetNotificationResult(Long id, String content, String notificationType) {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package io.perfume.api.notification.application.port.out.notification;

import dto.repository.CursorPageable;
import dto.repository.CursorPagination;
import io.perfume.api.notification.application.port.in.dto.NotificationResult;
import io.perfume.api.notification.domain.Notification;

import java.util.Optional;

public interface NotificationQueryRepository {

Optional<Notification> findById(Long id);

CursorPagination<Notification> findByreceiveUserId(CursorPageable pageable, long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.perfume.api.notification.application.service;

import dto.repository.CursorPageable;
import dto.repository.CursorPagination;
import io.perfume.api.notification.application.port.in.GetNotificationUseCase;
import io.perfume.api.notification.application.port.in.dto.GetNotificationCommand;
import io.perfume.api.notification.application.port.in.dto.GetNotificationResult;
import io.perfume.api.notification.application.port.out.notification.NotificationQueryRepository;
import org.springframework.stereotype.Service;

@Service
public class GetNotificationService implements GetNotificationUseCase {

private final NotificationQueryRepository notificationQueryRepository;

public GetNotificationService(NotificationQueryRepository notificationQueryRepository) {
this.notificationQueryRepository = notificationQueryRepository;
}

@Override
public CursorPagination<GetNotificationResult> getNotifications(long userId, GetNotificationCommand command) {
var pageable =
new CursorPageable(command.pageSize(), command.getDirection(), command.getCursor());
var notifications = notificationQueryRepository.findByreceiveUserId(pageable, userId);
var result =
notifications.getItems().stream()
.map(
item ->
new GetNotificationResult(
item.getId(),
item.getContent(),
item.getNotificationType().getType()))
.toList();

return CursorPagination.of(result, notifications);
}
}
12 changes: 6 additions & 6 deletions perfume-api/src/main/resources/application-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ spring:
datasource:
driver-class-name: 'com.mysql.cj.jdbc.Driver'
url: 'jdbc:mysql://localhost:3307/test'
username: 'test'
password: 'test'
username: 'root'
password: '9409'
hikari:
maximum-pool-size: 5
max-lifetime: 1800000
Expand All @@ -22,10 +22,10 @@ spring:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
redis:
host: localhost
port: 6379
password: very-strong-local-redis-password!@#$!%!@#
# redis:
# host: localhost
# port: 6379
# password: very-strong-local-redis-password!@#$!%!@#
data:
web:
pageable:
Expand Down
Loading

0 comments on commit e28460c

Please sign in to comment.