Skip to content

Commit

Permalink
refactor: 쿠폰, 알림 및 토큰 패키지 및 클래스명 변경 (#105)
Browse files Browse the repository at this point in the history
* refactor: 쿠폰 및 토큰 패키지 및 클래스명 변경

* refactor: 알림 패키지 및 클래스명 변경, Fcm 로직 분리
  • Loading branch information
hongdosan authored Nov 20, 2023
1 parent f12569e commit a4c2f2f
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
import com.moabam.api.application.auth.mapper.AuthMapper;
import com.moabam.api.application.auth.mapper.AuthorizationMapper;
import com.moabam.api.application.member.MemberService;
import com.moabam.api.domain.auth.repository.TokenRepository;
import com.moabam.api.dto.auth.AuthorizationCodeRequest;
import com.moabam.api.dto.auth.AuthorizationCodeResponse;
import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse;
import com.moabam.api.dto.auth.AuthorizationTokenRequest;
import com.moabam.api.dto.auth.AuthorizationTokenResponse;
import com.moabam.api.dto.auth.LoginResponse;
import com.moabam.api.dto.auth.TokenSaveValue;
import com.moabam.api.infrastructure.redis.TokenRepository;
import com.moabam.global.auth.model.AuthorizationMember;
import com.moabam.global.auth.model.PublicClaim;
import com.moabam.global.common.util.GlobalConstant;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,11 @@
public final class NotificationMapper {

private static final String NOTIFICATION_TITLE = "모아밤";
private static final String KNOCK_BODY = "님이 콕 찔렀습니다.";
private static final String CERTIFY_TIME_BODY = "방 인증 시간입니다.";

public static Notification toKnockNotificationEntity(String nickname) {
public static Notification toNotification(String body) {
return Notification.builder()
.setTitle(NOTIFICATION_TITLE)
.setBody(nickname + KNOCK_BODY)
.build();
}

public static Notification toCertifyAuthNotificationEntity(String title) {
return Notification.builder()
.setTitle(NOTIFICATION_TITLE)
.setBody(title + CERTIFY_TIME_BODY)
.setBody(body)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import com.moabam.api.application.room.RoomService;
import com.moabam.api.domain.notification.repository.NotificationRepository;
import com.moabam.api.domain.room.Participant;
import com.moabam.api.domain.room.repository.ParticipantSearchRepository;
import com.moabam.api.dto.notification.KnockNotificationStatusResponse;
import com.moabam.api.infrastructure.redis.NotificationRepository;
import com.moabam.api.infrastructure.fcm.FcmService;
import com.moabam.global.auth.model.AuthorizationMember;
import com.moabam.global.error.exception.ConflictException;
import com.moabam.global.error.exception.NotFoundException;
Expand All @@ -32,8 +30,12 @@
@Transactional(readOnly = true)
public class NotificationService {

private static final String KNOCK_BODY = "%s님이 콕 찔렀습니다.";
private static final String CERTIFY_TIME_BODY = "%s방 인증 시간입니다.";
private static final String KNOCK_KEY = "room_%s_member_%s_knocks_%s";

private final FcmService fcmService;
private final RoomService roomService;
private final FirebaseMessaging firebaseMessaging;
private final NotificationRepository notificationRepository;
private final ParticipantSearchRepository participantSearchRepository;

Expand All @@ -45,8 +47,9 @@ public void sendKnockNotification(AuthorizationMember member, Long targetId, Lon
validateConflictKnockNotification(knockKey);
validateFcmToken(targetId);

Notification notification = NotificationMapper.toKnockNotificationEntity(member.nickname());
sendAsyncFcm(targetId, notification);
String fcmToken = notificationRepository.findFcmTokenByMemberId(targetId);
String notificationBody = String.format(KNOCK_BODY, member.nickname());
fcmService.sendAsyncFcm(fcmToken, notificationBody);
notificationRepository.saveKnockNotification(knockKey);
}

Expand All @@ -57,8 +60,9 @@ public void sendCertificationTimeNotification() {

participants.parallelStream().forEach(participant -> {
String roomTitle = participant.getRoom().getTitle();
Notification notification = NotificationMapper.toCertifyAuthNotificationEntity(roomTitle);
sendAsyncFcm(participant.getMemberId(), notification);
String fcmToken = notificationRepository.findFcmTokenByMemberId(participant.getMemberId());
String notificationBody = String.format(CERTIFY_TIME_BODY, roomTitle);
fcmService.sendAsyncFcm(fcmToken, notificationBody);
});
}

Expand All @@ -80,15 +84,6 @@ public KnockNotificationStatusResponse checkMyKnockNotificationStatusInRoom(Auth
.toKnockNotificationStatusResponse(knockNotificationStatus.get(true), knockNotificationStatus.get(false));
}

private void sendAsyncFcm(Long fcmTokenKey, Notification notification) {
String fcmToken = notificationRepository.findFcmTokenByMemberId(fcmTokenKey);

if (fcmToken != null) {
Message message = NotificationMapper.toMessageEntity(notification, fcmToken);
firebaseMessaging.sendAsync(message);
}
}

private void validateConflictKnockNotification(String knockKey) {
if (notificationRepository.existsByKey(knockKey)) {
throw new ConflictException(ErrorMessage.CONFLICT_KNOCK);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
package com.moabam.api.infrastructure.redis;
package com.moabam.api.domain.auth.repository;

import java.time.Duration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.moabam.api.dto.auth.TokenSaveValue;
import com.moabam.api.infrastructure.redis.HashRedisRepository;

@Repository
public class TokenRepository {

private static final int EXPIRE_DAYS = 14;

private final HashTemplateRepository hashTemplateRepository;
private final HashRedisRepository hashRedisRepository;

@Autowired
public TokenRepository(HashTemplateRepository hashTemplateRepository) {
this.hashTemplateRepository = hashTemplateRepository;
public TokenRepository(HashRedisRepository hashRedisRepository) {
this.hashRedisRepository = hashRedisRepository;
}

public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest) {
String tokenKey = parseTokenKey(memberId);

hashTemplateRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS));
hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS));
}

public TokenSaveValue getTokenSaveValue(Long memberId) {
String tokenKey = parseTokenKey(memberId);
return (TokenSaveValue)hashTemplateRepository.get(tokenKey);
return (TokenSaveValue)hashRedisRepository.get(tokenKey);
}

public void delete(Long memberId) {
String tokenKey = parseTokenKey(memberId);
hashTemplateRepository.delete(tokenKey);
hashRedisRepository.delete(tokenKey);
}

private String parseTokenKey(Long memberId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moabam.api.infrastructure.redis;
package com.moabam.api.domain.notification.repository;

import static com.moabam.global.common.util.GlobalConstant.*;
import static java.util.Objects.*;
Expand All @@ -7,6 +7,8 @@

import org.springframework.stereotype.Repository;

import com.moabam.api.infrastructure.redis.StringRedisRepository;

import lombok.RequiredArgsConstructor;

@Repository
Expand All @@ -19,17 +21,17 @@ public class NotificationRepository {
private final StringRedisRepository stringRedisRepository;

// TODO : 세연님 로그인 시, 해당 메서드 사용해서 해당 유저의 FCM TOKEN 저장하면 됩니다. Front와 상의 후 삭제예정
public void saveFcmToken(Long key, String value) {
public void saveFcmToken(Long memberId, String fcmToken) {
stringRedisRepository.save(
String.valueOf(requireNonNull(key)),
requireNonNull(value),
String.valueOf(requireNonNull(memberId)),
requireNonNull(fcmToken),
Duration.ofDays(EXPIRE_FCM_TOKEN)
);
}

public void saveKnockNotification(String key) {
public void saveKnockNotification(String knockKey) {
stringRedisRepository.save(
requireNonNull(key),
requireNonNull(knockKey),
BLANK,
Duration.ofHours(EXPIRE_KNOCK)
);
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/moabam/api/infrastructure/fcm/FcmService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.moabam.api.infrastructure.fcm;

import org.springframework.stereotype.Service;

import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import com.moabam.api.application.notification.NotificationMapper;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class FcmService {

private final FirebaseMessaging firebaseMessaging;

public void sendAsyncFcm(String fcmToken, String notificationBody) {
Notification notification = NotificationMapper.toNotification(notificationBody);

if (fcmToken != null) {
Message message = NotificationMapper.toMessageEntity(notification, fcmToken);
firebaseMessaging.sendAsync(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
import com.moabam.global.error.model.ErrorMessage;

@Repository
public class HashTemplateRepository {
public class HashRedisRepository {

private final RedisTemplate<String, Object> redisTemplate;
private final HashOperations<String, String, Object> hashOperations;
private final Jackson2HashMapper hashMapper;

public HashTemplateRepository(RedisTemplate<String, Object> redisTemplate) {
public HashRedisRepository(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
hashOperations = redisTemplate.opsForHash();
hashMapper = new Jackson2HashMapper(false);
}

// redisTemplate.opsForHash().putAll(key, hashMapper.toHash(value));
public void save(String key, Object value, Duration timeout) {
hashOperations.putAll(key, hashMapper.toHash(value));
redisTemplate.expire(key, timeout);
Expand All @@ -37,7 +38,7 @@ public void delete(String key) {
public Object get(String key) {
Map<String, Object> memberToken = hashOperations.entries(key);

if (memberToken.size() == 0) {
if (memberToken.isEmpty()) {
throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.time.Duration;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
Expand All @@ -11,25 +11,25 @@
@RequiredArgsConstructor
public class StringRedisRepository {

private final StringRedisTemplate stringRedisTemplate;
private final RedisTemplate<String, String> redisTemplate;

public void save(String key, String value, Duration timeout) {
stringRedisTemplate
redisTemplate
.opsForValue()
.set(key, value, timeout);
}

public void delete(String key) {
stringRedisTemplate.delete(key);
redisTemplate.delete(key);
}

public String get(String key) {
return stringRedisTemplate
return redisTemplate
.opsForValue()
.get(key);
}

public Boolean hasKey(String key) {
return stringRedisTemplate.hasKey(key);
return redisTemplate.hasKey(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ public class GlobalConstant {
public static final String SPACE = " ";
public static final int ONE_HOUR = 1;
public static final int HOURS_IN_A_DAY = 24;
public static final String KNOCK_KEY = "room_%s_member_%s_knocks_%s";
public static final String FIREBASE_PATH = "config/moabam-firebase.json";

public static final int LEVEL_DIVISOR = 10;
}
4 changes: 2 additions & 2 deletions src/main/java/com/moabam/global/config/FcmConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.moabam.global.config;

import static com.moabam.global.common.util.GlobalConstant.*;

import java.io.IOException;
import java.io.InputStream;

Expand All @@ -24,6 +22,8 @@
@EnableScheduling
public class FcmConfig {

private static final String FIREBASE_PATH = "config/moabam-firebase.json";

@Bean
public FirebaseMessaging firebaseMessaging() {
try (InputStream inputStream = new ClassPathResource(FIREBASE_PATH).getInputStream()) {
Expand Down
14 changes: 2 additions & 12 deletions src/main/java/com/moabam/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

Expand All @@ -26,20 +25,11 @@ public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
}

@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new StringRedisSerializer());
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);

return stringRedisTemplate;
}

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setDefaultSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.setConnectionFactory(redisConnectionFactory);

return redisTemplate;
Expand Down
10 changes: 5 additions & 5 deletions src/main/resources/static/docs/coupon.html
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ <h4 id="_요청" class="discrete">요청</h4>
<div class="content">
<pre class="highlight nowrap"><code class="language-http" data-lang="http">POST /admins/coupons HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 200
Content-Length: 192
Host: localhost:8080

{
Expand All @@ -483,7 +483,7 @@ <h4 id="_응답" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 64
Content-Length: 62

{
"message" : "쿠폰의 이름이 중복되었습니다."
Expand Down Expand Up @@ -514,7 +514,7 @@ <h4 id="_응답_2" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 58
Content-Length: 56

{
"message" : "존재하지 않는 쿠폰입니다."
Expand Down Expand Up @@ -546,7 +546,7 @@ <h4 id="_응답_3" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 58
Content-Length: 56

{
"message" : "존재하지 않는 쿠폰입니다."
Expand All @@ -569,7 +569,7 @@ <h4 id="_요청_4">요청</h4>
<div class="content">
<pre class="highlight nowrap"><code class="language-http" data-lang="http">POST /coupons/search HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 88
Content-Length: 84
Host: localhost:8080

{
Expand Down
Loading

0 comments on commit a4c2f2f

Please sign in to comment.