Skip to content

Commit

Permalink
v0.1.3
Browse files Browse the repository at this point in the history
v0.1.3
  • Loading branch information
char-yb authored Aug 29, 2024
2 parents 6012ee1 + 8f2368f commit ad45244
Show file tree
Hide file tree
Showing 42 changed files with 448 additions and 360 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ dependencies {
// FCM
implementation 'com.google.firebase:firebase-admin:9.3.0'
implementation 'com.fasterxml.jackson.core:jackson-core:2.16.1'

// SQS
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.1")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs'
implementation 'software.amazon.awssdk:sqs'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import com.depromeet.stonebed.domain.member.domain.MemberStatus;
import com.depromeet.stonebed.domain.member.domain.Profile;
import com.depromeet.stonebed.domain.member.dto.request.CreateMemberRequest;
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordBoostRepository;
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordRepository;
import com.depromeet.stonebed.global.error.ErrorCode;
import com.depromeet.stonebed.global.error.exception.CustomException;
Expand All @@ -34,7 +33,6 @@ public class AuthService {
private final FcmNotificationRepository fcmNotificationRepository;
private final MemberRepository memberRepository;
private final MissionRecordRepository missionRecordRepository;
private final MissionRecordBoostRepository missionRecordBoostRepository;

private final AppleClient appleClient;
private final KakaoClient kakaoClient;
Expand All @@ -55,7 +53,8 @@ public SocialClientResponse authenticateFromProvider(OAuthProvider provider, Str
public AuthTokenResponse socialLogin(
OAuthProvider oAuthProvider, String oauthId, String email) {
Optional<Member> memberOptional =
memberRepository.findByMemberOauthInfo(oAuthProvider.getValue(), email);
memberRepository.findByOauthInfoOauthProviderAndOauthInfoOauthId(
oAuthProvider.getValue(), oauthId);

return memberOptional
.map(
Expand All @@ -66,8 +65,8 @@ public AuthTokenResponse socialLogin(
? getTemporaryLoginResponse(member)
: getLoginResponse(member);
member.updateLastLoginAt();
member.updateOauthId(oauthId);
updateMemberNormalStatus(member);
log.info("소셜 로그인 진행: {}", member.getId());
return AuthTokenResponse.of(
tokenPair, member.getRole() == MemberRole.TEMPORARY);
})
Expand All @@ -82,6 +81,7 @@ public AuthTokenResponse socialLogin(
TokenPairResponse temporaryTokenPair =
jwtTokenService.generateTemporaryTokenPair(newMember);
newMember.updateLastLoginAt();
log.info("임시 회원가입 진행: {}", newMember.getId());
return AuthTokenResponse.of(temporaryTokenPair, true);
});
}
Expand All @@ -96,6 +96,7 @@ public AuthTokenResponse registerMember(CreateMemberRequest request) {

// 새 토큰 생성
TokenPairResponse tokenPair = getLoginResponse(registerMember);
log.info("일반 회원가입 진행: {}", registerMember.getId());
return AuthTokenResponse.of(tokenPair, false);
}
throw new CustomException(ErrorCode.ALREADY_EXISTS_MEMBER);
Expand Down Expand Up @@ -165,7 +166,6 @@ private void updateMemberNormalStatus(Member member) {

private void withdrawMemberRelationByMemberId(Long memberId) {
missionRecordRepository.deleteAllByMember(memberId);
missionRecordBoostRepository.deleteAllByMember(memberId);
fcmNotificationRepository.deleteAllByMember(memberId);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package com.depromeet.stonebed.domain.fcm.api;

import com.depromeet.stonebed.domain.fcm.application.FcmNotificationService;
import com.depromeet.stonebed.domain.fcm.application.FcmService;
import com.depromeet.stonebed.domain.fcm.application.FcmTokenService;
import com.depromeet.stonebed.domain.fcm.dto.request.FcmSendRequest;
import com.depromeet.stonebed.domain.fcm.dto.request.FcmTokenRequest;
import com.depromeet.stonebed.domain.fcm.dto.response.FcmNotificationResponse;
import com.depromeet.stonebed.global.util.FcmNotificationUtil;
import com.google.firebase.messaging.Notification;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
Expand All @@ -31,22 +26,9 @@
@RequestMapping("/alarm")
@RequiredArgsConstructor
public class FcmController {
private final FcmService fcmService;
private final FcmTokenService fcmTokenService;
private final FcmNotificationService fcmNotificationService;

@Operation(summary = "푸시 메시지 전송", description = "저장된 모든 토큰에 푸시 메시지를 전송합니다.")
@PostMapping("/send")
public ResponseEntity<Void> pushMessageToAll(
@RequestBody @Validated FcmSendRequest fcmSendRequest) {
Notification notification =
FcmNotificationUtil.buildNotification(
fcmSendRequest.title(), fcmSendRequest.body());
List<String> tokens = fcmTokenService.getAllTokens();
fcmService.sendMulticastMessage(notification, tokens);
return ResponseEntity.ok().build();
}

@Operation(summary = "FCM 토큰 저장", description = "로그인 시 FCM 토큰을 저장합니다.")
@PostMapping("/token")
public ResponseEntity<Void> fcmTokenStore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.depromeet.stonebed.domain.fcm.dao.FcmNotificationRepository;
import com.depromeet.stonebed.domain.fcm.dao.FcmRepository;
import com.depromeet.stonebed.domain.fcm.domain.FcmMessage;
import com.depromeet.stonebed.domain.fcm.domain.FcmNotification;
import com.depromeet.stonebed.domain.fcm.domain.FcmNotificationType;
import com.depromeet.stonebed.domain.fcm.domain.FcmToken;
Expand All @@ -11,19 +12,19 @@
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordBoostRepository;
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordRepository;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import com.depromeet.stonebed.domain.sqs.application.SqsMessageService;
import com.depromeet.stonebed.global.common.constants.FcmNotificationConstants;
import com.depromeet.stonebed.global.error.ErrorCode;
import com.depromeet.stonebed.global.error.exception.CustomException;
import com.depromeet.stonebed.global.util.FcmNotificationUtil;
import com.depromeet.stonebed.global.util.MemberUtil;
import com.google.firebase.messaging.Notification;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand All @@ -35,7 +36,7 @@
@RequiredArgsConstructor
@Transactional
public class FcmNotificationService {
private final FcmService fcmService;
private final SqsMessageService sqsMessageService;
private final FcmNotificationRepository notificationRepository;
private final MissionRecordBoostRepository missionRecordBoostRepository;
private final MissionRecordRepository missionRecordRepository;
Expand Down Expand Up @@ -151,17 +152,18 @@ private FcmNotificationConstants determineNotificationType(Long totalBoostCount)

private void sendBoostNotification(
MissionRecord missionRecord, FcmNotificationConstants notificationConstants) {
Notification notification =
FcmNotificationUtil.buildNotification(
notificationConstants.getTitle(), notificationConstants.getMessage());

String token =
fcmRepository
.findByMember(missionRecord.getMember())
.map(FcmToken::getToken)
.orElseThrow(() -> new CustomException(ErrorCode.FAILED_TO_FIND_FCM_TOKEN));

fcmService.sendSingleMessage(notification, token);
FcmMessage fcmMessage =
FcmMessage.of(
notificationConstants.getTitle(),
notificationConstants.getMessage(),
token);
sqsMessageService.sendMessage(fcmMessage);

saveNotification(
FcmNotificationType.BOOSTER,
Expand Down Expand Up @@ -204,10 +206,23 @@ private List<FcmNotification> buildNotificationList(
}

public void sendAndNotifications(String title, String message, List<String> tokens) {
Notification notification = FcmNotificationUtil.buildNotification(title, message);
fcmService.sendMulticastMessage(notification, tokens);
List<List<String>> batches = createBatches(tokens, 10);

for (List<String> batch : batches) {
sqsMessageService.sendBatchMessages(batch, title, message);
}

List<FcmNotification> notifications = buildNotificationList(title, message, tokens);
notificationRepository.saveAll(notifications);
}

private List<List<String>> createBatches(List<String> tokens, int batchSize) {
return IntStream.range(0, (tokens.size() + batchSize - 1) / batchSize)
.mapToObj(
i ->
tokens.subList(
i * batchSize,
Math.min(tokens.size(), (i + 1) * batchSize)))
.collect(Collectors.toList());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ List<FcmNotification> findByMemberIdAndCreatedAtLessThanEqual(

boolean existsByCreatedAtLessThan(LocalDateTime createdAt);

// Delete
@Modifying
@Query(
"UPDATE FcmNotification fn SET fn.deletedAt = CURRENT_TIMESTAMP WHERE fn.member.id = :memberId")
@Query("DELETE FROM FcmNotification fn WHERE fn.member.id = :memberId")
void deleteAllByMember(@Param("memberId") Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.depromeet.stonebed.domain.fcm.domain;

public record FcmMessage(String title, String body, String token) {

public static FcmMessage of(String title, String body, String token) {
return new FcmMessage(title, body, token);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
package com.depromeet.stonebed.domain.fcm.domain;

import com.depromeet.stonebed.domain.common.BaseFullTimeEntity;
import com.depromeet.stonebed.domain.common.BaseTimeEntity;
import com.depromeet.stonebed.domain.member.domain.Member;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Entity
@Table(name = "fcm_notification")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SQLDelete(sql = "UPDATE fcm_notification SET deleted_at = NOW() WHERE id = ?")
@SQLRestriction("deleted_at IS NULL")
public class FcmNotification extends BaseFullTimeEntity {
public class FcmNotification extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
public record FeedContentGetResponse(
@Schema(description = "미션 ID", example = "1") Long missionId,
@Schema(description = "미션 제목", example = "산책하기") String missionTitle,
@Schema(description = "미션 완료 메시지", example = "산책하기 미션을 수행했어요!")
String missionCompleteMessage,
@Schema(description = "미션 기록 ID", example = "1") Long missionRecordId,
@Schema(description = "작성자 ID", example = "1") Long authorId,
@Schema(description = "작성자 프로필 닉네임") String authorProfileNickname,
Expand All @@ -20,6 +22,7 @@ public static FeedContentGetResponse from(FindFeedDto missionRecord) {
return new FeedContentGetResponse(
missionRecord.mission().getId(),
missionRecord.mission().getTitle(),
missionRecord.mission().getCompleteMessage(),
missionRecord.missionRecord().getId(),
missionRecord.author().getId(),
missionRecord.author().getProfile().getNickname(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@
import com.depromeet.stonebed.domain.member.domain.Member;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {

@Query(
"SELECT m FROM Member m WHERE m.oauthInfo.oauthProvider = :provider AND m.oauthInfo.oauthEmail = :email")
Optional<Member> findByMemberOauthInfo(
@Param("provider") String oauthProvider, @Param("email") String email);

Optional<Member> findByOauthInfoOauthProviderAndOauthInfoOauthEmail(
String oauthProvider, String email);
Optional<Member> findByOauthInfoOauthProviderAndOauthInfoOauthId(
String oauthProvider, String oauthId);
}
Loading

0 comments on commit ad45244

Please sign in to comment.